TDD for Android

2b0404a5db1a74f01bf3bf94d142e28c?s=47 Alex Zhukovich
September 14, 2016

TDD for Android

Overview of base principle Test-Driven Development. How to use TDD for Android project. Tips for creating well tested Android projects.

Project:
https://github.com/AlexZhukovich/TemperatureConverterTDD

Blog:
http://alexzh.com

2b0404a5db1a74f01bf3bf94d142e28c?s=128

Alex Zhukovich

September 14, 2016
Tweet

Transcript

  1. 2.

    Why do we test? •Testing ensure the Quality of the

    product; •Tests help improve design code; •Tests check that code works;
  2. 3.

    Observations • Huge activities / fragments • Hard to add

    a new feature • Hard to test • Many apps tested in manual way • Fragmentation • Slow Android tests • Required device • Robolectric • Espresso • etc
  3. 4.

    Bad vs Good tests @Test public void test1() { mPresenter.attachView(null);

    mPresenter.convertTemperature(); mPresenter.detachView(); verify(mView, never()).getInputValue(); verify(mView, never()).getFromTemperatureUnit(); verify(mView, never()).getToTemperatureUnit(); verify(mView, never()).setOutputValue(anyDouble()); when(mTemperatureFactory.getTemperatureConverter()) .thenReturn(mConverter); doNothing().when(mConverter).convertData(any(InputData.class)); mPresenter.attachView(mView); mPresenter.convertTemperature(); mPresenter.detachView(); verify(mEventBus).register(any()); verify(mView).displayProgress(); verify(mView, times(1)).getInputValue(); verify(mView, times(1)).getFromTemperatureUnit(); verify(mView, times(1)).getToTemperatureUnit(); verify(mEventBus).unregister(any()); } @Test public void shouldVerifyConvertDataSuccessful () { mPresenter.attachView(mView); mPresenter.convertTemperature(); mPresenter.detachView(); verify(mEventBus).register(any()); ... } @Test public void shouldVerifyConvertDataWithNullView() { mPresenter.attachView(null); mPresenter.convertTemperature(); mPresenter.detachView(); verify(mView, never()).getInputValue(); ... } Don’t Do
  4. 5.

    Bad vs Good UI tests @Test public void shouldVerifyGravity() {

    View view = ...; assertEquals(Gravity.START, view.getForegroundGravity()); ... } @Test public void shouldVerifyOrderCoffee() { onView(withId(R.id.name_edit)) .perform(typeText("User"), closeSoftKeyboard()); onView(withId(R.id.pay)) .perform(click()); onView(withId(R.id.detail_text)) .check(matches(isDisplayed())); } Don’t Do
  5. 6.
  6. 12.

    Recommendation for setting up the device / emulator One small

    recommendation for avoiding flakiness is turning off animation on real or virtual devices. • Window animation scale • Transition animation scale • Animator duration scale • Settings / Developer Options/
  7. 14.

    Hamcrest + Espresso public static Matcher<Object> withToolbarTitle(final Matcher<String> 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); } }; } onView(withId(R.id.toolbar)) .check(matches(withToolbarTitle( is (TITLE)))); startsWith endsWith ...
  8. 15.

    UI test @RunWith(AndroidJUnit4.class) public class TemperatureConverterActivityTest { @Test public void

    shouldVerifyConvertingFromCelsiusToFahrenheit() { onView(withId(R.id.inputView)) .perform(typeText(String.valueOf(CELSIUS_VALUE)), closeSoftKeyboard()); setTemperatureUnit(R.id.inputTemperatureSpinner, CELSIUS_STR); setTemperatureUnit(R.id.outputTemperatureSpinner, FAHRENHEIT_STR); onView(withId(R.id.convertButton)) .perform(click()); onView(withId(R.id.outputView)) .check(matches(isDisplayed())); onView(withId(R.id.outputView)) .check(matches(withText(getOutputString(FAHRENHEIT_VALUE)))); } }
  9. 16.

    Presenter test @RunWith(MockitoJUnitRunner.class) public class TemperatureConverterPresenterTest { @Mock TemperatureConverterView mView;

    … private TemperatureConverterPresenter mPresenter; @Test public void shouldVerifyConvertDataSuccessfulWithCorrectView() { mPresenter.attachView(mView); mPresenter.convertTemperature(); mPresenter.detachView(); verify(mEventBus).register(any()); ... } }
  10. 17.

    Asynchronous calls using Mockito (Retrofit2) @RunWith(MockitoJUnitRunner.class) public class OnlineConverterTemperatureTest {

    private OnlineConverterTemperature mConverter; @Mock EventBus mEventBus; @Mock TemperatureConverterApiService mApiService; @Mock Call<ConvertedResult> mCall; @Mock ResponseBody mResponseBody; @Captor ArgumentCaptor<Callback<ConvertedResult>> argumentCapture; @Test public void shouldGetConvertedResult() { when(mApiService.getConvertedData(anyString(), anyString(), anyString())) .thenReturn(mCall); Response<ConvertedResult> response = Response.success(mConvertedResult); mConverter.convertData(mInputData); verify(mCall).enqueue(argumentCapture.capture()); argumentCapture.getValue().onResponse(null, response); verify(mEventBus).post(new TemperatureConvertedSuccessful(mConvertedResult)); } }
  11. 18.

    Testing system stuff • Notifications; • Press hardware buttons; •

    Interacting with any applications. UI Automator
  12. 29.

    Thank you 1. Getting Started with Testing (Android) https://developer.android.com/training/testing/start/index.html 2.

    Espresso https://google.github.io/android-testing-support-library/docs/espresso/ 3. Ui Automator https://developer.android.com/training/testing/ui-testing/uiautomator-testing.html 4. Robolectric http://robolectric.org/ 5. Project https://github.com/AlexZhukovich/TemperatureConverterTDD 6. Blog http://alexzh.com/ @Alex_Zhukovich