Stryker、JavaScriptでの突然変異テスト

最近、私は「突然変異テスト」と呼ばれるソフトウェアテスト方法に精通し、テストを書くためのこのアプローチのファンになりました。







理論第一



突然変異テストの目的は、効果のないテストや不完全なテストを識別することです。つまり、本質的にテストをテストすることです。







アイデアは、ソースコードの小さなランダムな断片を修正し、テストの反応を観察することです。 変更を行った後もテストが合格した場合、そのような一連のテストは無効または不完全です。







ソースコードで変換を実行するルール(たとえば、 false



ではなくtrue



を置換するルール)は、 ミューテーター(mutation operator)と呼ばれます。 ミューテーターとして、算術演算とブール演算子の符号の置換、場所での変数のゼロ化と再配置、コード分岐の削除なども使用します。 ソースコードに加えられた変更は、 突然変異と呼ばれます。 突然変異の獲得の結果として、ソースコードは突然変異し、 突然変異体になります。 テスト後、変異体は2つのカテゴリに分類されます。









自動ミューテーションテストでは、元のソースコードのミュータントが多数作成され、それぞれに対してテストセットが実行されます。







突然変異テストの有効性の測定基準はMSIインジケーター(突然変異スコアインジケーター)であり、これは殺された突然変異体と生存者の比率を反映しています。 MSIとテストでのコードカバレッジの割合の差が大きいほど、テストの品質を評価するための情報量の少ない基準がカバレッジの割合になります。







ミューテーターの組み合わせが相互に排他的なミューテーションを引き起こすことがあり、結果のミュータントは(元のプログラムと)同等であると言います。 これは、小規模プロジェクトでもMSIを100%で達成することが非常に難しい理由の1つです。







今すぐ練習



Strykerと呼ばれる自動化された突然変異テストフレームワークについて説明します。







プロジェクトを準備するには、stryker-cliパッケージをグローバルにインストールします。







 npm i -g stryker-cli
      
      





次に、プロジェクトdevsにstrykerおよびstryker-apiパッケージをインストールして保存します







 npm i --save-dev stryker stryker-api
      
      





Mochaを自動テストフレームワークとして使用し、 Chaiはステートメントライブラリとして私に馴染みがあります。







 npm i --save-dev chai mocha@3.5.0
      
      





stryker init



実行してみましょう。この初期化ユーティリティはいくつかの質問をします。設定と構成に従ってすべてを選択し、さらにレポートのリストにhtmlアイテムを追加しました。 これは次の行と同等です。







 npm i --save-dev stryker-api stryker-mocha-runner stryker-mocha-framework stryker-html-reporter
      
      





構成の最後にstryker.conf.js



次の内容stryker.conf.js



ファイルが作成されます。







 module.exports = function(config) { config.set({ files: [{ pattern: 'src/**/*.js', mutated: true, included: false }, 'test/**/*.js' ], mutate: [], testRunner: 'mocha', testFramework: 'mocha', mutator: 'es5', transpilers: [], reporter: ['html', 'clear-text', 'progress'], coverageAnalysis: 'perTest' }); };
      
      





オプションを見つけて、自分用に設定します。









実用的な例として、次の構造を持つプロジェクトを作成しました







 ├── app.js ├── package.json ├── stryker.conf.js └── test └── app.test.js
      
      





メインファイルには1つの関数のみが含まれ、エクスポートされます







 // app.js module.exports = { userIsOldEnough: (user) => user.age >= 18 };
      
      





突然変異テストの概念を実証するために、2回のパスでも、プロジェクトに100%カバレッジの単体テストを提供します。







 // test/app.test.js const expect = require('chai').expect, app = require('../app'); describe('Site', () => { it('can be visited by an adult', () => { expect(app.userIsOldEnough({ age: 23 })).to.be.true; }); it('can not be visited by a child', () => { expect(app.userIsOldEnough({ age: 13 })).to.be.false; }); });
      
      





Stryker構成ファイルは次のようになります







 // stryker.conf.js module.exports = function(config) { config.set({ files: [{ pattern: 'app.js', mutated: true }, 'test/**/*.js' ], testRunner: 'mocha', reporter: ['html', 'clear-text', 'progress'], testFramework: 'mocha' }); };
      
      





また、便宜上、 package.json



いくつかのスクリプトを追加しました。







 { "name": "mutations-demo", "version": "1.0.0", "private": true, "scripts": { "test": "istanbul cover _mocha", "posttest": "stryker run" }, "main": "app.js", "devDependencies": { "chai": "^4.1.2", "mocha": "^3.5.0", "istanbul": "^0.4.5", "stryker": "^0.13.0", "stryker-api": "^0.11.0", "stryker-html-reporter": "^0.10.1", "stryker-mocha-framework": "^0.6.1", "stryker-mocha-runner": "^0.9.1" }, "dependencies": { "underscore": "^1.8.3" } }
      
      





実行する







 npm t
      
      





そして今、楽しい部分が始まります。すべてのユニットテストに合格し、コードの100%をカバーしていることを確認できます。







  Site ✓ can be visited by an adult ✓ can not be visited by a child 2 passing (15ms) =============================== Coverage summary =============================== Statements : 100% ( 2/2 ) Branches : 100% ( 0/0 ) Functions : 100% ( 0/0 ) Lines : 100% ( 2/2 ) ================================================================================
      
      





その後、突然変異テストが自動的に開始されます。ここでは、MSI 50%の形式で悪いニュースを取得します。







 Mutant survived! Mutator: BinaryOperator - userIsOldEnough: (user) => user.age >= 18 + userIsOldEnough: (user) => user.age > 18 Tests ran: Site can be visited by an adult Site can not be visited by a child Ran 1.50 tests per mutant on average. ----------|---------|----------|-----------|------------|----------|---------| File | % score | # killed | # timeout | # survived | # no cov | # error | ----------|---------|----------|-----------|------------|----------|---------| All files | 50.00 | 1 | 0 | 1 | 0 | 0 | app.js | 50.00 | 1 | 0 | 1 | 0 | 0 | ----------|---------|----------|-----------|------------|----------|---------|
      
      





レポートは、テスト操作が>=



から>



への論理操作の変更の影響を受けなかったため、テストが不完全であると結論付けました。したがって、サイトのユーザーが18歳の場合、テストは機能をチェックしません。 このレポートはコミット間の差分のように見えますが、設定によると、より美しいレポートが同様の htmlドキュメントの形式で生成されます。







このプロジェクトのリポジトリはGithubにあります。 そして、あなたは何も上げることができず、ログを見ることができるように、私はTravisにプロジェクトを追加しました。








All Articles