Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing frameworks for Android

Testing frameworks for Android

The presentation will tell which frameworks you can use to test your application starting from local unit tests and end with functional testing.
Tools that are described are JUnit, Mockito, PowerMock, Hamcrest, Espresso, Robolectric.

PShchahelski

April 12, 2018
Tweet

More Decks by PShchahelski

Other Decks in Programming

Transcript

  1. Write Tests • Rapid feedback on failures • Easier to

    fix on early stage • Safety net • Free to refactor, clean up, optimize • Backbone of software
  2. Android test code • project sources • ${module}/src/main/java • instrumentation

    tests • ${module}/src/androidTest/java • unit test • ${module}/src/test/java
  3. Unit Tests • Run on JVM • Modified android.jar •

    gradle test • ${module}/build/reports/tests/${variant}/index.html
  4. JUnit • Used to test pure java/kotlin • Does not

    provide any mocks or Android APIs • The main part of both unit and instrumentation tests
  5. Annotations • @BeforeClass • @Rule • @Before • @Test •

    @After • @AfterClass • @Ignore https://cdn-images-1.medium.com/max/1000/1*Lp8XW7Flz91G8GoISm9NKA.jpeg
  6. Annotations @RunWith(JUnit4::class) class ExampleUnitTest { private lateinit var myClass: MyClass

    private lateinit var map: MutableMap<String, String> @Before fun setUp() { myClass = MyClass() map = mutableMapOf() } @Test fun addition_isCorrect() { assertEquals(4, 2 + 2) } @After fun tearDown() { map.clear() } }
  7. Mockito • Very readable syntax • Annotation support • Large

    stackoverflow community • Most popular Java/Android mocking framework
  8. Mockito 1. ${project}/build.gradle
 
 repositories { jcenter() } 2. ${module}/build.gradle


    dependencies { 
 ...
 
 testCompile "org.mockito:mockito-core:2.+" 
 }
  9. Creating a mock private lateinit var postExecution: PostExecutionThread @Before fun

    setUp() { postExecution = Mockito.mock(PostExecutionThread::class.java) } https://examples.javacodegeeks.com/core-java/mockito/mockito-initmocks-example/
  10. Dummy class Messenger(private val templateEngine: TemplateEngine, private val mailServer: MailServer)

    { fun sendMessage(client: Client, template: Template) { val msgContent: String = templateEngine.prepare(template, client) mailServer.send(client.getEmail(), msgContent) } } @Test fun testDummy(){ val template = mock(Template::class.java) sut.sendMessage(client, template) }
  11. Stub class Messenger(private val templateEngine: TemplateEngine, private val mailServer: MailServer)

    { fun sendMessage(client: Client, template: Template) { val msgContent: String = templateEngine.prepare(template, client) mailServer.send(client.getEmail(), msgContent) } } @Test fun testStub(){ val templateEngine = mock(TemplateEngine::class.java) val sut = Messenger(mailServer, templateEngine) `when`(templateEngine.prepare(template, client)) .thenReturn(MSG_CONTENT) sut.sendMessage(client, template) }
  12. Spy class Messenger(private val templateEngine: TemplateEngine, private val mailServer: MailServer)

    { fun sendMessage(client: Client, template: Template) { val msgContent: String = templateEngine.prepare(template, client) mailServer.send(client.getEmail(), msgContent) } } @Test fun testSpy(){ val mailServer = mock(MailServer::class.java) val sut = Messenger(mailServer, templateEngine) sut.sendMessage(client, template) verify(mailServer).send("[email protected]", msgContent) }
  13. Verifying private lateinit var sut: GetInstalledAppsUseCase private lateinit var repository:

    CachedAppRepository @Before fun setUp() { repository = mock() thread = mock() postExecution = mock() sut = GetInstalledAppsUseCase(repository, thread, postExecution) } @Test fun `test use case is built correctly`() { sut.buildUseCaseObservable(None) verify(repository).getInstalledApps() }
  14. Verifying private lateinit var sut: GetInstalledAppsUseCase private lateinit var repository:

    CachedAppRepository @Before fun setUp() { repository = mock() thread = mock() postExecution = mock() sut = GetInstalledAppsUseCase(repository, thread, postExecution) } @Test fun `test use case is built correctly`() { sut.buildUseCaseObservable(None) verify(repository, ).getInstalledApps() } times(1) atLeast(1) atLeastOnce(1) atMost(1) only() never()
  15. Verifying private lateinit var sut: GetInstalledAppsUseCase private lateinit var repository:

    CachedAppRepository @Before fun setUp() { repository = mock() thread = mock() postExecution = mock() sut = GetInstalledAppsUseCase(repository, thread, postExecution) } @Test fun `test use case is built correctly`() { sut.buildUseCaseObservable(None) verify(repository).saveInstalledApps( ) } any(T::class.java) anyInt() anyString() anyOrNull() anyArray(Array<T>::class.java)
  16. Stubbing @Test fun stubMethod() { val user = User(webService, USER_ID,

    PASSWORD) `when`(webService.isOffline()).thenReturn(true) user.login() verify(webService, never()).login() }
  17. Stubbing @Test fun stubMethod() { val user = User(webService, USER_ID,

    PASSWORD) `when`(webService.isOffline()).thenReturn(true) user.login() verify(webService, never()).login() }
  18. Stubbing @Test fun stubMethod() { val user = User(webService, USER_ID,

    PASSWORD) `when`(webService.isOffline()).thenReturn(true) user.login() verify(webService, never()).login() } https://www.javacodegeeks.com/2013/02/testing-expected-exceptions-with-junit-rules.html
  19. Stubbing • Normal syntax
 
 `when`(webService.isOffline()).thenReturn(true) • Alternative syntax
 


    doReturn(true).`when`(webService).isOffline() • BDD
 
 given(webService.isOffline()).willReturn(true)

  20. PowerMock @RunWith(PowerMockRunner::class) @PrepareForTest(Static::class) class YourTestCase { @Test fun testMethodThatCallsStaticMethod() {

    // mock all the static methods in a class called "Static" PowerMockito.mockStatic(Static::class.java) // use Mockito to set up your expectation Mockito.`when`(Static.firstStaticMethod(param)).thenReturn(value) // execute your test classCallStaticMethodObj.execute() // Always use PowerMockito.verifyStatic(Class) first // to start verifying behavior PowerMockito.verifyStatic(Static::class.java, Mockito.times(2)) // IMPORTANT: Call the static method you want to verify Static.firstStaticMethod(param) } }
  21. Espresso 1. ${module}/build.gradle
 dependencies { 
 ...
 
 androidTestImplementation 'com.android.support.test:runner:1.0.1'

    androidTestImplementation ‘com.android.support.test.espresso:espresso-core:3.0.1'
 } 2. ${module}/build.gradle
 android.defaultConfig { 
 
 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" }
  22. View @RunWith(AndroidJUnit4::class) class LoginActivityTest { @Rule @JvmField val loginActivityRule =

    ActivityTestRule<LoginActivity>(LoginActivity::class.java) @Test fun shouldVerifyEmptyPassword(){ onView(withId(R.id.email)) .perform(typeText("Alex"), closeSoftKeyboard()) onView(withId(R.id.login)) .perform(click()) onView(withId(R.id.password)) .check(matches(withError(`is`("Please enter a password")))) } }
  23. View fun withError(expected: Matcher<String>): Matcher<View> { return object : TypeSafeMatcher<View>()

    { override fun matchesSafely(item: View): Boolean { return if (item is TextInputLayout) { expected.matches(item.error.toString()) } else false } override fun describeTo(description: Description) { description.appendText("with error message: ") description.appendText(expected.toString()) } } }
  24. Robolectric • Run Android tests on JVM • Tests are

    fast • Include Android classes in tests
  25. Robolectric @RunWith(RobolectricTestRunner::class) class MainActivityTest { @Test fun testTitleColor(){ val activity

    = Robolectric.setupActivity(MainActivity::class) assertTrue(activity.getTitle().toString().equals("Main Activity")) } }
  26. Robolectric @RunWith(RobolectricTestRunner::class) /** * Calls the same lifecycle methods on

    the Activity called by Android the first time the Activity is created. * * @return Activity controller instance. */ public ActivityController<T> setup() { return create().start().postCreate(null).resume().visible(); }
  27. Robolectric @RunWith(RobolectricTestRunner::class) class MyActivityTest { @Test @Throws(Exception::class) fun clickingButton_shouldChangeResultsViewText() {

    val activity = Robolectric.setupActivity(MyActivity::class.java) val button = activity.findViewById(R.id.button) as Button val results = activity.findViewById(R.id.results) as TextView button.performClick() assertThat(results.getText().toString()) .isEqualTo("Robolectric Rocks!") } }
  28. Resources • https://developer.android.com/training/testing/index.html • https://www.youtube.com/watch?v=pK7W5npkhho&feature=youtu.be • https://www.youtube.com/watch?v=AlxMGxs2QnM&feature=youtu.be • https://www.youtube.com/watch?v=DJDBl0vURD4 •

    https://dzone.com/articles/understanding-junits-runner • http://www.vogella.com/tutorials/AndroidTesting/article.html • http://myhexaville.com/2018/01/01/android-local-tests-robolectric/
  29. Resources • https://proandroiddev.com/testing-user-interface-of-android-app-with- architecture-components-data-binding-and-dagger-2-5d15c3779807 • https://academy.realm.io/posts/360-andev-2017-inaki-villar-advanced-expresso/ • https://www.philosophicalhacker.com/post/psa-dont-use-esprsso-idling- resources-like-this/ •

    https://android.jlelse.eu/helping-androiddev-to-mock-tests-in-kotlin- ab3be5204559#.8l72082k2 • https://dzone.com/articles/7-popular-unit-test-naming • https://www.youtube.com/watch?v=AlxMGxs2QnM&feature=youtu.be
  30. Resources • https://engineering.21buttons.com/mocking-kotlin-classes-with-mockito-the-fast- way-631824edd5ba • Growing Object-Oriented Software, Guided by

    Tests by Steve Freeman, Nat Pryce (Book) • Tomek Kaczanowski - Practical Unit Testing with JUnit and Mockito (Book) • XUnit Test Patterns Refactoring Test Code (Book)