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

KMMのテストのtips / KMM testing tips

KMMのテストのtips / KMM testing tips

Masatoshi Kubode

July 29, 2021
Tweet

More Decks by Masatoshi Kubode

Other Decks in Programming

Transcript

  1. ©2021 Wantedly, Inc.
    ,..ͷςετͷUJQT
    ,PUMJO.VMUJQMBUGPSN.PCJMFษڧձ8BOUFEMZºνʔϜϥϘ
    ٱอग़խढ़
    2021/07/29

    View Slide

  2. ©2021 Wantedly, Inc.
    XXXXBOUFEMZDPNJELVCPEF

    View Slide

  3. ©2021 Wantedly, Inc.
    w ,..ͷςετʹ͍ͭͯ
    w ,PUMJO+7.ͱͷҧ͍
    w ҧ͍ΛຒΊΔUJQTͷ঺հ
    "HFOEB

    View Slide

  4. ©2021 Wantedly, Inc.
    ,..ͷςετʹ͍ͭͯ

    View Slide

  5. ©2021 Wantedly, Inc.
    ,..͸6*Λ࣋ͨͳ͍
    ,..Λςετ͢Δʹ͸ʁ
    🙅ϚχϡΞϧ
    🙆ςετίʔυ
    ,..ͷςετͷඞཁੑ
    https://speakerdeck.com/malvinstn/automated-testing-in-a-kmm-project?slide=6

    View Slide

  6. ©2021 Wantedly, Inc.
    ,..ͷςετͷσΟϨΫτϦߏ੒
    iOS/AndroidͲͪΒͰ΋ςετ͞ΕΔίʔυ
    iOSͰͷΈςετ͞ΕΔίʔυ
    AndroidͰͷΈςετ͞ΕΔίʔυ

    View Slide

  7. ©2021 Wantedly, Inc.
    ,PUMJO+7.ͱͷҧ͍

    View Slide

  8. ©2021 Wantedly, Inc.
    ,PUMJO$PNNPOͷ"1*͔͠ར༻Ͱ͖ͳ͍
    w +6OJU 3VMFͳͲ

    w NPDLL
    w DPSPVUJOFUFTU
    w FUD
    DPNNPO5FTUͱ+7.5FTUͱͷҧ͍
    // 🙅
    @Rule @JvmField val rule = SomeTestRule()
    // 🙅
    @MockK lateinit var mock: SomeInterface
    @Test fun test() {
    // 🙅
    every { mock.doSomething() } just Runs
    doTest()
    // 🙅
    verify { mock.doSomething() }
    }

    View Slide

  9. ©2021 Wantedly, Inc.
    ҧ͍ΛຒΊΔUJQTͷ঺հ

    View Slide

  10. ©2021 Wantedly, Inc.
    5FTU3VMF

    View Slide

  11. ©2021 Wantedly, Inc.
    w %#Λ࢖ͬͨ෦෼ͷςετΛ͍ͨ͠
    w ςετͷͨͼʹΫϦʔϯͳ%#Λ࢖͍͍ͨ
    w ΫϦΞ͢ΔίʔυΛຖճॻ͘ͷ͸ਏ͍ʜ
    5FTU3VMF
    class MyTest1 {
    lateinit var db: VisitAppDb
    @BeforeTest fun setup() {
    db = VisitAppDb(testSqlDriver())
    }
    @AfterTest fun tearDown() {
    db.clean()
    }
    }
    class MyTest2 {
    lateinit var db: VisitAppDb
    @BeforeTest fun setup() {
    db = VisitAppDb(testSqlDriver())
    }
    @AfterTest fun tearDown() {
    db.clean()
    }
    }
    class MyTest3 {
    lateinit var db: VisitAppDb
    @BeforeTest fun setup() {
    db = VisitAppDb(testSqlDriver())
    }
    @AfterTest fun tearDown() {
    db.clean()
    }
    }
    class MyTest4… {
    lateinit var db: Visi
    @BeforeTest fun setup
    db = VisitAppDb(t
    }
    @AfterTest fun tearDo
    db.clean()
    }
    }

    View Slide

  12. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    interface TestRule {
    fun onBeforeTest() = Unit
    fun onAfterTest() = Unit
    }
    class TestRules(vararg testRules: TestRule) : List by testRules.toList()
    interface HasTestRules {
    val testRules: TestRules
    @BeforeTest
    fun setupTestRules() {
    testRules.forEach { it.onBeforeTest() }
    }
    @AfterTest
    fun tearDownTestRules() {
    testRules.forEach { it.onAfterTest() }
    }
    }

    View Slide

  13. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    interface TestRule {
    fun onBeforeTest() = Unit
    fun onAfterTest() = Unit
    }
    class TestRules(vararg testRules: TestRule) : List by testRules.toList()
    interface HasTestRules {
    val testRules: TestRules
    @BeforeTest
    fun setupTestRules() {
    testRules.forEach { it.onBeforeTest() }
    }
    @AfterTest
    fun tearDownTestRules() {
    testRules.forEach { it.onAfterTest() }
    }
    }
    ϧʔϧΛ࡞੒͢Δʹ͸5FTU3VMFΛ࣮૷

    View Slide

  14. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    interface TestRule {
    fun onBeforeTest() = Unit
    fun onAfterTest() = Unit
    }
    class TestRules(vararg testRules: TestRule) : List by testRules.toList()
    interface HasTestRules {
    val testRules: TestRules
    @BeforeTest
    fun setupTestRules() {
    testRules.forEach { it.onBeforeTest() }
    }
    @AfterTest
    fun tearDownTestRules() {
    testRules.forEach { it.onAfterTest() }
    }
    }
    5FTU3VMFΛ࢖͍͍ͨ5FTUΫϥεʹ࣮૷ͤ͞Δ
    ͜ΕΒͷσϑΥϧτ࣮૷͕5FTUΫϥεͰࣗಈͰݺ͹ΕΔ

    View Slide

  15. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    class DbTestRule : TestRule {
    private lateinit var sqlDriver: SqlDriver
    lateinit var db: VisitAppDb
    private set
    override fun onBeforeTest() {
    sqlDriver = testSqlDriver()
    db = VisitAppDb(driver = sqlDriver)
    }
    override fun onAfterTest() {
    sqlDriver.close()
    }
    }
    class MyTest : HasTestRules {
    val dbTestRule = DbTestRule()
    override val testRules = TestRules(dbTestRule)
    @Test
    fun test() {
    dbTestRule.db.userQueries.insertOrReplace(User())
    val all = dbTestRule.db.userQueries.selectAll().executeAsList()
    assertEquals(1, all.size)
    }
    }

    View Slide

  16. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    class DbTestRule : TestRule {
    private lateinit var sqlDriver: SqlDriver
    lateinit var db: VisitAppDb
    private set
    override fun onBeforeTest() {
    sqlDriver = testSqlDriver()
    db = VisitAppDb(driver = sqlDriver)
    }
    override fun onAfterTest() {
    sqlDriver.close()
    }
    }
    class MyTest : HasTestRules {
    val dbTestRule = DbTestRule()
    override val testRules = TestRules(dbTestRule)
    @Test
    fun test() {
    dbTestRule.db.userQueries.insertOrReplace(User())
    val all = dbTestRule.db.userQueries.selectAll().executeAsList()
    assertEquals(1, all.size)
    }
    }
    5FTU3VMFΛ࣮૷ͯ͠#FGPSF"GUFSΛهड़
    UFTU4RM%SJWFS
    ͕ຖճ%#ΫϦΞ͍ͯ͠Δ

    View Slide

  17. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    class DbTestRule : TestRule {
    private lateinit var sqlDriver: SqlDriver
    lateinit var db: VisitAppDb
    private set
    override fun onBeforeTest() {
    sqlDriver = testSqlDriver()
    db = VisitAppDb(driver = sqlDriver)
    }
    override fun onAfterTest() {
    sqlDriver.close()
    }
    }
    class MyTest : HasTestRules {
    val dbTestRule = DbTestRule()
    override val testRules = TestRules(dbTestRule)
    @Test
    fun test() {
    dbTestRule.db.userQueries.insertOrReplace(User())
    val all = dbTestRule.db.userQueries.selectAll().executeAsList()
    assertEquals(1, all.size)
    }
    }
    5FTUΫϥεʹ)BT5FTU3VMFTΛ࣮૷

    View Slide

  18. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    class DbTestRule : TestRule {
    private lateinit var sqlDriver: SqlDriver
    lateinit var db: VisitAppDb
    private set
    override fun onBeforeTest() {
    sqlDriver = testSqlDriver()
    db = VisitAppDb(driver = sqlDriver)
    }
    override fun onAfterTest() {
    sqlDriver.close()
    }
    }
    class MyTest : HasTestRules {
    val dbTestRule = DbTestRule()
    override val testRules = TestRules(dbTestRule)
    @Test
    fun test() {
    dbTestRule.db.userQueries.insertOrReplace(User())
    val all = dbTestRule.db.userQueries.selectAll().executeAsList()
    assertEquals(1, all.size)
    }
    }
    ࢖͍͍ͨ5FTU3VMFΛࢦఆ

    View Slide

  19. ©2021 Wantedly, Inc.
    5FTU3VMF࣮૷
    class DbTestRule : TestRule {
    private lateinit var sqlDriver: SqlDriver
    lateinit var db: VisitAppDb
    private set
    override fun onBeforeTest() {
    sqlDriver = testSqlDriver()
    db = VisitAppDb(driver = sqlDriver)
    }
    override fun onAfterTest() {
    sqlDriver.close()
    }
    }
    class MyTest : HasTestRules {
    val dbTestRule = DbTestRule()
    override val testRules = TestRules(dbTestRule)
    @Test
    fun test() {
    dbTestRule.db.userQueries.insertOrReplace(User(userId = 1, name = "Foo"))
    val all = dbTestRule.db.userQueries.selectAll().executeAsList()
    assertEquals(1, all.size)
    }
    }
    !#FGPSF5FTU!"GUFS5FTUΛॻ͔ͳͯ͘Α͘ͳΔ

    View Slide

  20. ©2021 Wantedly, Inc.
    .PDL

    View Slide

  21. ©2021 Wantedly, Inc.
    w ͍͍ײ͡ͷ.PDLϥΠϒϥϦ͸ͳ͍
    w ࣗ෼Ͱॻ͔͘͠ͳ͍
    .PDL

    View Slide

  22. ©2021 Wantedly, Inc.
    w ςελϒϧͳઃܭʹ͢Δ
    w ந৅ JOUFSGBDF
    ʹґଘ
    w ۩৅ DMBTT PCKFDU HMPCBMGVO
    ʹґଘ͠ͳ͍
    ςελϒϧͳઃܭ

    View Slide

  23. ©2021 Wantedly, Inc.
    ςελϒϧͳઃܭ
    class ApiClient {
    fun createUser(): User {
    // ࣮ࡍ͸௨৴͢Δ
    return User(…)
    }
    }
    class UserRepository {
    fun create(): User {
    return ApiClient().createUser()
    }
    }

    View Slide

  24. ©2021 Wantedly, Inc.
    ςελϒϧͳઃܭ
    class ApiClient {
    fun createUser(): User {
    // ࣮ࡍ͸௨৴͢Δ
    return User(…)
    }
    }
    class UserRepository {
    fun create(): User {
    return ApiClient().createUser()
    }
    }
    "QJ$MJFOUͱ͍͏۩৅Ϋϥεʹґଘ͍ͯ͠Δ

    View Slide

  25. ©2021 Wantedly, Inc.
    @Test
    fun test() {
    val user = UserRepository().create()
    // ௨৴݁Ռ͕Θ͔Βͳ͍ͷͰςετͰ͖ͳ͍
    // ௨৴ࣦഊͨ͠ΒͲ͏ͳΔʁ
    }
    ςελϒϧͳઃܭ

    View Slide

  26. ©2021 Wantedly, Inc.
    ςελϒϧͳઃܭ
    interface ApiClient {
    fun createUser(): User
    }
    class ApiClientImpl : ApiClient {
    override fun createUser(): User {
    // ࣮ࡍ͸௨৴͢Δ
    return User()
    }
    }
    class UserRepository(private val apiClient: ApiClient) {
    fun create(): User {
    return apiClient.createUser()
    }
    }
    "QJ$MJFOUͱ͍͏JOUFSGBDFʹ͢Δ

    View Slide

  27. ©2021 Wantedly, Inc.
    ςελϒϧͳઃܭ
    interface ApiClient {
    fun createUser(): User
    }
    class ApiClientImpl : ApiClient {
    override fun createUser(): User {
    // ࣮ࡍ͸௨৴͢Δ
    return User()
    }
    }
    class UserRepository(private val apiClient: ApiClient) {
    fun create(): User {
    return apiClient.createUser()
    }
    }
    JOUFSGBDFΛ࣮૷ͨ͠*NQMΫϥεʹ͢Δ

    View Slide

  28. ©2021 Wantedly, Inc.
    ςελϒϧͳઃܭ
    interface ApiClient {
    fun createUser(): User
    }
    class ApiClientImpl : ApiClient {
    override fun createUser(): User {
    // ࣮ࡍ͸௨৴͢Δ
    return User()
    }
    }
    class UserRepository(private val apiClient: ApiClient) {
    fun create(): User {
    return apiClient.createUser()
    }
    }
    JOUFSGBDFΛ஫ೖͯ͠JOUFSGBDFʹґଘ͢Δ

    View Slide

  29. ©2021 Wantedly, Inc.
    ςελϒϧͳઃܭ
    class ApiClientMock : ApiClient {
    lateinit var mock: () -> User
    override fun createUser() = mock()
    }
    NPDLʹϥϜμΛೖΕΔ͜ͱͰDSFBUF6TFS
    ͷ݁ՌΛࠩ͠ସ͑

    View Slide

  30. ©2021 Wantedly, Inc.
    @Test
    fun test() {
    val apiClient = ApiClientMock()
    apiClient.mock = { User("test") }
    val user = UserRepository(apiClient).create()
    assertEquals("test", user.id)
    }
    @Test
    fun testFailed() {
    val apiClient = ApiClientMock()
    apiClient.mock = { throw ExpectedException() }
    try {
    UserRepository(apiClient).create()
    fail()
    } catch (e: ExpectedException) {
    // Expected
    }
    }
    ςελϒϧͳઃܭ

    View Slide

  31. ©2021 Wantedly, Inc.
    @Test
    fun test() {
    val apiClient = ApiClientMock()
    apiClient.mock = { User("test") }
    val user = UserRepository(apiClient).create()
    assertEquals("test", user.id)
    }
    @Test
    fun testFailed() {
    val apiClient = ApiClientMock()
    apiClient.mock = { throw ExpectedException() }
    try {
    UserRepository(apiClient).create()
    fail()
    } catch (e: ExpectedException) {
    // Expected
    }
    }
    ςελϒϧͳઃܭ
    NPDLϓϩύςΟʹϥϜμΛೖΕͯϞοΫ͢Δ

    View Slide

  32. ©2021 Wantedly, Inc.
    @Test
    fun test() {
    val apiClient = ApiClientMock()
    apiClient.mock = { User("test") }
    val user = UserRepository(apiClient).create()
    assertEquals("test", user.id)
    }
    @Test
    fun testFailed() {
    val apiClient = ApiClientMock()
    apiClient.mock = { throw ExpectedException() }
    try {
    UserRepository(apiClient).create()
    fail()
    } catch (e: ExpectedException) {
    // Expected
    }
    }
    ςελϒϧͳઃܭ
    .PDLΛ஫ೖɺNPDL͕࢖ΘΕΔ

    View Slide

  33. ©2021 Wantedly, Inc.
    @Test
    fun test() {
    val apiClient = ApiClientMock()
    apiClient.mock = { User("test") }
    val user = UserRepository(apiClient).create()
    assertEquals("test", user.id)
    }
    @Test
    fun testFailed() {
    val apiClient = ApiClientMock()
    apiClient.mock = { throw ExpectedException() }
    try {
    UserRepository(apiClient).create()
    fail()
    } catch (e: ExpectedException) {
    // Expected
    }
    }
    ςελϒϧͳઃܭ
    .PDLͷ಺༰͔Βظ଴͞ΕΔ݁ՌΛςετ

    View Slide

  34. ©2021 Wantedly, Inc.
    @Test
    fun test() {
    val apiClient = ApiClientMock()
    apiClient.mock = { User("test") }
    val user = UserRepository(apiClient).create()
    assertEquals("test", user.id)
    }
    @Test
    fun testFailed() {
    val apiClient = ApiClientMock()
    apiClient.mock = { throw ExpectedException() }
    try {
    UserRepository(apiClient).create()
    fail()
    } catch (e: ExpectedException) {
    // Expected
    }
    }
    ςελϒϧͳઃܭ
    ྫ֎͕ى͖ͨͱ͖ͷςετ΋༰қʹ

    View Slide

  35. ©2021 Wantedly, Inc.
    ·ͱΊ

    View Slide

  36. ©2021 Wantedly, Inc.
    w ,..ͷςετ͸·ͩπʔϧ͕গͳ͍
    w ͳ͔ͬͨΒ࡞͍ͬͯ͘ਫ਼ਆ💪
    w *OUFSGBDF*NQMΛ෼͚Δςελϒϧͳઃܭ͕ॏཁ
    w ϘΠϥʔϓϨʔτ͸૿͑ͯ͠·͏
    w DPNQJMFSQMVHJOͰ͍͍ײ͡ʹ͍ͨ͠
    ·ͱΊ

    View Slide

  37. ϖʔδλΠτϧ ϖʔδαϒλΠτϧ
    ©2021 Wantedly, Inc.
    5IBOLT

    View Slide