GoにおけるMutation Testingの実践チャレンジ

176 Views

March 23, 26

スライド概要

Go Junction #1 で発表した資料となります。

profile-image

DeNA が社会の技術向上に貢献するため、業務で得た知見を積極的に外部に発信する、DeNA 公式のアカウントです。DeNA エンジニアの登壇資料をお届けします。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

GoにおけるMutation Testingの 実践チャレンジ DeNA エンジニアリング室開発デザイングループ(SWET) 伊藤 瑛 © DeNA Co., Ltd. 1

2.

自己紹介 ● 伊藤 瑛 ○ ● https://github.com/akito0107 DeNA エンジニアリング室開発デザイングループマネージャー SWETと呼ばれていたところ ● ● © DeNA Co., Ltd. 好きな言語など ○ Go, TypeScript ○ LEAN4 Web系全般の開発をやっています(それとGL) 2

3.

本日のアジェンダ ● Mutation Testingとは何か ● Mutation Testingのユースケース ● rmut (Mutation Testing Tool) の設計と実装 ● rmutの課題 © DeNA Co., Ltd. 3

4.

ミューテーションテスト ミューテーション解析 ご存知の方はいらっしゃいますか? © DeNA Co., Ltd. 4

5.

Mutation Testing とは何か ● テストの品質を測定する手法 (ミューテーション解析とも呼ぶ) ● コードに意図的な不具合(ミュータント)を入れて、テストが失敗するかを見る。 ○ 🙆失敗すれば killed ○ 🙅失敗しなければ survived ● Coverageレベルで確認できるのはテストコードがそこを通ったか。 ● Mutation Testingでは、そのテストが不具合を発見できる能力があるかどうかを確認 できる。 © DeNA Co., Ltd. 5

6.

シンプルな例 [IMAGE: IMG_015] Replace this with the actual image from Drive © DeNA Co., Ltd. 6

7.

Test実行 (日本では)成人は18歳からなので、実装にはバグがあるにもかかわらず Testがpassしてしまう *Coverageは100% © DeNA Co., Ltd. 7

8.

Mutation Testingの実施 ● > を >= に変える mutant を適用してtest実行する *意図的な不具合を混入する ● Test実行 => Testが落ちない(Survived) Testが十分では無いことが機械的にわかる © DeNA Co., Ltd. 8

9.

Mutation Testingの基本的な考え方 ● Test対象コードに変異(mutant)を埋め込み、Test Suiteを回す ● Test Suiteがfailしなかった場合、Test Suiteにはその変異を発見できる能力がない、 つまり、適切なTest Suiteになっていないと捉える ● 変異は様々な種類がある ○ Operatorの書き換え + → - / < → <= … ○ literalの書き換え 1 → -1 / “some string” → “” ○ ● 関数呼び出しの削除 https://pitest.org/quickstart/mutators/ あたりが参考になる © DeNA Co., Ltd. 9

10.

もう一つの例 Query Builder © DeNA Co., Ltd. 10

11.

このTest Codeは十分か? © DeNA Co., Ltd. 11

12.

このTest Codeは十分か? ←Test Data作成部分 © DeNA Co., Ltd. 12

13.

Server Sideプログラミングあるある ● 複雑なSQLのTestにはDBに十分にTest Dataを入れておかなくてはならない ● どれくらいのデータを入れておくと十分なのか、なかなか判断しづらい ● (個人の経験) 100行超えるSQLに対するTestをレビューするのは無理... © DeNA Co., Ltd. 13

14.

Query Builderに対するMutation (仮説) Queryの条件を書き換えてもTestが通る ⇨ 条件の部分をTestできる能力がない © DeNA Co., Ltd. 14

15.

Mutation Testingでわかる可能性があること ● Test Data / Fixtureの網羅性 ● Assertionが正しいか ● ライブラリの挙動を勘違いしていないか ○ ● Equalsの振る舞いの誤解など etc... AI AgentがTestを大量に生成する時代、Testの”正しさ”を計測できる手法になりうる © DeNA Co., Ltd. 15

16.

今日の結論: Mutation Testingをやろう © DeNA Co., Ltd. 16

17.

現実の壁 ● 運用が難しい ● とんでもなく実行時間がかかる © DeNA Co., Ltd. ○ mutant1つごとにTest Suiteを回すのがmutation testingの基本的な挙動 ○ mutantを100個埋め込むとTest実行時間100倍 ○ 何も考えずにやると、大体数万個のmutantを埋め込む ○ 厳しい 17

18.

rmut ● 自作のMutation Testing Toolを作っている ○ DeNAの実プロダクトで運用にチャレンジ中 ● 高速化・運用上の工夫を時間が許す限り話します ● Runtime MUTation © DeNA Co., Ltd. 18

19.

Mutation Testing Toolの素朴な実装 実装は簡単 実行速度は... © DeNA Co., Ltd. 19

20.

