Slide 1

Slide 1 text

「GigaViewer For Apps」の単体テスト を 振り返る kubell.mobile #2 1

Slide 2

Slide 2 text

自己紹介 kk__777 (けーけー) 木村 洸太 株式会社はてな GigaViewer For Apps コミックDAYS kubell.mobile #2 2

Slide 3

Slide 3 text

JUnit4 ↓ Spek ↓ Kotest ↓ Junit4(回帰) kubell.mobile #2 3

Slide 4

Slide 4 text

アーキテクチャ 公式 + Apollo Kotlin ViewModelが直接 Apollo を扱う ことが多い 詳しくは 以下 を見てね https://speakerdeck.com/numero anddev/graphqltogigaviewer-for- apps kubell.mobile #2 4

Slide 5

Slide 5 text

Spek いわゆる テストスイート- テストケース を構造化して 自然言語で書いていける object CalculatorSpec: Spek({ describe("A calculator") { val calculator by memoized { Calculator() } describe("addition") { it("returns the sum of its arguments") { assertEquals(3, calculator.add(1, 2)) } } } }) kubell.mobile #2 5

Slide 6

Slide 6 text

Gherkin 記法 object SetFeature: Spek({ Feature("Set") { val set by memoized { mutableSetOf() } Scenario("adding items") { When("adding foo") { set.add("foo") } Then("it should have a size of 1") { assertEquals(1, set.size) } Then("it should contain foo") { assertTrue(set.contains("foo")) } } kubell.mobile #2 6

Slide 7

Slide 7 text

Spek 2019年くらいに DroidKaigiでもいくつかセッションがあり Robolectric は 使えない 制限はある テストの記述を簡単にしつつ,テストを書く習慣を作りやすそう? kubell.mobile #2 7

Slide 8

Slide 8 text

Spek - 初期の頃から入っている - 慣れると書きやすい。慣れるまでがちょっと大変だったかも - よくあるBDDスタイルのやつで 良いなとは思ってた kubell.mobile #2 8

Slide 9

Slide 9 text

Spek だんだんと障害が出てくる メンテがされなくなる https://github.com/spekframework/spek/issues/959 IDEで 実行できなくなったので いちいちコマンド実行のストレス Robolectric は動かない Composeのテストは変わらずJUnit4 ちょっとしたAndroid依存もモックが必要 kubell.mobile #2 9

Slide 10

Slide 10 text

Kotest Rspecが源流の記法が存在 => Spek に似たコードは保持できる 歴史も長く, メンテされなくなるというリスクはなさそう kubell.mobile #2 10

Slide 11

Slide 11 text

class MyTests : DescribeSpec({ describe("score") { it("start as zero") { // test here } describe("with a strike") { it("adds ten") { // test here } it("carries strike to the next frame") { // test here } } describe("for the opposite team") { it("Should negate one score") { // test here } } } }) kubell.mobile #2 11

Slide 12

Slide 12 text

Kotest kotest-extensions-robolectric https://github.com/kotest/kotest-extensions-robolectric @RobolectricTest class ContainedRobolectricRunnerDefaultApplicationTest : StringSpec({ "Get the Application defined in AndroidManifest.xml" { ApplicationProvider.getApplicationContext()::class shouldBe MainApplication::class } }) ちょっとしたAndroid依存 を 使えるように Composeまでは.. Ruleが使えないので かけないはず kubell.mobile #2 12

Slide 13

Slide 13 text

入れてすぐ kotest-extensions-robolectric が アーカイブされてた? 結局、そんなに使う事がなかった.. kubell.mobile #2 13

Slide 14

Slide 14 text

結局 Robolectricが絡むかどうかでテストの書き方変えるのが面倒 どうせComposeのテストは書けない 使い分けるの難しすぎるので両方やめて JUnit4 統一はありなのでは 記法の違いはそんなに気にならない 学習コストがややあり kubell.mobile #2 14

Slide 15

Slide 15 text

-> 戻しちゃおう kubell.mobile #2 15

Slide 16

Slide 16 text

JUnit4の @Enclosed で構造化を残す術もありそうで、 シェルでサッとできるかも しれないが.. classファイルにコンパイルした際に ファイル名 長すぎて Gradle では落ちる とい うissueがある https://github.com/gradle/gradle/issues/21547 kubell.mobile #2 16

Slide 17

Slide 17 text

-> 40ファイルくらいなのでマンパワーで頑張る -> 構造化はいったんなしでよいでしょう kubell.mobile #2 17

Slide 18

Slide 18 text

というわけで現状 基本的にはJUnit4 + AndroidJunit4Runner を使ってる Assert は Truth モック,スタブ は Mockk 一部 Composeも、 Robolectric で UnitTest空間でテスト kubell.mobile #2 18

Slide 19

Slide 19 text

今後を考えてみる? kubell.mobile #2 19

Slide 20

Slide 20 text

Kotest Android サードパーティ https://github.com/LeoColman/kotest-android kubell.mobile #2 20

Slide 21

Slide 21 text

作ってる人は robolectric-extension と一緒 アーカイブされてたkotest-extensions-robolectricも入ってる Instrumentation Testの仕組みもある Ruleは使えなさそう? -> サードパーティ とはいえ 再考もあり? kubell.mobile #2 21

Slide 22

Slide 22 text

サッとためせそうなのはPowerAssert? https://kotlinlang.org/docs/power-assert.html kubell.mobile #2 22

Slide 23

Slide 23 text

Truth value of : createSummary(...) keys with wrong values for key : Alice expected value : 2 but got value : 1 unexpected keys for key : Alic unexpected value: 1 expected : {Alice=2, Bob=1} but was : {Alic=1, Bob=1, Alice=1} value of : createSummary(...) keys with wrong values for key : Alice expected value : 2 but got value : 1 unexpected keys for key : Alic unexpected value: 1 expected : {Alice=2, Bob=1} but was : {Alic=1, Bob=1, Alice=1} at sample.ComplexLogicTestTruth.testCreateSummary(ComplexTest.kt:34) kubell.mobile #2 23

Slide 24

Slide 24 text

powerAssert Unexpected summary: {Alie=1, Bob=1, Alice=1} assert(summary == mapOf("Alice" to 2, "Bob" to 1)) { "Unexpected summary: $summary" } | | | | | | | | | (Bob, 1) | | | (Alice, 2) | | {Alice=2, Bob=1} | false {Alie=1, Bob=1, Alice=1} java.lang.AssertionError: Unexpected summary: {Alie=1, Bob=1, Alice=1} assert(summary == mapOf("Alice" to 2, "Bob" to 1)) { "Unexpected summary: $summary" } | | | | | | | | | (Bob, 1) | | | (Alice, 2) | | {Alice=2, Bob=1} | false {Alie=1, Bob=1, Alice=1} kubell.mobile #2 24

Slide 25

Slide 25 text

kotlin.test assert(summary == mapOf("Alice" to 2, "Bob" to 1)) { "Unexpected summary: $summary" } Truth assertThat(logic.createSummary(people)).containsExactlyEntriesIn( mapOf("Alice" to 2, "Bob" to 1) ) kubell.mobile #2 25

Slide 26

Slide 26 text

テストピラミッドで見 る現状 下三つは JUnit Component Testは VRT も 上は 手打鍵 , QAチーム kubell.mobile #2 26

Slide 27

Slide 27 text

VRT kubell.mobile #2 27

Slide 28

Slide 28 text

ComposePreviewScreenshotTesting https://developer.android.com/studio/preview/compose-screenshot-testing?hl=ja 今年のDroidKaigiでも紹介がちょこちょこあったやつ @Preview を 専用のソースセットから覗けばいいだけなので楽 まだ alphaなので バグもちらほら kubell.mobile #2 28

Slide 29

Slide 29 text

ピラミッドの上をやっていくなら Kotest なり Cucumber なり、 BDD フレームワーク で Instrumentation Test も ありかも Cucumber: https://cucumber.io/docs/installation/ kubell.mobile #2 29

Slide 30

Slide 30 text

今後のTODO... かけてない Composeを巻き込んだ単体テスト VRT の対象 InstrumentationTestの充実? kubell.mobile #2 30

Slide 31

Slide 31 text

終わり kubell.mobile #2 31