StrykerおよびWebテストランナーによる突然変異試験の設定


では、突然変異試験は何ですか?さて、それは私たちのテストの品質を評価することができますテストのタイプです.
もちろん、我々のテストがすべての我々のソースコードを実行するかどうか見るためにコードカバレッジをチェックすることができました.それで、我々はすべての可能性をテストして、我々が少しのバグも持っていないと確信していると思うことができました?
では、この小さな例を見てみましょう.
function compareGreaterThan18(a) {
  return a > 18;
}
ここでは、関数のパラメータが18より大きい場合にtrueを返します.

テストランナーウェブテストランナー


  • Webテストランナーをインストールします
    
      npm i --save-dev @web/test-runner
    
    

  • chaiをインストールします
    
      npm i --save-dev @esm-bundle/chai
    
    

  • WTRの設定を作成します(ただし、Webテストランナーpaht/to/*. test . js --ノードの解決で実行できます)
    ジャストクリエイトweb-test-runner.config.mjs プロジェクトのルートのファイル:
    
      export default {
        coverage: true,
        files: ['./src/test/*.test.js'],
        nodeResolve: true,
        rootDir: '../../', //
      }
    
    
    RootdirはMonorepo内のモジュールを解決するために使用されます.この場合、strykerはモジュールを正しく解決することができます.
    すべてのオプションをチェックすることができますhttps://modern-web.dev/docs/test-runner/cli-and-configuration/

  • さて、テストを作成できます.
    
      import { expect } from '@esm-bundle/chai';
      import { compareGreaterThan18 } from '../compareGreaterThan18.js'
    
      describe('compareGreaterThan18', () => {
        it('should return true if the number is greater than 18', () => {
          expect(compareGreaterThan18(27)).to.be.true;
        });
      });
    
    

  • テストを実行する
    
      npx wtr
    
    


  • そして、それで、我々はコード報道の100 %を得ました、しかし、我々はこのテストが十分であると確信していますか?
    いいえ、十分ではありません.誰かが変わるならば、何が起こりますか> コードの内部>= ?... まあ、テストがまだ失敗したときに動作します.
    そして、18が27より低いもう一つのナンバーに変わるならば、同じことは起こります.
    この例では、どのようなテストが追加されているかを確認するのは簡単ですが、コードの変更がバグを追加することができるのは必ずしも簡単ではありません.

    さて、どうやってこれを解決できるか見てみましょう.

    Stryker Mutatorをセットアップしましょう


    StrykerはJavaScriptの突然変異テストフレームワークです.
    いくつかの変異体を追加することでコードを変更します.例えば、前の関数では> to >= または、< .
    それから、あなたのテストが失敗するならば、変異体は殺されます.
    それで、若干の変異体を殺しましょう.

  • インストール
    
      npm i --save-dev @stryker-mutator/core
    
    

  • Strykerの設定を作成する
    ファイル名stryker.conf.js
    
      /**
      * @type {import('@stryker-mutator/api/core').StrykerOptions}
      */
      module.exports = {
        testRunner: 'command',
        files: ['src/*.js', 'src/**/*.test.js', 'package.json', '*.mjs'],
        mutate: ['src/*.js', '!src/**/*.test.js'],
        packageManager: 'npm',
        reporters: ['html', 'clear-text', 'progress'],
      };
    
    
    ここでは、テストのランナーを設定します.この場合は、コマンドを実行しますnpm test .
    files プロパティは、テストランナーのサンドボックスにファイルを含める必要があります通常、それを設定する必要はありませんので、デフォルトでは、gitで無視されないすべてのファイルを使用する必要があります.
    それから、我々は変異したいファイルを加えます'src/*.js' そして、我々が変異したくないもの'!src/**/*.test.js' を返します.
    すべてのオプションをチェックすることができますhttps://stryker-mutator.io/docs/stryker/configuration

  • テストコマンドを設定してWTRを実行します
    
      "scripts": {
        "test": "wtr"
      },
    
    

  • 私たちのWebテストランナーの設定を変更します
    ストーカー使用mutation switching すべての変異体を同時にコードに入れることができるようにするには、各突然変異を実行する前にコードを変更する必要はありません.
    それから、それはどの変異がテストされているかを選択するために環境変数を使います__STRYKER_ACTIVE_MUTANT__ .
    Webテストランナーでは、テストをブラウザで実行しているので、テストを読むことができ、それを使用できるように、この変数を注入する必要があります.
    我々の中でweb-test-runner.config.mjs TestunnerHTMLプロパティをアクティブな突然変異体に設定します.
    
      function getCurrentMutant() {
        return process.env.__STRYKER_ACTIVE_MUTANT__;
      }
    
      export default {
        coverage: true,
        files: ['./src/test/*.test.js'],
        nodeResolve: true,
        rootDir: '../../',
        testRunnerHtml: testFramework =>
          `<html>
            <body>
              <script>
                  window.__stryker__ = window.__stryker__ || {};
                  window.__stryker__.activeMutant = ${getCurrentMutant()};
                  window.process = {
                      env: {
                          __STRYKER_ACTIVE_MUTANT__: ${getCurrentMutant()},
                      }
                  }
              </script>
              <script type="module" src="${testFramework}"></script>
            </body>
          </html>`,
      }
    
    
    バージョン5から、そして、Stryker__STRYKER_ACTIVE_MUTANT__ and activeMutant 文字列型でなければならないので、式の周りに二重引用符または単一引用符を入れてください${getCurrentMutant()} .
    
        window.__stryker__ = window.__stryker__ || {};
        window.__stryker__.activeMutant = '${getCurrentMutant()}'; // Single quotes to be sure it is a string so it works on Stryker version 5
        window.process = {
            env: {
                __STRYKER_ACTIVE_MUTANT__: '${getCurrentMutant()}',  // Single quotes to be sure it is a string so it works on Stryker version 5
            }
        }
    
    

  • 今、我々は突然変異のテストを実行することができます
    
      npx stryker run    
    
    
    一旦終了すると、次のようなレポートが表示されます.

    この場合、我々のテストは5のうち2つの変異体から生き残ることができなかった.
    さあ、突然変異体を殺しましょう!
  • 変異体を殺すためにいくつかのテストを加えましょう


    最初の生存突然変異体は次のようなものです
    -    return a > 18;
    +    return true;
    
    マイナス記号は変更されたものを示し、プラスは変更されたことを示します.
    ここでは、我々の声明が常に戻るために変わるならば、我々はわかることができますtrue , 私たちのテストは、すべてが大丈夫です、それはそうではありません、そして、それは将来バグの起源でありえました.
    それで、それを修正しましょう、我々が18より低いならば、我々が何が起こるかについてチェックするテストを加えなければなりません.
    it('should return true if the number is greater than 18', () => {
      expect(compareGreaterThan18(14)).to.be.false;
    });
    
    このテストで、我々は1つの変異体を殺して、我々は1つを残して殺すことができます.
    -    return a > 18;
    +    return a >= 18;
    
    この突然変異体は、私たちが、18が18ならば何が起こるかをチェックしないということを蒔いています.
    it('should return true if the number is greater than 18', () => {
      expect(compareGreaterThan18(18)).to.be.false;
    });
    
    そしておめでとう、今我々はすべての変異体を殺すことがあります!

    結論


    これにより、コードカバレッジが私たちのテストが良いか悪いかを教えてくれませんでした.その代わりに、我々はStrykerと同様に突然変異試験を実行すべきです.
    つの方法は、私たちのテストについては、自信を持って、例えば、スコアのスコアをチェックすることです.
    そして、突然変異試験は多くの時間を取ることができます、例では、それはすべてのテストを実行するために3秒かかるだけであることを示しました、しかし、あなたのプロジェクトが成長するとき、それは大いに多くをします.
  • 突然変異をする必要がある変異、デモフォルダやモックを変異しないでください.
  • テストのパフォーマンスを向上させようとします.テストを同時に実行し、テストを実行するために必要なものだけをロードします.
  • 参考文献

  • https://modern-web.dev/docs/test-runner/overview/
  • https://stryker-mutator.io/
  • イゴメザル / Strykerウェブテストランナー


    ウェブテストランナーと併用したStryker突然変異試験の例