Slide 1

Slide 1 text

Kanon (@ysknsid25) #TSKaigiKansai テストコード品質を高めるために Mutationライブラリ・ Strykerを 実践導入してみた

Slide 2

Slide 2 text

README @ysknsid25 水瀬いのりさん が推し 表紙のアイコンは公式配布のものです。 https://x.com/tonikakuhayate/status/1806583319951233034?s=46&t=zwt7nxAvhST7_30NZnyMTQ @yskn_sid25 今日は神戸からきました

Slide 3

Slide 3 text

この発表のゴール 3 ● Mutation Testについて知っていただく ● 導入に必要な手順を知っていただく ● 導入してみて困ったこととその解決法を知っていただく ● 「Mutation Testを試しにやってみようかな」と思ってい ただく

Slide 4

Slide 4 text

4 Mutation Testとは?

Slide 5

Slide 5 text

コードに意図的なバグを植え付けることで、 テストコードの検証が適切に行われているか? を測定する手法 5

Slide 6

Slide 6 text

6 自分が自動テストに対してずっと思ってたこと テストコードがほんまに正しく書けてるか分からんのに、 テストコードを書いて「カバレッジx%やからヨシ!」 でほんまに品質って担保できてるんか?

Slide 7

Slide 7 text

7 カバレッジレポートの罠 ● 通常のカバレッジメトリクスは嘘をつく ● 例えば以下の(極端なテストケース)など コードサンプル javascript-testing-best-practice

Slide 8

Slide 8 text

8 そこで登場するのが Mutation Test ● Mutation = (突然) 変異 ○ コードを意図的に変更し、バグを植え付ける ○ ex) a===0 を a !== 0 と変異させる ○ その後テストを実行し、正しいテストが書かれていなければアサーションが エラーとなるはず ○ エラーとならなかった箇所がきちんと検証されていないと判断できる ● Googleでは2017~18年ごろから全社的に導入されているっぽい 参考:Googleにおける突然変異テストの状況 変異の内容と種類

Slide 9

Slide 9 text

9 Mutation Test に関わる指標 ● Killed ○ 変異後、成功すべきテストが失敗したことにより検知された変異の数 ● Survived ○ 変異後、失敗すべきテストが成功したことにより検知された変異の数 つまり、Survivedの数が多ければ多いほどテストコードの品質が低い

Slide 10

Slide 10 text

10 Mutation Test に関わる指標 Mutation Score = Killed / Total * 100 この割合が高いほど品質がよい

Slide 11

Slide 11 text

11 百聞は一見に如かず

Slide 12

Slide 12 text

12 Stryker を使っていろいろと見てみます ● Mutation ライブラリ ● 変異を自動で作ったり、レポート作ったり ● 対応言語 ○ JS ○ C# ○ Scala Playground (C#) があるのでそれを使ってみる

Slide 13

Slide 13 text

13 余談: TypeScript でも Strykerは変異を起こしてくれる レポートを見た感じ TSに対して変異を作っている 設定は特にしてない

Slide 14

Slide 14 text

14 余談: tsxでもOK

Slide 15

Slide 15 text

15 Mutation Reportを見てみる

Slide 16

Slide 16 text

16 “失敗すべきテストが成功するとは?” これを覚えておい てください これを覚えておい てください

Slide 17

Slide 17 text

17 “失敗すべきテストが成功するとは?”

Slide 18

Slide 18 text

18 “失敗すべきテストが成功するとは?” 変異前も前後も 結果は100 =変異してるのに テストは成功する この場合は 引数が適切でない

Slide 19

Slide 19 text

19 適切なテストにするには? テストパラメータを 修正し、再実行

Slide 20

Slide 20 text

20 LGTM👍 スコアアップ 🎉 Survivedだった 変異が消えた

Slide 21

Slide 21 text

21 導入してみる

Slide 22

Slide 22 text

22 今回の導入環境など ● Next.js (v14.2.4) ○ TypeScript (v5.2.2) ○ React (v18.2.0) ○ Vitest (v2.0.5) ○ Stryker (v8.5.0) ● 既存のプロジェクト

Slide 23

Slide 23 text

23 今回の導入環境など ● Nest.js (v10.4.1) ○ TypeScript (v5.5.4) ○ node (v20.11.0) ○ Vitest (v2.0.5) ○ Stryker (v8.5.0) ● 新規プロジェクトに導入

Slide 24

Slide 24 text

24 導入方法 ● 導入自体は超簡単 ○ npm i -g stryker-cli ○ npm i --save-dev @stryker-mutator/vitest-runner ○ npx stryker run ● stryker.config.jsonという設定ファイルができる ● このファイルに対していろいろ書いていく

Slide 25

Slide 25 text

25 詳しい記事

Slide 26

