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

Espresso, Beyond the basics @ 360AnDev

Espresso, Beyond the basics @ 360AnDev

32018c62accb6edf57251e04308d4216?s=128

Iñaki Villar

July 14, 2017
Tweet

Transcript

  1. Beyond the basics Espresso Beyond the basics @inyaki_mwc

  2. None
  3. onView(withId(R.id.button_take_photo))
 .perform(click())
 .check(matches(isDisplayed()));


  4. onView(withId(R.id.button_take_photo))
 .perform(click())
 .check(matches(isDisplayed()));


  5. Structure

  6. public static ViewInteraction onView(final Matcher<View> viewMatcher) {
 return BASE.plus(new ViewInteractionModule(viewMatcher)).viewInteraction();


    }
  7. BaseLayerComponent

  8. BaseLayerModule UiControllerModule BaseLayerComponent

  9. BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule

    Recycler BaseLayerComponent UiController
  10. BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule

    Recycler BaseLayerComponent UiController
  11. ActivityLifeCycleMonitor

  12. ActivityLifeCycleMonitor PRE_ON_CREATE, CREATED, STARTED, RESUMED, PAUSED, STOPPED, RESTARTED, DESTROYED

  13. ActivityLifeCycleMonitor MonitoringInstrumentation

  14. ActivityLifeCycleMonitor AndroidJUnitRunner MonitoringInstrumentation ExposedInstrumentationApi Instrumentation MonitoringInstrumentation

  15. ActivityLifeCycleMonitor MonitoringInstrumentation mLifecycleMonitor.signalLifecycleChange(Stage.DESTROYED, activity);

  16. BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule

    Recycler BaseLayerComponent UiController
  17. ActivityRootLister

  18. ActivityRootLister WindowManagerGlobal

  19. ActivityRootLister WindowManagerGlobal mViews mParams new Root.Builder() .withDecorView(views.get(i)) .withWindowLayoutParams(params.get(i)) .build());

  20. BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule

    Recycler BaseLayerComponent UiController
  21. AsyncTaskPoolMonitor

  22. @CompatAsyncTask @SdkAsyncTask AsyncTaskPoolMonitor

  23. 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; } } AsyncTaskPoolMonitor
  24. private final ThreadPoolExecutor pool; AsyncTaskPoolMonitor

  25. private final ThreadPoolExecutor pool; MODERN_ASYNC_TASK_CLASS_NAME = “android.support.v4.content.ModernAsyncTask” MODERN_ASYNC_TASK_FIELD_NAME = "THREAD_POOL_EXECUTOR";

    AsyncTaskPoolMonitor ThreadPoolExtractor
  26. BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule

    Recycler BaseLayerComponent UiController
  27. EventInjector

  28. EventInjector EventInjectionStrategy

  29. EventInjector EventInjectionStrategy InputManager injectInputEventMethod.invoke(instanceInputManagerObject,motionEvent,motionEventMode);

  30. BaseLayerModule ActivityLifeCycleMonitor Context Looper AsyncTaskPoolMonitor Executor ActivityRootLister EventInjector FailureHandler UiControllerModule

    Recycler BaseLayerComponent UiController
  31. UiController

  32. UiController boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException; boolean injectKeyEvent(KeyEvent event) throws

    InjectEventSecurityException; boolean injectString(String str) throws InjectEventSecurityException;
  33. UiController boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException; boolean injectKeyEvent(KeyEvent event) throws

    InjectEventSecurityException; boolean injectString(String str) throws InjectEventSecurityException; EventInjector AsyncTaskMonitor IdlingResourceRegistry
  34. UiController boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException; boolean injectKeyEvent(KeyEvent event) throws

    InjectEventSecurityException; boolean injectString(String str) throws InjectEventSecurityException; EventInjector AsyncTaskMonitor IdlingResourceRegistry boolean allResourcesAreIdle()
  35. ViewInteractionComponent

  36. ViewInteractionModule Matcher<Root> ViewFinder Matcher<View> View ViewInteractionComponent

  37. ViewInteractionModule Matcher<Root> ViewFinder Matcher<View> View ViewInteractionComponent throw new AmbiguousViewMatcherException throw

    new NoMatchingViewException
  38. ViewInteractionModule Matcher<Root> ViewFinder Matcher<View> View ViewInteractionComponent public static ViewInteraction onView(final

    Matcher<View> viewMatcher) { return BASE.plus(new ViewInteractionModule(viewMatcher)).viewInteraction(); }
  39. ViewInteraction

  40. ViewInteraction public ViewInteraction perform(final ViewAction... viewActions) public ViewInteraction check(final ViewAssertion

    viewAssert)
  41. Synchronization

  42. 
 perform(click());
 
 MainActivityTest


  43. 
 perform(click());
 new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER)); 
 MainActivityTest
 
 ViewActions


  44. 
 perform(click());
 new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER)); 
 MotionEvents.sendDown(uiController, coordinates, precision);


    
 MainActivityTest
 
 ViewActions
 
 Tapper

  45. 
 perform(click());
 new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER)); 
 MotionEvents.sendDown(uiController, coordinates, precision);


    
 uiController.injectMotionEvent(motionEvent);
 
 MainActivityTest
 
 ViewActions
 
 Tapper
 
 MotionEvents

  46. InjectKeyEvent

  47. InjectKeyEvent loopMainThreadUntilIdle loopUntil Asynctask Idling

  48. InjectKeyEvent loopMainThreadUntilIdle loopUntil Asynctask Idling keyEventExecutor submit(event) loopUntil KeyInject

  49. @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()); }
  50. @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()); }
  51. None
  52. None
  53. None
  54. None
  55. None
  56. abstract class WrappingES(delegate: ExecutorService) : ExecutorService { val delegate: ExecutorService

    = checkNotNull(delegate) abstract fun <T> wrapTask(callable: Callable<T>): Callable<T> fun wrapTask(command: Runnable): Runnable { val wrapped = wrapTask(Executors.callable<Any>(command, null)) return Runnable { try { wrapped.call() } catch (e: Exception) { throw RuntimeException(e) } } }
  57. abstract class WrappingES(delegate: ExecutorService) : ExecutorService { val delegate: ExecutorService

    = checkNotNull(delegate) abstract fun <T> wrapTask(callable: Callable<T>): Callable<T> fun wrapTask(command: Runnable): Runnable { val wrapped = wrapTask(Executors.callable<Any>(command, null)) return Runnable { try { wrapped.call() } catch (e: Exception) { throw RuntimeException(e) } } }
  58. abstract class WrappingES(delegate: ExecutorService) : ExecutorService { val delegate: ExecutorService

    = checkNotNull(delegate) abstract fun <T> wrapTask(callable: Callable<T>): Callable<T> fun wrapTask(command: Runnable): Runnable { val wrapped = wrapTask(Executors.callable<Any>(command, null)) return Runnable { try { wrapped.call() } catch (e: Exception) { throw RuntimeException(e) } } }
  59. override fun execute(command: Runnable) { delegate.execute(wrapTask(command)) } override fun <T>

    submit(task: Callable<T>): Future<T> { return delegate.submit(wrapTask(checkNotNull(task))) } override fun submit(task: Runnable): Future<*> { return delegate.submit(wrapTask(task)) } override fun <T> submit(task: Runnable, result: T): Future<T> { return delegate.submit(wrapTask(task), result) }
  60. class IdlingResourceExecutorService(delegate: ExecutorService, val mIdlingResource: CountingIdlingResource) : GuavaWrappingExecutorService(delegate) { override

    fun <T> wrapTask(callable: Callable<T>): Callable<T> { return WrappedCallable(callable) } private inner class WrappedCallable<T>(private val delegate: Callable<T>) : Callable<T> { @Throws(Exception::class) override fun call(): T { mIdlingResource.increment() val call: T try { call = delegate.call() } finally { mIdlingResource.decrement() } return call } } } 
 }
  61. class IdlingResourceExecutorService(delegate: ExecutorService, val mIdlingResource: CountingIdlingResource) : GuavaWrappingExecutorService(delegate) { override

    fun <T> wrapTask(callable: Callable<T>): Callable<T> { return WrappedCallable(callable) } private inner class WrappedCallable<T>(private val delegate: Callable<T>) : Callable<T> { @Throws(Exception::class) override fun call(): T { mIdlingResource.increment() val call: T try { call = delegate.call() } finally { mIdlingResource.decrement() } return call } } } 
 }
  62. class IdlingResourceExecutorService(delegate: ExecutorService, val mIdlingResource: CountingIdlingResource) : GuavaWrappingExecutorService(delegate) { override

    fun <T> wrapTask(callable: Callable<T>): Callable<T> { return WrappedCallable(callable) } inner class WrappedCallable<T>(val delegate: Callable<T>) : Callable<T> { @Throws(Exception::class) override fun call(): T { mIdlingResource.increment() val call: T try { call = delegate.call() } finally { mIdlingResource.decrement() } return call } } } 
 }
  63. None
  64. 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();
 }
 };
 }

  65. @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();
 }
 }
 });
 }
  66. RxJavaPlugins.getInstance().registerSchedulersHook(RxSchedulerHook.get());


  67. RxIdler

  68. RxIdler https://github.com/square/RxIdler

  69. Rules

  70. @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();
 }
 };
  71. @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();
 }
 };
  72. @Rule public IntentTestRule<Main> activityTestRule = new IntentTestRule<>(Main)

  73. @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();
 }
 
 

  74. public interface TestRule {
 Statement apply(Statement base, Description description);
 }

  75. 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;
 } }
  76. 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();
 }
 }
 }
  77. 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();
 }
 }
 };
 }
 }
  78. 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());

  79. public abstract class TestWatcher implements TestRule

  80. 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
  81. 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
  82. 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);
 }

  83. 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);
 }

  84. 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);
 }

  85. public class RuleChain implements TestRule


  86. 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”))));
  87. 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 Starting middle Starting inner Finishing inner Finishing middle Finishing outer
  88. public NewActivityTestRule<Main> activityRule = new NewActivityTestRule<>(Main.class);
 


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

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

  90. 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);

  91. None
  92. What’s next?

  93. Android Test Orchestrator

  94. Test Apk APK AndroidJunitRunner Android Test Orchestrator

  95. Test Apk APK Orchestrator AndroidJunitRunner Android Test Orchestrator

  96. Test Apk APK Orchestrator AndroidJunitRunner Test Test Test Android Test

    Orchestrator
  97. Multiprocess Espresso

  98. Espresso AndroidJunitRunner Multiprocess Espresso

  99. Espresso AndroidJunitRunner Multiprocess Espresso Espresso AndroidJunitRunner

  100. Espresso AndroidJunitRunner Multiprocess Espresso Espresso AndroidJunitRunner

  101. dependencies { androidTestUtil 'com.linkedin.testbutler:test-butler-app:1.3.0@apk' }

  102. None
  103. None
  104. @inyaki_mwc Thanks