陳 釗 琪 [Chiu-Ki Chan]
  
 No need to sleep 
onView(ViewMatcher) .perform(ViewAction) .check(ViewAssertion);
 onView(withId(R.id.greet_button)) .perform(ViewAction) .check(ViewAssertion);
 onView(withId(R.id.greet_button)) .perform(click()) .check(ViewAssertion);
 onView(withId(R.id.greet_button)) .perform(click()) .check(matches(not(isEnabled()));
 hello-worldonView(withId(R.id.greet_button)) .perform(click()) .check(matches(not(isEnabled()));
 onView(withId(R.id.greet_button)) .perform(click()) .check(matches(not(isEnabled()));
 
 
 
 
 
 
  @Test public void toolbarTitle() { CharSequence title = InstrumentationRegistry .getTargetContext().getString(R.string.my_title); matchToolbarTitle(title); } private static ViewInteraction matchToolbarTitle( CharSequence title) { return onView( allOf( isAssignableFrom(TextView.class), withParent(isAssignableFrom(Toolbar.class)))) .check(matches(withText(title.toString()))); }   private static ViewInteraction matchToolbarTitle( CharSequence title) { return onView(isAssignableFrom(Toolbar.class)) .check(matches(withToolbarTitle(is(title)))); }   private static Matcher<Object> withToolbarTitle( final Matcher<CharSequence> textMatcher) { return new BoundedMatcher<Object, Toolbar>(Toolbar.class) { @Override public boolean matchesSafely(Toolbar toolbar) { return textMatcher.matches(toolbar.getTitle()); } @Override public void describeTo(Description description) { description.appendText("with toolbar title: "); textMatcher.describeTo(description); } }; }  ToolbaronView(ViewMatcher) .perform(ViewAction) .check(ViewAssertion);
onData(ObjectMatcher) .DataOptions .perform(ViewAction) .check(ViewAssertion);
 
 Item 27?
 final Item[] items = new Item[COUNT]; for (int i = 0; i < COUNT; ++i) { items[i] = new Item(i); } ArrayAdapter<Item> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items); listView.setAdapter(adapter); 
  public static class Item { private final int value; public Item(int value) { this.value = value; } public String toString() { return String.valueOf(value); } }  
  listView.setOnItemClickListener( new AdapterView .OnItemClickListener() { public void onItemClick( AdapterView<?> parent, View view, int position, long id) { textView.setText( items[position].toString()); textView.setVisibility(View.VISIBLE); } }); 
  @Test public void clickItem() { onView(withId(R.id.text)) .check(matches(not(isDisplayed()))); onData(withValue(27)) .inAdapterView(withId(R.id.list)) .perform(click()); onView(withId(R.id.text)) .check(matches(withText("27"))) .check(matches(isDisplayed())); }  public static Matcher<Object> withValue(final int value) { return new BoundedMatcher<Object, MainActivity.Item>(MainActivity.Item.class) { @Override public void describeTo(Description description) { description.appendText("has value " + value); } @Override public boolean matchesSafely( MainActivity.Item item) { return item.toString().equals(String.valueOf(value)); } }; }  
 onData 
 RecyclerView is a ViewGroup,AdapterView  @Test public void clickItem() { onView(withId(R.id.text)) .check(matches(not(isDisplayed()))); onView(withId(R.id.recycler_view)) .perform( RecyclerViewActions.actionOnItemAtPosition(27, click())); onView(withId(R.id.text)) .check(matches(withText("27"))) .check(matches(isDisplayed())); }  actionOnItemAtPosition  // ListView onData(withValue(27)) .inAdapterView(withId(R.id.list)) .perform(click()); actionOnHolderItem with Matcher<VH>actionOnItem with Matcher<View>list-view-basicrecycler-view-basicDefine your own condition
 e.g. IntentService is not running. 
 @Override public String getName() { return IntentServiceIdlingResource.class.getName(); }  @Override public void registerIdleTransitionCallback( ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; }  @Override public boolean isIdleNow() { boolean idle = !isIntentServiceRunning(); if (idle && resourceCallback != null) { resourceCallback.onTransitionToIdle(); } return idle; }  private boolean isIntentServiceRunning() { ActivityManager manager = (ActivityManager) context.getSystemService( Context.ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo info : manager.getRunningServices(Integer.MAX_VALUE)) { if (RepeatService.class.getName().equals( info.service.getClassName())) { return true; } } return false; }  @Before public void registerIntentServiceIdlingResource() { idlingResource = new IntentServiceIdlingResource( InstrumentationRegistry.getTargetContext()); Espresso.registerIdlingResources(idlingResource); } @After public void unregisterIntentServiceIdlingResource() { Espresso.unregisterIdlingResources(idlingResource); } Dependency injection.
Different objects for app and test.
Mock objects in test.
 public interface DemoComponent { void inject(MainActivity mainActivity); }  @Singleton @Component(modules = ClockModule.class) public interface ApplicationComponent extends DemoComponent { }  @Singleton @Component(modules = MockClockModule.class) public interface TestComponent extends DemoComponent { void inject(MainActivityTest mainActivityTest); } 
  public class DemoApplication extends Application { private final DemoComponent component = createComponent(); protected DemoComponent createComponent() { return DaggerDemoApplication_ApplicationComponent.builder() .clockModule(new ClockModule()) .build(); } public DemoComponent component() { return component; } }  public class MockDemoApplication extends DemoApplication { @Override protected DemoComponent createComponent() { return DaggerMainActivityTest_TestComponent.builder() .mockClockModule(new MockClockModule()) .build(); } }  public class MockTestRunner extends AndroidJUnitRunner { @Override public Application newApplication( ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return super.newApplication( cl, MockDemoApplication.class.getName(), context); } }  testInstrumentationRunner 'com.sqisland.android.test_demo.MockTestRunner'  /* App */ public DateTime getNow() { return new DateTime(); }  /* Test */ Mockito.when(clock.getNow()) .thenReturn(new DateTime(2008, 9, 23, 0, 0, 0));   /* Espresso */ onView(withId(R.id.date)) .check(matches(withText("2008-09-23")));  Matcher, ViewAction, ViewAssertion