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

KMMのテストのtips / KMM testing tips

KMMのテストのtips / KMM testing tips

E505897a79eede1a676f92740261e8f8?s=128

Masatoshi Kubode

July 29, 2021
Tweet

Transcript

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

  2. ©2021 Wantedly, Inc. XXXXBOUFEMZDPNJELVCPEF

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

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

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

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

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

  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() } }
  9. ©2021 Wantedly, Inc. ҧ͍ΛຒΊΔUJQTͷ঺հ

  10. ©2021 Wantedly, Inc. 5FTU3VMF

  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() } }
  12. ©2021 Wantedly, Inc. 5FTU3VMF࣮૷ interface TestRule { fun onBeforeTest() =

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

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

    Unit fun onAfterTest() = Unit } class TestRules(vararg testRules: TestRule) : List<TestRule> 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ΫϥεͰࣗಈͰݺ͹ΕΔ
  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) } }
  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 ͕ຖճ%#ΫϦΞ͍ͯ͠Δ
  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Λ࣮૷
  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Λࢦఆ
  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Λॻ͔ͳͯ͘Α͘ͳΔ
  20. ©2021 Wantedly, Inc. .PDL

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

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

    ۩৅ DMBTT PCKFDU HMPCBMGVO ʹґଘ͠ͳ͍ ςελϒϧͳઃܭ
  23. ©2021 Wantedly, Inc. ςελϒϧͳઃܭ class ApiClient { fun createUser(): User

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

    { // ࣮ࡍ͸௨৴͢Δ return User(…) } } class UserRepository { fun create(): User { return ApiClient().createUser() } } "QJ$MJFOUͱ͍͏۩৅Ϋϥεʹґଘ͍ͯ͠Δ
  25. ©2021 Wantedly, Inc. @Test fun test() { val user =

    UserRepository().create() // ௨৴݁Ռ͕Θ͔Βͳ͍ͷͰςετͰ͖ͳ͍ // ௨৴ࣦഊͨ͠ΒͲ͏ͳΔʁ } ςελϒϧͳઃܭ
  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ʹ͢Δ
  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Ϋϥεʹ͢Δ
  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ʹґଘ͢Δ
  29. ©2021 Wantedly, Inc. ςελϒϧͳઃܭ class ApiClientMock : ApiClient { lateinit

    var mock: () -> User override fun createUser() = mock() } NPDLʹϥϜμΛೖΕΔ͜ͱͰDSFBUF6TFS ͷ݁ՌΛࠩ͠ସ͑
  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 } } ςελϒϧͳઃܭ
  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ϓϩύςΟʹϥϜμΛೖΕͯϞοΫ͢Δ
  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͕࢖ΘΕΔ
  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ͷ಺༰͔Βظ଴͞ΕΔ݁ՌΛςετ
  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 } } ςελϒϧͳઃܭ ྫ֎͕ى͖ͨͱ͖ͷςετ΋༰қʹ
  35. ©2021 Wantedly, Inc. ·ͱΊ

  36. ©2021 Wantedly, Inc. w ,..ͷςετ͸·ͩπʔϧ͕গͳ͍ w ͳ͔ͬͨΒ࡞͍ͬͯ͘ਫ਼ਆ💪 w *OUFSGBDF*NQMΛ෼͚Δςελϒϧͳઃܭ͕ॏཁ w

    ϘΠϥʔϓϨʔτ͸૿͑ͯ͠·͏ w DPNQJMFSQMVHJOͰ͍͍ײ͡ʹ͍ͨ͠ ·ͱΊ
  37. ϖʔδλΠτϧ ϖʔδαϒλΠτϧ ©2021 Wantedly, Inc. 5IBOLT