Vitestで書いたテストに対してStrykerを利用したMutation Testingを試してみる
TL;DR
- Mutation Test とは
- 実際に
Stryker
という JS のライブラリを利用して、Mutation Test をやってみる
Mutation Test とは
Mutation = 突然変異 = コードを意図的に変更し、バグを植え付けること
だそうです。
Mutation Test においてはライブラリなどを利用し、コードの一部を意図的に変更します。
例えば、a===0
をa!==0
のように改変する(=変異させる)といった感じです。
その後テストを実行し、正しくテストが書かれていたとすれば、結果は変異前と変わるためテストが失敗するはずです。
しかし、正しくテストが書かれていなければテストは失敗しません。
Mutation Test においてはこれを、Killed
とSurvived
と定義しています。
- Killed
- 変異後、成功すべきテストが失敗したことにより検知された変異の数
- Survived
- 変異後、失敗すべきテストが成功したことにより検知された変異の数
つまり、Survived
の数が多ければ多いほど、テストコードの品質が低いことを著しています。
以上から、Mutation Test を導入することで何がよいかというと、見かけ上のコードカバレッジが高く、作成したソースコード全般的にテストコードが網羅できていたとしても、テストコードが正しく書けているとは限らないので、その部分を担保してくれるということです。
例えば以下のようなテストコードはカバレッジ 100%ですがテストには全く意味がありません。
function addNewOrder(newOrder) {
logger.log(`Adding new order ${newOrder}`);
DB.save(newOrder);
Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`);
return { approved: true };
}
it("Test addNewOrder, don't use such test names", () => {
addNewOrder({ assignee: "John@mailer.com", price: 120 });
}); //Triggers 100% code coverage, but it doesn't check anything
ここまでの話はjavascript-testing-best-practicesの内容を要約したものですので、よければ原文の方も見てみてください。
Stryker を利用した Mutation Test
まず公式が提供しているサンプルプロジェクトを利用して、実際に Mutation Test を実行してみます。
git clone https://github.com/stryker-mutator/robobar-example
npm install
ライブラリを install 後、まずはテストを実行します。
npm test
そうすると、カバレッジレポートがreports/coverage/lcov-report/index.html
に出力されます。
レポートを確認すると、一見カバレッジが 100%のように見えます。
この 100%が本当に信じられる数値なのか?を今度は MutationTest を実行して確認してみます。
npm run test:mutation
そうすると、今度はreports/mutation
ディレクトリに実行結果が出力されます。
このレポートを確認すると、159件の変異のうち、killedされたのは86件だったので、実際のカバレッジとしては50%後半くらい
であることが判明しました。
既存のプロジェクトに Stryker を導入する
では次は、既存のプロジェクトに Striker を導入してみようと思います。
私が個人的に運営しているこちらのリポジトリに入れてみようと思います。テストランナーはVitest
を使っています。
ではまず現状のカバレッジを確認してみます。
なかなか優秀なカバレッジのようですが、これが本当に信じられる値かどうか Stryker に検証してもらいましょう。
まずはstryker-cli
をインストール。
npm i -g stryker-cli
その後、Stryker を導入したいプロジェクトのルートディレクトリで以下を実行すると、対話形式で設定が開始されます。
stryker init
Stryker is currently not installed.
? Do you want to install Stryker locally? npm
|STRYKER|
~control the mutants~
..####@####..
.########@########.
.#####################.
#########################
###########################
###########################
@@@#####################@@@
###########################
###########################
#########################
'######################'
'########@#########'
''####@####''
Installing: npm install --save-dev @stryker-mutator/core
その後 3 つ質問されるので答えてあげます。
? Are you using one of these frameworks? Then select a preset configuration. create-react-app
? What file type do you want for your config file? JSON
Writing & formatting stryker.config.json...
? Which package manager do you want to use? npm
すると JSON で以下のような設定ファイルが作成されます。
{
"$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
"_comment": "This config was generated using 'stryker init'. Please see the guide for more information: https://stryker-mutator.io/docs/stryker-js/guides/react",
"testRunner": "jest",
"reporters": ["progress", "clear-text", "html"],
"coverageAnalysis": "off",
"jest": {
"projectType": "create-react-app"
}
}
この状態でテストを実行すると、testRunner
がvitest
になっていないのでテストが失敗します。また、vitest
に変えてあげても失敗します。
どうやら追加でvitest
のテストランナーを入れてあげないとダメらしいです。
npm i --save-dev @stryker-mutator/vitest-runner
この状態でstryker run
をするとテストが実行され、先ほどと同じくreport/mutation
ディレクトリにレポートが作成されました。
かなりMutation
が生き残ってしまっており、実は有効なテストになってなさげな箇所があることがわかります。
それがどこかを確認しましょう。
ファイル名をクリックすると上記のような画面が表示され、赤線部分が Stryker による変異箇所です。
差分形式で表示されているのでわかりやすいですね。
これをみながら、「どういった変異に対してテストが有効にできていないか?」を修正していく必要があります。
これは後ほど地道に直していきます、、、
おわりに
見かけ上のカバレッジが信用に足りない可能性があり、テストの品質を上げてくれるのは嬉しいなと思いました。
Kotlin にも Mutation テストをかけるライブラリがあるのか、また調べたいです。
メンバー募集中!
サーバーサイド Kotlin コミュニティを作りました!
Kotlin ユーザーはぜひご参加ください!!
また関西在住のソフトウェア開発者を中心に、関西エンジニアコミュニティを一緒に盛り上げてくださる方を募集しています。
よろしければ Conpass からメンバー登録よろしくお願いいたします。
Discussion