Navigation Drawer を開閉するには android.R.id.home ボタンをタップすればいいので、
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
public MainActivityTest() {
super(MainActivity.class);
}
@Override
public void setUp() throws Exception {
super.setUp();
// Espresso will not launch our activity for us, we must launch it via
// getActivity().
getActivity();
}
public void testDrawerOpen() {
// Drawer open
onView(withId(android.R.id.home)).perform(click());
// Click Settings
onView(withId(R.id.settings)).perform(click());
}
}
で OK そうです(R.id.settings は Navigation Drawer 内のボタン)。
ところが、これはテストに失敗します。
onView(withId(R.id.settings)).perform(click());
のところで、そんな Id の View は見つからないと言われてしまいます。
原因は、Navigation Drawer が開き終わる前に View を探そうとするからです。
そこで、Navigation Drawer が開き(または閉じ)はじめてから、閉じる(または開く)まで、
Espresso に今は Idle 状態じゃないと伝えるようにします。
まず、
IdlingResource インタフェースを実装したクラスのインスタンスを用意し、Espresso.registerIdlingResources()で登録します。
IdlingResource を実装したクラスとして、
CountingIdlingResource が用意されています。
このクラスは内部でカウンターを持っていて、increment() と decrement() でカウンターの値を変え、カウンターが 0 のときが Idle 状態として Espresso に伝えられます。
Navigation Drawer を実現している DrawerLayout クラスの
DrawerListener.onDrawerStateChanged() を利用して、カウンターの値を変えるようにします。
そのため、テスト対象の Activity に口を用意しないといけません。
public class MainActivity extends ActionBarActivity implements DrawerListener {
private DrawerLayout mDrawerLayout;
private View mDrawerContainer;
private ActionBarDrawerToggle mDrawerToggle;
private DrawerFragment mDrawerFragment;
/**
* Espresso で Drawer を開くため
*/
public interface DrawerStateListener {
public void onDrawerStateChanged(int newState);
}
private DrawerStateListener mDrawerStateListener;
public void setDrawerListener(DrawerStateListener l) {
mDrawerStateListener = l;
}
public DrawerStateListener getDrawerListener() {
return mDrawerStateListener;
}
/**
*
*/
@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.activity_main);
mDrawerFragment = (DrawerFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_drawer);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerContainer = findViewById(R.id.left_drawer);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer,
R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerClosed(View drawerView) {
supportInvalidateOptionsMenu();
super.onDrawerClosed(drawerView);
}
@Override
public void onDrawerOpened(View drawerView) {
supportInvalidateOptionsMenu();
super.onDrawerOpened(drawerView);
}
@Override
public void onDrawerStateChanged(int newState) {
if (mDrawerStateListener != null) {
mDrawerStateListener.onDrawerStateChanged(newState);
}
super.onDrawerStateChanged(newState);
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
...
}
Activity に DrawerStateListener という口を用意しました。
この DrawerStateListener を実装した DrawerStateListenerImpl クラスを用意し、onDrawerStateChanged(int newState) で newState の値に応じて increment(), decrement() します。
このテストでは Navigation Drawer 部分をドラッグしないので DrawerLayout.STATE_DRAGGING は無いと思って簡略化しています。
setUp() の中で getActivity() で取得した Activity に対して DrawerStateListenerImpl のインスタンスを差し込み、registerIdlingResources() で DrawerStateListenerImpl で参照している CountingIdlingResource のインスタンスを登録しています。
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
public MainActivityTest() {
super(MainActivity.class);
}
private class DrawerStateListenerImpl implements DrawerStateListener {
private final DrawerStateListener realDrawerListener;
private final CountingIdlingResource mIdlingResource;
private DrawerStateListenerImpl(DrawerStateListener l, CountingIdlingResource idlingResource) {
this.realDrawerListener = l;
this.mIdlingResource = checkNotNull(idlingResource);
}
@Override
public void onDrawerStateChanged(int newState) {
// ドラッグしないので
if (newState != DrawerLayout.STATE_IDLE) {
mIdlingResource.increment();
} else {
mIdlingResource.decrement();
}
if (realDrawerListener != null) {
realDrawerListener.onDrawerStateChanged(newState);
}
}
}
@Override
public void setUp() throws Exception {
super.setUp();
// Espresso will not launch our activity for us, we must launch it via
// getActivity().
MainActivity activity = getActivity();
CountingIdlingResource countingResource = new CountingIdlingResource("DrawerCalls");
activity.setDrawerListener(new DrawerStateListenerImpl(activity.getDrawerListener(), countingResource));
registerIdlingResources(countingResource);
}
public void testDrawerOpen() {
// Drawer open
onView(withId(android.R.id.home)).perform(click());
// Click Settings
onView(withId(R.id.settings)).perform(click());
}
}
このテストでは、ちゃんと R.id.settings ボタンがクリックされます。