Этот пост посвящен модульному тестированию нативного приложения для 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.