Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Espresso, Beyond the Basics. DroidCon Vietnam

Espresso, Beyond the Basics. DroidCon Vietnam

Presentation of Espresso at Droidon Vietnam 2017

Iñaki Villar

April 15, 2017
Tweet

More Decks by Iñaki Villar

Other Decks in Technology

Transcript

  1. class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage

    coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
  2. class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage

    coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
  3. class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage

    coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
  4. Instrumentation MonitorningInstrumentation AndroidJunitRunner class notClass size log annotation notAnnotation numShards

    shardIndex delay_msec coverage coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle
  5. class notClass size log annotation notAnnotation numShards shardIndex delay_msec coverage

    coverageFile suiteAss. debug listener package notPackage timeout_msec testFile disableAnal. appListener idle Instrumentation MonitorningInstrumentation AndroidJunitRunner
  6. 
 perform(click());
 new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER)); 
 MotionEvents.sendDown(uiController, coordinates, precision);


    
 uiController.injectMotionEvent(motionEvent);
 
 MainActivityTest
 
 ViewActions
 
 Tapper
 
 MotionEvents

  7. @Override
 public void loopMainThreadUntilIdle() {
 do {
 if (!asyncTaskMonitor.isIdleNow()) {


    asyncTaskMonitor.notifyWhenIdle(…);
 condChecks.add(IdleCondition.ASYNC_TASKS_HAVE_IDLED);
 }
 
 if (!compatTaskMonitor.isIdleNow()) {
 compatTaskMonitor.notifyWhenIdle(…);
 condChecks.add(IdleCondition.COMPAT_TASKS_HAVE_IDLED);
 }
 
 if (!idlingResourceRegistry.allResourcesAreIdle()) {
 idlingResourceRegistry.notifyWhenAllResourcesAreIdle(…);
 condChecks.add(IdleCondition.DYNAMIC_TASKS_HAVE_IDLED);
 }
 
 loopUntil(condChecks);
 } while (!sIdleNow()); } UiControllerImpl.java
  8. @Override
 public void loopMainThreadUntilIdle() {
 do {
 if (!asyncTaskMonitor.isIdleNow()) {


    asyncTaskMonitor.notifyWhenIdle(…);
 condChecks.add(IdleCondition.ASYNC_TASKS_HAVE_IDLED);
 }
 
 if (!compatTaskMonitor.isIdleNow()) {
 compatTaskMonitor.notifyWhenIdle(…);
 condChecks.add(IdleCondition.COMPAT_TASKS_HAVE_IDLED);
 }
 
 if (!idlingResourceRegistry.allResourcesAreIdle()) {
 idlingResourceRegistry.notifyWhenAllResourcesAreIdle(…);
 condChecks.add(IdleCondition.DYNAMIC_TASKS_HAVE_IDLED);
 }
 
 loopUntil(condChecks);
 } while (!sIdleNow()); } UiControllerImpl.java
  9. private final AtomicReference<IdleMonitor> monitor = new AtomicReference<IdleMonitor>(null);
 private final ThreadPoolExecutor

    pool;
 private final AtomicInteger activeBarrierChecks = new AtomicInteger(0);
 AsyncTaskPoolMonitor.java
  10. boolean isIdleNow() {
 if (!pool.getQueue().isEmpty()) {
 return false;
 } else

    {
 int activeCount = pool.getActiveCount();
 if (0 != activeCount) {
 if (monitor.get() == null) {
 activeCount = activeCount - activeBarrierChecks.get();
 }
 }
 return 0 == activeCount;
 }
 } private final AtomicReference<IdleMonitor> monitor = new AtomicReference<IdleMonitor>(null);
 private final ThreadPoolExecutor pool;
 private final AtomicInteger activeBarrierChecks = new AtomicInteger(0);
 AsyncTaskPoolMonitor.java
  11. public boolean registerResources(final List resourceList) { } public boolean unregisterResources(final

    List resourceList) {
 }
 IdlingResourceRegistry.java boolean allResourcesAreIdle() {
 for (int i = idleState.nextSetBit(0); i >= 0 && i < resources.size();
 i = idleState.nextSetBit(i + 1)) {
 idleState.set(i, resources.get(i).isIdleNow());
 }
 return idleState.cardinality() == resources.size();
 }
  12. abstract class WrappingExecutorService implements ExecutorService {
 private final ExecutorService delegate;


    
 protected GuavaWrappingExecutorService(ExecutorService delegate) {
 this.delegate = checkNotNull(delegate);
 }
 
 protected abstract <T> Callable<T> wrapTask(Callable<T> callable);
 
 protected Runnable wrapTask(Runnable command) {
 final Callable<Object> wrapped = wrapTask(Executors.callable(command);
 return new Runnable() {
 @Override
 public void run() {
 wrapped.call();
 }
 };
 }
 @Override
 public final void execute(Runnable command) {
 delegate.execute(wrapTask(command));
 }
 } Guava
  13. abstract class WrappingExecutorService implements ExecutorService {
 private final ExecutorService delegate;


    
 protected GuavaWrappingExecutorService(ExecutorService delegate) {
 this.delegate = checkNotNull(delegate);
 }
 
 protected abstract <T> Callable<T> wrapTask(Callable<T> callable);
 
 protected Runnable wrapTask(Runnable command) {
 final Callable<Object> wrapped = wrapTask(Executors.callable(command);
 return new Runnable() {
 @Override
 public void run() {
 wrapped.call();
 }
 };
 }
 @Override
 public final void execute(Runnable command) {
 delegate.execute(wrapTask(command));
 }
 } Guava
  14. abstract class WrappingExecutorService implements ExecutorService {
 private final ExecutorService delegate;


    
 protected GuavaWrappingExecutorService(ExecutorService delegate) {
 this.delegate = checkNotNull(delegate);
 }
 
 protected abstract <T> Callable<T> wrapTask(Callable<T> callable);
 
 protected Runnable wrapTask(Runnable command) {
 final Callable<Object> wrapped = wrapTask(Executors.callable(command);
 return new Runnable() {
 @Override
 public void run() {
 wrapped.call();
 }
 };
 }
 @Override
 public final void execute(Runnable command) {
 delegate.execute(wrapTask(command));
 }
 } Guava
  15. abstract class WrappingExecutorService implements ExecutorService {
 private final ExecutorService delegate;


    
 protected GuavaWrappingExecutorService(ExecutorService delegate) {
 this.delegate = checkNotNull(delegate);
 }
 
 protected abstract <T> Callable<T> wrapTask(Callable<T> callable);
 
 protected Runnable wrapTask(Runnable command) {
 final Callable<Object> wrapped = wrapTask(Executors.callable(command);
 return new Runnable() {
 @Override
 public void run() {
 wrapped.call();
 }
 };
 }
 @Override
 public final void execute(Runnable command) {
 delegate.execute(wrapTask(command));
 }
 } Guava
  16. public class IdlingExecutorService extends GuavaWrappingExecutorService {
 private final CountingIdlingResource mIdlingResource;


    
 public IdlingResourceExecutorService(ExecutorService delegate,
 CountingIdlingResource idling) {
 super(delegate);
 mIdlingResource = idling;
 }
 

  17. @Override
 protected <T> Callable<T> wrapTask(Callable<T> callable) {
 return new WrappedCallable<>(callable);


    }
 
 private final class WrappedCallable<T> implements Callable<T> {
 private final Callable<T> delegate;
 
 public WrappedCallable(Callable<T> delegate) {
 this.delegate = delegate;
 }
 
 @Override
 public T call() throws Exception {
 mIdlingResource.increment();
 T call;
 try {
 call = delegate.call();
 } finally {
 mIdlingResource.decrement();
 } } }

  18. @Override
 protected <T> Callable<T> wrapTask(Callable<T> callable) {
 return new WrappedCallable<>(callable);


    }
 
 private final class WrappedCallable<T> implements Callable<T> {
 private final Callable<T> delegate;
 
 public WrappedCallable(Callable<T> delegate) {
 this.delegate = delegate;
 }
 
 @Override
 public T call() throws Exception {
 mIdlingResource.increment();
 T call;
 try {
 call = delegate.call();
 } finally {
 mIdlingResource.decrement();
 } } }

  19. public class RxSchedulerHook extends RxJavaSchedulersHook {
 
 private Scheduler customScheduler;


    private CountingIdlingResource idling;
 private static RxSchedulerHook sInstance;
 public RxSchedulerHook(CountingIdlingResource countingIdlingResource) {
 FailedTest watcher = new FailedTest();
 idling = countingIdlingResource;
 customScheduler = new Scheduler() {
 @Override public Scheduler.Worker createWorker() {
 return new CustomWorker();
 }
 };
 }
 RxJava
  20. public class RxSchedulerHook extends RxJavaSchedulersHook {
 
 private Scheduler customScheduler;


    private CountingIdlingResource idling;
 private static RxSchedulerHook sInstance;
 public RxSchedulerHook(CountingIdlingResource countingIdlingResource) {
 FailedTest watcher = new FailedTest();
 idling = countingIdlingResource;
 customScheduler = new Scheduler() {
 @Override public Scheduler.Worker createWorker() {
 return new CustomWorker();
 }
 };
 }
 RxJava
  21. public class RxSchedulerHook extends RxJavaSchedulersHook {
 
 private Scheduler customScheduler;


    private CountingIdlingResource idling;
 private static RxSchedulerHook sInstance;
 public RxSchedulerHook(CountingIdlingResource countingIdlingResource) {
 FailedTest watcher = new FailedTest();
 idling = countingIdlingResource;
 customScheduler = new Scheduler() {
 @Override public Scheduler.Worker createWorker() {
 return new CustomWorker();
 }
 };
 }
 RxJava
  22. @Override public Subscription schedule(final Action0 action, final long delayTime, TimeUnit

    unit) {
 return super.schedule(new Action0() {
 @Override public void call() {
 
 action.call();
 }
 }, delayTime, unit);
 }
 
 @Override public Subscription schedule(final Action0 action) {
 return super.schedule(new Action0() {
 @Override public void call() {
 idling.increment();
 try {
 action.call();
 } finally {
 idling.decrement();
 }
 }
 });
 } RxJava
  23. @Override public Subscription schedule(final Action0 action, final long delayTime, TimeUnit

    unit) {
 return super.schedule(new Action0() {
 @Override public void call() {
 
 action.call();
 }
 }, delayTime, unit);
 }
 
 @Override public Subscription schedule(final Action0 action) {
 return super.schedule(new Action0() {
 @Override public void call() {
 idling.increment();
 try {
 action.call();
 } finally {
 idling.decrement();
 }
 }
 });
 } RxJava
  24. @Override public Subscription schedule(final Action0 action, final long delayTime, TimeUnit

    unit) {
 return super.schedule(new Action0() {
 @Override public void call() {
 
 action.call();
 }
 }, delayTime, unit);
 }
 
 @Override public Subscription schedule(final Action0 action) {
 return super.schedule(new Action0() {
 @Override public void call() {
 idling.increment();
 try {
 action.call();
 } finally {
 idling.decrement();
 }
 }
 });
 } RxJava
  25. @Before
 public void setUp() {
 OkHttpClient client = new OkHttpClient();


    IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", client);
 Espresso.registerIdlingResources(resource);
 } OkHttp3IdlingResource
  26. @Before
 public void setUp() {
 OkHttpClient client = new OkHttpClient();


    IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", client);
 Espresso.registerIdlingResources(resource);
 } private OkHttp3IdlingResource(String name, Dispatcher dispatcher) {
 this.name = name;
 this.dispatcher = dispatcher;
 dispatcher.setIdleCallback(new Runnable() {
 public void run() {
 ResourceCallback callback = OkHttp3IdlingResource.this.callback;
 if(callback != null) {
 callback.onTransitionToIdle();
 }
 
 }
 });
 }
 
 public boolean isIdleNow() {
 return this.dispatcher.runningCallsCount() == 0;
 }
 
 OkHttp3IdlingResource
  27. @Rule public ActivityTestRule<Main> activityTestRule = new ActivityTestRule<>(Main){
 @Override
 protected void

    beforeActivityLaunched() {
 super.beforeActivityLaunched();
 }
 
 @Override
 protected void afterActivityLaunched() {
 super.afterActivityLaunched();
 }
 
 @Override
 protected void afterActivityFinished() {
 super.afterActivityFinished();
 }
 }; ActivityTestRule
  28. @Rule public ActivityTestRule<Main> activityTestRule = new ActivityTestRule<>(Main){
 @Override
 protected void

    beforeActivityLaunched() {
 super.beforeActivityLaunched();
 }
 
 @Override
 protected void afterActivityLaunched() {
 super.afterActivityLaunched();
 }
 
 @Override
 protected void afterActivityFinished() {
 super.afterActivityFinished();
 }
 }; ActivityTestRule
  29. @Rule public IntentTestRule<Main> activityTestRule = new IntentTestRule<>(Main) 
 
 public

    IntentsTestRule(Class<T> activityClass, boolean initialTouchMode,
 boolean launchActivity) {
 super(activityClass, initialTouchMode, launchActivity);
 }
 
 @Override
 protected void afterActivityLaunched() {
 Intents.init();
 super.afterActivityLaunched();
 }
 
 @Override
 protected void afterActivityFinished() {
 super.afterActivityFinished();
 Intents.release();
 }
 
 
 IntentTestRule
  30. public class CustomActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
 
 public

    CustomActivityTestRule(Class<T> activity) {
 super(activity);
 }
 
 public CustomActivityTestRule(Class<T> activity, boolean initialTouchMode) {
 super(activity, initialTouchMode);
 }
 
 
 @Override
 protected void afterActivityLaunched() {
 Intents.init();
 super.afterActivityLaunched();
 }
 
 @Override
 protected void afterActivityFinished() {
 super.afterActivityFinished();
 Intents.release();
 } CustomActivityTestRule
  31. public class CustomActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
 
 public

    CustomActivityTestRule(Class<T> activity) {
 super(activity);
 }
 
 public CustomActivityTestRule(Class<T> activity, boolean initialTouchMode) {
 super(activity, initialTouchMode);
 }
 
 
 @Override
 protected void afterActivityLaunched() {
 Intents.init();
 super.afterActivityLaunched();
 }
 
 @Override
 protected void afterActivityFinished() {
 super.afterActivityFinished();
 Intents.release();
 } CustomActivityTestRule
  32. public interface TestRule {
 Statement apply(Statement base, Description description);
 }

    public class UiThreadTestRule implements TestRule {
 private static final String LOG_TAG = "UiThreadTestRule";
 
 @Override
 public Statement apply(final Statement base, Description description) {
 return new UiThreadStatement(base, shouldRunOnUiThread(description));
 }
 
 protected boolean shouldRunOnUiThread(Description description) {
 return description.getAnnotation(UiThreadTest.class) != null;
 } } TestRule
  33. private class ActivityStatement extends Statement {
 
 private final Statement

    mBase;
 
 public ActivityStatement(Statement base) {
 mBase = base;
 }
 
 @Override
 public void evaluate() throws Throwable {
 try {
 if (mLaunchActivity) {
 act = launchActivity(getActivityIntent());
 }
 mBase.evaluate();
 } finally {
 finishActivity();
 }
 }
 } TestRule
  34. public class TraceTestRule implements TestRule {
 
 private Trace trace;


    
 @Override
 public Statement apply(final Statement base, Description description) {
 return new Statement() {
 @Override
 public void evaluate() throws Throwable {
 try {
 trace.start();
 base.evaluate();
 } finally {
 trace.end();
 }
 }
 };
 }
 } TestRule
  35. public class TraceTestRule implements TestRule {
 
 private Trace trace;


    
 @Override
 public Statement apply(final Statement base, Description description) {
 return new Statement() {
 @Override
 public void evaluate() throws Throwable {
 try {
 trace.start();
 base.evaluate();
 } finally {
 trace.end();
 }
 }
 };
 }
 } @Rule
 public TraceTestRule traceTestRule = new TraceTestRule(getTargetContext());
 TestRule TestRule
  36. return new Statement() {
 @Override
 public void evaluate() throws Throwable

    {
 List<Throwable> errors = new ArrayList<Throwable>();
 
 startingQuietly(description, errors);
 try {
 base.evaluate();
 succeededQuietly(description, errors);
 } catch (AssumptionViolatedException e) {
 errors.add(e);
 skippedQuietly(e, description, errors);
 } catch (Throwable e) {
 errors.add(e);
 failedQuietly(e, description, errors);
 } finally {
 finishedQuietly(description, errors);
 }
 
 MultipleFailureException.assertEmpty(errors);
 }
 }; public abstract class TestWatcher implements TestRule TestWatcher
  37. return new Statement() {
 @Override
 public void evaluate() throws Throwable

    {
 List<Throwable> errors = new ArrayList<Throwable>();
 
 startingQuietly(description, errors);
 try {
 base.evaluate();
 succeededQuietly(description, errors);
 } catch (AssumptionViolatedException e) {
 errors.add(e);
 skippedQuietly(e, description, errors);
 } catch (Throwable e) {
 errors.add(e);
 failedQuietly(e, description, errors);
 } finally {
 finishedQuietly(description, errors);
 }
 
 MultipleFailureException.assertEmpty(errors);
 }
 }; public abstract class TestWatcher implements TestRule TestWatcher
  38. public class FailedTest extends TestWatcher {
 
 public FailedTest() {


    uiAutomation = UiDevice.getInstance();
 }
 
 
 @Override
 protected void failed(Throwable e, Description description) {
 super.failed(e, description);
 String fileNameBase = getFileNameWithoutExtension(description);
 saveScreenshot(fileNameBase);
 saveInfo(fileNameBase);
 }
 
 @Override
 protected void succeeded(Description description) {
 super.succeeded(description);
 }
 
 @Override
 protected void skipped(AssumptionViolatedException e, Description description) {
 super.skipped(e, description);
 }
 TestWatcher
  39. public class FailedTest extends TestWatcher {
 
 public FailedTest() {


    uiAutomation = UiDevice.getInstance();
 }
 
 
 @Override
 protected void failed(Throwable e, Description description) {
 super.failed(e, description);
 String fileNameBase = getFileNameWithoutExtension(description);
 saveScreenshot(fileNameBase);
 saveInfo(fileNameBase);
 }
 
 @Override
 protected void succeeded(Description description) {
 super.succeeded(description);
 }
 
 @Override
 protected void skipped(AssumptionViolatedException e, Description description) {
 super.skipped(e, description);
 }
 TestWatcher
  40. public class FailedTest extends TestWatcher {
 
 public FailedTest() {


    uiAutomation = UiDevice.getInstance();
 }
 
 
 @Override
 protected void failed(Throwable e, Description description) {
 super.failed(e, description);
 String fileNameBase = getFileNameWithoutExtension(description);
 saveScreenshot(fileNameBase);
 saveInfo(fileNameBase);
 }
 
 @Override
 protected void succeeded(Description description) {
 super.succeeded(description);
 }
 
 @Override
 protected void skipped(AssumptionViolatedException e, Description description) {
 super.skipped(e, description);
 }
 TestWatcher
  41. public class RuleChain implements TestRule{
 public RuleChain ruleChain = RuleChain.outerRule(new

    LogRule("outer rule")
 .around(new LogRule("middle around rule")
 .around(new LogRule("inner around rule”)))); RuleChain
  42. public class RuleChain implements TestRule{
 public RuleChain ruleChain = RuleChain.outerRule(new

    LogRule("outer rule")
 .around(new LogRule("middle around rule")
 .around(new LogRule("inner around rule”)))); RuleChain
  43. public class RuleChain implements TestRule{
 public RuleChain ruleChain = RuleChain.outerRule(new

    LogRule("outer rule")
 .around(new LogRule("middle around rule")
 .around(new LogRule("inner around rule”)))); Starting outer rule Starting middle rule Starting inner rule Finishing inner rule Finishing middle rule Finishing outer rule RuleChain
  44. public NewActivityTestRule<Main> activityRule = new NewActivityTestRule<>(Main.class);
 
 
 @Rule
 public

    RuleChain chain = RuleChain.outerRule(new FailedTest() .around(activityRule);

  45. public NewActivityTestRule<Main> activityRule = new NewActivityTestRule<>(Main.class);
 
 
 @Rule
 public

    RuleChain chain = RuleChain.outerRule(new FailedTest() .around(activityRule);
 
 @Rule
 public RuleChain chain = RuleChain.outerRule(new FailedTest()) .around(new TraceTestRule()) .around(activityRule);

  46. public class Device {
 public Device() {
 }
 
 private

    static boolean deviceEquals(String device) {
 return Build.DEVICE.equals(device);
 }
 
 public static class Genymotion implements Condition {
 public Genymotion() {
 }
 
 public boolean isSatisfied() {
 return Device.deviceEquals("vbox86p");
 }
 }
 }
 AndroidTestRules
  47. @Before
 public void grantPhonePermission() {
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {


    getInstrumentation().getUiAutomation().executeShellCommand(
 "pm grant " + getTargetContext().getPackageName()
 + " android.permission.CALL_PHONE");
 }
 }
  48. afterEvaluate {
 tasks.each { task ->
 if (task.name.startsWith('connectedAndroidTest')) {
 task.dependsOn

    grantAnimationPermissions
 }
 }
 }
 devices=$($adb devices | grep -v 'List of devices' | cut -f1 | grep '.')
 
 for device in $devices; do
 echo "Setting permissions to device" $device "for package" $package
 $adb -s $device shell pm grant $package android.permission.xxxx done
  49. Anton Batishchev Anton Malinskiy Chiu-ki Chan Erik Hellman Friedger Müffke

    Iordanis Giannakakis Jake Wharton Jose Alcérreca Michael Bailey Paul Blundell Stephan Linzner Valera Zakharov Xavi Rigau ATSL Spoon Fork TestButler Burst RxPresso AndroidTestRules