2013年12月1日日曜日

Espresso で Navigation Drawer を開閉する(DIを使わない編)

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 ボタンがクリックされます。


0 件のコメント:

コメントを投稿