Using Kotlin for tests

Using Kotlin for tests

Android Testing Boot Camp
TwitterID変えました。@lvla0805 -> @MoyuruAizawa

5f533179da1c82722252cbcb93e7356f?s=128

Moyuru Aizawa

May 10, 2017
Tweet

Transcript

  1. 2.

    lvla lvla0805 Ѫᖒ๖ (Moyuru Aizawa) - Kotlin engineer of FRESH!

    Div. CyberAgent, Inc. - Previously at Pairs Div. Eureka, Inc.
  2. 4.
  3. 5.

    ‣ JetBrains, OSS ‣ Statically typed programming language ‣ For

    the JVM, Android and the browser ‣ Interoperable with Java Kotlin?
  4. 6.

    ‣ Type inference ‣ Null safety ‣ Property ‣ Functional

    programming support ‣ Extension functions ‣ Delegation ‣ … Features
  5. 7.
  6. 12.
  7. 13.
  8. 14.

    public class Person { public final String firstName; public final

    String familyName; public Person(String firstName, String familyName) { this.firstName = firstName; this.familyName = familyName; } public String getFullName() { return firstName + " " + familyName; } } Person
  9. 15.

    public class Person { public final String firstName; public final

    String familyName; public Person(String firstName, String familyName) { this.firstName = firstName; this.familyName = familyName; } public String getFullName() { return firstName + " " + familyName; } } Person
  10. 16.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", "Aizawa") assertThat(person.fullName, `is`("Moyuru Aizawa")) } } Test Person#getFullName
  11. 17.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", "Aizawa") assertThat(person.fullName, `is`("Moyuru Aizawa")) } } Test Person#getFullName Create a instance of “Person”
  12. 18.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", "Aizawa") assertThat(person.fullName, `is`("Moyuru Aizawa")) } } Test Person#getFullName
  13. 19.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", "Aizawa") assertThat(person.fullName, `is`("Moyuru Aizawa")) } } Test Person#getFullName Property
  14. 20.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", "Aizawa") assertThat(person.fullName, `is`("Moyuru Aizawa")) } } Test Person#getFullName “is” is reserved word of Kotlin
  15. 21.
  16. 22.

    public class Person { ... @Override public boolean equals(Object o)

    { ... } @Override public int hashCode() { ... } } Person#equals, Person#hashCode
  17. 25.

    public class PersonRepository { private final PersonDao dao; private final

    PersonClient client; ... public Single<List<Person>> getPersons() { return client.fetch() .doOnSuccess(dao::insert); } } Test PersonRepository#getPersons
  18. 26.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons
  19. 27.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons Mock classes
  20. 28.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons
  21. 29.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons Create expected values [Person(firstName=0, familyName=0), Person(firstName=1, familyName=1), …]
  22. 30.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons Configure return behavior for mock
  23. 31.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons Configure return behavior for mock
  24. 32.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons Configure return behavior for mock
  25. 33.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons “when” is reserved word of Kotlin
  26. 34.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons Invoke PersonRepository#getPersons and assertion
  27. 35.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } Test PersonRepository#getPersons Verify invocation
  28. 37.

    ‣ knit ‣ Junit API set for Kotlin ‣ kmockito

    ‣ Mockito for Kotlin Libraries for Kotlin tests
  29. 38.
  30. 39.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", "Aizawa") assertThat(person.fullName, `is`("Moyuru Aizawa")) } } [BEFORE] Test Person#getFullName
  31. 40.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", “Aizawa") person.fullName.should be "Moyuru Aizawa" } } [AFTER] Test Person#getFullName
  32. 41.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", “Aizawa") person.fullName.should be "Moyuru Aizawa" } } How does it work?
  33. 42.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", “Aizawa") person.fullName.should be "Moyuru Aizawa" } } How does it work? Extension property
  34. 43.

    class PersonTest { @Test fun getFullName() { val person =

    Person("Moyuru", “Aizawa") person.fullName.should be "Moyuru Aizawa" } } How does it work? Infix call
  35. 44.
  36. 45.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock(PersonDao::class.java) val client = mock(PersonClient::class.java) val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() `when`(client.fetch()).thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) verify(dao).insert(expected) } } [BEFORE] Test PersonRepository#getPersons
  37. 46.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock<PersonDao>() val client = mock<PersonClient>() val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() client.fetch().invoked.thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) dao.verify().insert(expected) } } [AFTER] Test PersonRepository#getPersons
  38. 47.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock<PersonDao>() val client = mock<PersonClient>() val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() client.fetch().invoked.thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) dao.verify().insert(expected) } } How does it work?
  39. 48.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock<PersonDao>() val client = mock<PersonClient>() val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() client.fetch().invoked.thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) dao.verify().insert(expected) } } How does it work? Inline function, reified type parameter
  40. 49.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock<PersonDao>() val client = mock<PersonClient>() val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() client.fetch().invoked.thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) dao.verify().insert(expected) } } How does it work? Extension property
  41. 50.

    class PersonRepositoryTest { @Test fun getPersons() { val dao =

    mock<PersonDao>() val client = mock<PersonClient>() val repository = PersonRepository(dao, client) val expected = Array(5, Int::toString) .map { Person(it, it) }.toList() client.fetch().invoked.thenReturn(Single.just(expected)) repository.getPersons().test().assertValue(expected) dao.verify().insert(expected) } } How does it work? Extension function
  42. 51.
  43. 53.

    public class Person { public final String firstName; public final

    String familyName; public Person(String firstName, String familyName) { this.firstName = firstName; this.familyName = familyName; } public String getFullName() { return firstName + " " + familyName; } @Override public boolean equals(Object o) { … } @Override public int hashCode() { … } } It is too annoying to override equals/hashCode
  44. 54.

    data class Person(val firstName: String, val lastName: String) { fun

    getFullName() = firstName + " " + lastName } Use data class The compiler automatically derives equals()/hashCode(), toString(), …
  45. 56.

    ‣ By default, all classes in Kotlin are final ‣

    Mockito cannot mock final classes… Mock Kotlin classes
  46. 57.

    ‣ Open ‣ Interface ‣ PowerMock ‣ Mockito 2.1.0(or above)

    MockMaker (*experimental) How to mock…?
  47. 60.

    interface PersonClient { fun fetch(): Single<List<Person>> } class PersonClientImpl: PersonClient

    { override fun fetch(): Single<List<Person>> { ... } } Interface
  48. 62.

    Create the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line mock-maker-inline or

    testCompile “org.mockito:mockito-core:2.x.y” testCompile “org.mockito:mockito-inline:2.x.y" Mockito2 MockMaker
  49. 63.

    ‣ Create the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker ‣ Configure resolutionStrategy configurations {

    test { resolutionStrategy { force “org.mockito:mockito-core:2.x.y” // 2.1.0 or above } } } Use inline mock maker with kmockito
  50. 64.
  51. 65.