Этот пост посвящен модульному тестированию нативного приложения для Android. Работая над своим собственным скромным Android-приложением, я хотел добавить неинструментированные модульные тесты и был удивлен, насколько сложно было использовать фиктивные объекты. По общему признанию, инструментальные тесты, выполняемые на эмуляторе или реальном устройстве, вероятно, являются лучшим способом, но я хотел более быстрый способ убедиться, что моя внутренняя логика все еще работала, как ожидалось, при внесении изменений.
Тестируемый код
Вот один из примеров кода, который я пытался протестировать:
public class ViewOnTouchDragStartListener implements View.OnTouchListener {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
ClipData clipData = ClipData.newPlainText("", "");
View.DragShadowBuilder dsb = new View.DragShadowBuilder(view);
view.startDrag(clipData, dsb, view, 0);
view.setVisibility(View.INVISIBLE);
return true;
} else {
return false;
}
}
}
Когда я начал писать свой тестовый метод, мой план был прост, просто используйте Mockito, чтобы создавать макеты для объектов View и MotionEvent, устанавливать ожидания, запускать тест и наблюдать за зеленой полосой. Вместо этого я получил множество ошибок RuntimeException («Stub!») . Поэтому я вернулся к чертежной доске (Google), которая привела меня к PowerMock. Я уже некоторое время использую Mockito, и я знал о PowerMock, который можно использовать для расширения возможностей таких платформ, как Mockito и EasyMock, для включения, среди прочего, насмешек над конструкторами, частными и статическими методами. Казалось, это именно то, что я искал, чтобы помочь с моими проблемами модульного тестирования Android.
Модульное тестирование займет два
Я скачал powermock-mockit-junit-1.4.10 (самый актуальный на момент написания статьи). Также доступны загрузки для работы с TestNG и EasyMock . После некоторой консультации с документацией вот мой улучшенный модульный тест
@RunWith(PowerMockRunner.class)
@PrepareForTest({MotionEvent.class, ClipData.class,ViewOnTouchDragStartListener.class,View.class})
public class ViewOnTouchDragStartListenerTest {
private ViewOnTouchDragStartListener dragStartListener;
private View view;
private MotionEvent motionEvent;
private ClipData clipData;
private View.DragShadowBuilder dragShadowBuilder;
@Before
public void setUp() throws Exception {
dragStartListener = new ViewOnTouchDragStartListener();
view = PowerMockito.mock(View.class);
PowerMockito.mockStatic(ClipData.class);
clipData = PowerMockito.mock(ClipData.class);
motionEvent = PowerMockito.mock(MotionEvent.class);
dragShadowBuilder = PowerMockito.mock(View.DragShadowBuilder.class);
}
@Test
public void testOnTouchActionDown() throws Exception {
PowerMockito.when(motionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
when(ClipData.newPlainText("", "")).thenReturn(clipData);
PowerMockito.whenNew(View.DragShadowBuilder.class).withArguments(view).thenReturn(dragShadowBuilder);
boolean isActionDown = dragStartListener.onTouch(view, motionEvent);
InOrder inOrder = inOrder(view);
inOrder.verify(view).startDrag(Matchers.<ClipData>anyObject(), Matchers.<View.DragShadowBuilder>anyObject(), eq(view),eq(0));
inOrder.verify(view).setVisibility(View.INVISIBLE);
assertThat(isActionDown,is(true));
}
Я нашел это довольно простым для реализации, и, что самое приятное, я стал зеленеть для всех своих модульных тестов! Я был впечатлен способностью PowerMock смоделировать вызов конструктора View.DragShadowBuilder внутри метода onTouch ViewOnTouchDragStartListener (строка 24 примера тестового кода и строка 5 примера кода, соответственно). Следует признать, что, вероятно, должен был существовать отдельный метод, который создавал бы и возвращал объект DragShadowBuilder, а затем использовал бы шпионскую функциональность PowerMock / Mockito, которая позволяет выборочно моделировать методы на реальных объектах.
Надеюсь, это поможет вам в тестировании устройств Android.