AST書き換え & Compileを1回で済ますアイディア こうする これを *説明のために簡略化しています。 実際の実装はもう少し複雑です。 © DeNA Co., Ltd. 20

21.

AST書き換え & Compileを1回で済ますアイディア ● 1回のAST書き換えで 全ての ミューテーションを埋め込み、TestIdを付与する ● TestMainでTestをloopさせ、loopごとにUniqueなTestIdをSetして、それぞれのloopで 異なるMutatorが起動するようにする ● AST書き換え & Compileが1度で済む はやい!!!! © DeNA Co., Ltd. *rmutオリジナルのアイディアというわけではありません。他のmutation testing toolもこの方法で実装され ているものもあります。 21

22.

一旦まとめ ● この設計だけで実行時間の問題が全て解決!とはならない ● (そもそも実装が難しい) ● Mutationの結果無限ループを発生させてしまう問題など 実運用にはまだまだ課題がある ● callgraph解析で効率的にmutation範囲を絞るなどの運用上 の工夫もしています ● この辺りの問題を効率よくテストするための手法について Lint Night #4( https://lintnight.connpass.com/event/385142/ ) で話す予定です © DeNA Co., Ltd. 22

23.

Appendix. © DeNA Co., Ltd. 23

24.

実装の難しさ ● 実装が難しい ● 素朴な実装だと、Compile Errorになるような書き換えを行なっても、そのmutantだ けが失敗(killed)とマークされる ● 今回の実装だとAST書き換えにおいてComile Errorになるような書き換えを行なって しまったら、全てのMutation Testingが動かない。 © DeNA Co., Ltd. 24

25.

Compileができるコードを生成するのが難しい理由 ● 匿名関数でmutatorをwrapするという特性上、書き換え対象のASTが何の型なのかを 知る必要がある ○ ● © DeNA Co., Ltd. ASTだけではわからないので、packages.Loadを使っている この場合、 a + b というexpressionが何の型を持つのかを調べる必要がある 25

26.

注意を要するケース Untyped ● untyped int のケース ● 1000 というリテラルだけをみても型が ● なにも考えないとこうなる 決まらないケースがあり、ASTの親の型 やコンテキストなどを調べる必要があ る。 © DeNA Co., Ltd. 26

27.

Compile Errorは突破しても... ● Mutationによる無限ループ混入問題 ● 解決策 ○ Timeout ■ go標準の仕組みを使える ■ 何万件とtestを回す中で1回の test実行で数分もっていかれ るのは辛い ○ LoopCounterを仕込む © DeNA Co., Ltd. 27

28.

LoopCounter ● for を発見したら、AST書き換え時にLoopCounterを仕込み、n回loopしたらpanicす るようにして、早期にtestを失敗させる ● 全てではないが、ある程度の無限ループには対応できるようになった。 ○ ● © DeNA Co., Ltd. goto や再帰により発生する無限ループなど、まだ対応できていないものもある それでも無限ループしてしまうものはgo testのtimeoutで対応する 28

29.

無限ループ? ● 1万回LoopCounterを回すのに1万秒かかる... ● 未解決問題...? © DeNA Co., Ltd. 29

30.

その他の運用上の工夫 ● sync.*系のmutationの除外(deadlockするので) ● mutation範囲の限定 ○ gitのdiffからcallgraphを解析して、そのPRでどのファイルが影響がありそうか を推定し、mutationをかける範囲を限定する ● custom mutator pluginの仕組み ○ squirrel等のGo標準以外のコードに対するmutatorはpluginで提供 & ユーザが pluginを書いて拡張できるようにする © DeNA Co., Ltd. 30

31.

課題は色々... ● Compile Errorがたまにある ○ そんな構文が!?のような驚き ○ 構文解析・書き換えのツールの効率的なtest手法について Lint Night #4( https://lintnight.connpass.com/event/385142/ )で話す予定です。 ● どこまで行っても実行時間は安定しない ○ ● 急に無限ループを踏むことがある Survivedなmutationであっても意味がないことがある ■ ● © DeNA Co., Ltd. logger, 例外処理など plugin配布の難しさ ○ go/plugin... ○ wasiで配布も難しい 31

32.

Mutation Testingについて知るためのreferences ● https://stryker-mutator.io/docs/ ● https://github.com/hcoles/pitest/blob/master/so_you_want_to_build_mutation_t esting_system.md ● © DeNA Co., Ltd. https://research.google/pubs/state-of-mutation-testing-at-google/ 32

33.

まとめ ● Mutation Testingの紹介 ○ ● ● © DeNA Co., Ltd. Testの品質を測定しよう 自作Mutation Testing Tool, rmutの設計 ○ Compileを一度にすませ、test実行時にmutationを制御する設計 ○ 無限ループへの対応 ○ callgraph解析などで適用範囲を最小化する rmutの課題 ○ 実行時間 ○ plugin 33

34.

© DeNA Co., Ltd. 34