Slide 26 text

26 実際に導入して困ったこと

Slide 27

Slide 27 text

27 実戦で困ったこと ● 正攻法で実行すると死ぬほど遅い ● Stryker Dashboardがプライベートリポジトリだと使えない

Slide 28

Slide 28 text

28 正攻法で実行すると死ぬほど遅い CIに入れられるわけない Unit テストは 2,30秒くらいで終わっ てる

Slide 29

Slide 29 text

29 なぜ遅いのか? ● 変異の数はこの式で決まる ○ 変異発生対象ファイル × 変異の種類 ● 全体の実行時間はこの式で決まる ○ 変異の数 × Unitテストケースの実行時間 ● Strykerのデフォルト設定 ○ 全てのファイル × 全変異パターン ● よって変異発生対象ファイルと変異の種類を絞っ てあげればいい 変異発生対象ファイルを指定 除外する変異発生対象を指定

Slide 30

Slide 30 text

30 変異対象のファイル絞ったったらええやん!

Slide 31

Slide 31 text

31 これで勝つる

Slide 32

Slide 32 text

32 そう簡単に改善サセネーヨ ★ CIに (ry

Slide 33

Slide 33 text

33 実行時間で困った時の解法: 差分実行を使う ● incremental オプション ○ incremental: true で設定 ○ jsonで前回の結果を保存 ○ 変異対象のファイルに差分があった箇所のみ新たに(再度)変異を発生させ て実行

Slide 34

Slide 34 text

34 LGTM👍 ただ、変更箇所が多いと 実行時間は比例する が、PRを小さくするという意識を チームに植え付けられたので 結果オーライ

Slide 35

Slide 35 text

35 Stryker Dashboardがプライベートリポジトリに使えない ● やろうとしていたこと ○ GHAでMutation Testを定期実行して、レポートを取得 ■ Mutation Scoreがキープできているか? ■ Mutation Scoreをより高くしていくために、どこを直す必要があるか? ● なぜ? ○ チームメンバーの品質への意識の向上 ○ 修正を未経験新入社員にやってもらうことでプロダクションに影響のないところでコー ディング経験を積んでもらえる

Slide 36

Slide 36 text

36 Google Cloud Storageへ送信 + Slack通知 ● Workload Identity連携でGCSへ送信 ● バケットのURLをSlack通知 ● バケットは非公開&権限付与されたアカウントのみ 閲覧可能な設定 ● GCSに置いたレポートはライフサイクル管理を設定 することで、一定期間が過ぎたものは削除される

Slide 37

Slide 37 text

37 LGTM👍 推しに通知してもらえて wktk

Slide 38

Slide 38 text

38 導入した結果、どうなったか?

Slide 39

Slide 39 text

39 Unitテストのカバレッジ (新規マイクロサービス) ● 一般的には65% ~ 80%くらいを目指すのが良いくらいとされるなか、かなり高い数値を キープできている (2024/09/29現在) ● Mutation Testがあるおかげで、ある程度この数値も信頼できるようになった ● 特に無理してテストを書いている感じはない ● そもそも全体のStatementsは1805程度なので比較的小さい

Slide 40

Slide 40 text

40 Mutation テストのカバレッジ (新規マイクロサービス) ● こっちは77%くらいなのでまずまず ● Strykerにも閾値を設定できて(thresholdオプション)、今は50%以上なら CIはコケないようにしている

Slide 41

Slide 41 text

41 Unitテストのカバレッジ (フロント) ● フロント側は後からテストコードを書いていってるので全体的に低い ● 頑張ってカバレッジを上げていってるところ 💪

Slide 42

Slide 42 text

42 Mutation テストのカバレッジ (フロント) ● これも後から入れている & テストコードがそもそもないので、Survivedは必 然的に多くなる ● 今は全体ではなく、hooks系に絞ってやっているところ

Slide 43

Slide 43 text

43 感想 ● Good 👍 ○ 品質への意識が高まっただけでなく、結果的に PRを小さくする文化が浸透したのはよかった ○ 今回は小さめのプロジェクトに導入したおかげでわりかし早く実行できているが、大規模なプ ロジェクトに後から入れるのはしんどそう ○ 入れる場合、変異対象や種類を部分的に入れて、徐々に拡張していくのがよさそう ● Bad 👎 ○ CIのタイミングでは「どこが Suvivedしているか?」を知ることができない

Slide 44

Slide 44 text

44 PHPのMutationライブラリ・Infection にはそれがある

Slide 45

Slide 45 text

45 自動テスト・品質向上の一助になれば幸いです

Slide 46

Slide 46 text

46 (いれば) 懇親会で懇親しましょう Kanonさんと話してみたいという方

Slide 47

Slide 47 text

47 ご清聴、あざざました