陳 釗 琪 [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-world
onView(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);
}
};
}
Toolbar
onView(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-basic
recycler-view-basic
Define 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