Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8691

Jest + Property based Testing

$
0
0
はじめに 今読み進めている「関数型プログラミングの基礎 JavaScriptを使って学ぶ」の中で紹介されていた Property based testing に興味がわいたのでJavaScript環境で試した話です。 なお、Property based testing とはなんぞや という話については、本稿では詳しく触れません。そちらを知りたい方は、諸先輩方が投稿しているQiita記事などで調べて見てください。 環境 Node.js : v14.17.0 https://nodejs.org/ja/ npm : v6.14.13 jest : v27.0.1 https://jestjs.io/ja/ Facebook のオープンソース フルスタックテスティングフレームワーク fast-check : v2.14.0 https://dubzzz.github.io/fast-check.github.com/ Property based testing のフレームワーク jest-fast-check : v1.0.2 https://github.com/dubzzz/jest-fast-check jest と fast-check の統合用ライブラリ(記述を簡素化できるだけで必須ではない) 準備 作業ディレクトリを用意 (Node.js はインストール済みの前提です。) $ mkdir property-based-testing $ cd property-based-testing $ npm init --yes ライブラリをインストール $ npm install --save-dev jest fast-check jest-fast-check テスティングフレームワークの使用準備 編集前 package.json { "name": "property-based-testing", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "fast-check": "^2.15.0", "jest": "^27.0.3", "jest-fast-check": "^1.0.2" } } "scripts" の "test" を以下のように書き換える package.json抜粋 "scripts": { "test": "jest" }, 実践 題材 「関数型プログラミングの基礎 JavaScriptを使って学ぶ」 でも使われている 後者関数 を使って以下の3つのケースに分けて各テスティングフレームワークを使って行きます。 jest のみを使用 jest + fast-check を使用 jest + fast-check + jest-fast-check を使用 テスト対象 const succ = (x) => { return x + 1; }; テスト内容 上記関数において succ(0) + succ(x) = succ(succ(x)) という命題をProperty based testingを使って確認する 共通部分 3つのケースで共通となるテスト対象の関数を作成します。 $ mkdir src $ touch src/app.js src/app.js const succ = (x) => { return x + 1; }; module.exports = { succ, }; jest のみ使用 テストコードを追加します $ mkdir test $ touch test/app-jest.test.js test/app-jest.test.js const { succ } = require('../src/app'); const iterate = (init) => { return (step) => { return [init, (_) => { return iterate(step(init))(step); }]; }; }; /* 無限の整数列を生成する */ const enumFrom = (n) => { return iterate(n)(succ); }; /* ストリームのmap関数 */ const map = (transform) => { return (aStream) => { console.log(aStream); const head = aStream[0]; return [transform(head), (_) => { return map(transform)(aStream[1]()); }]; }; }; /* ストリームの先頭から引数n分だけ取り出す */ const take = (n) => { return (aStream) => { if (n === 0) { return null; } else { return [aStream[0], (_) => { return take(n-1)(aStream[1]()); }]; } }; }; /* ストリームの全ての要素がtrueであるか判定する */ const all = (aStream) => { const allHelper = (aStream, accumulator) => { const head = aStream[0]; const newAccumulator = accumulator && head; if (aStream[1]() === null) { return newAccumulator; } else { return allHelper(aStream[1](), newAccumulator); } }; return allHelper(aStream, true); }; /* 検証の対象となる命題 */ const proposition = (n) => { return succ(0) + succ(n) === succ(succ(n)); }; /* 100個の整数について命題が正しいか */ test( 'should succ(0) + succ(x) = succ(succ(x))', () => { expect( all( take(100)( map(proposition)(enumFrom(0)) ) ) ).toBe(true); } ); テストの実行は以下のコマンドで行います $ npm test 実行結果 上記のコードでは、1から100の100個の整数で命題が正しいかチェックしています このようにProperty based testingはフレームワークを使用しないでも実現することができます ただし、整数をランダムに生成したり、事前条件などを追加したりしようとすると柔軟性にかけます つまり、検証用のテストデータ生成部分の柔軟性に難がある(手間がかかる)ということです jest + fast-check を使用 では、Property based testingのフレームワークを使用した場合にどうなるかみてみます なお、fast-check の詳細は公式を確認してみてください $ touch test/app-fast-check.test.js test/app-fast-check.test.js const { succ } = require('../src/app'); const fc = require('fast-check'); test( 'should succ(0) + succ(x) = succ(succ(x))', () => fc.assert( fc.property( fc.integer({ min:1 }), (x) => { // console.log(x); expect(succ(0) + succ(x)).toBe(succ(succ(x))); } ) ) ); fast-checkの各関数の詳細については、公式およびGithubを参照してください 上記コードではfc.integer()が検証用のテストデータを生成している部分になります コメントアウトしてる部分を有効にすると、1以上のランダムな整数がテストデータとして使われていることが確認できます jest + fast-check + jest-fast-check を使用 fast-check を使うことにより、テストデータの生成がとてもシンプルに記述することができました ただ、fast-check のみ使用した場合だと、fc.assert()やfc.property()など、fast-check独自の記法が必要となり、jestとの記述の差異が生まれます この差異を埋めるのがjest-fast-checkです jest-fast-checkの詳細はGithubを参照してください jest-fast-checkを使ったコードが以下になります $ touch test/app-jest-fast-check.test.js test/app-jest-fast-check.test.js const { succ } = require('../src/app'); const { testProp, fc } = require('jest-fast-check'); testProp( 'should succ(0) + succ(x) = succ(succ(x))', fc.integer({ min:1 }), (x) => { expect(succ(0) + succ(x)).toBe(succ(succ(x))); } ); jest-fast-checkのソースコードを読むとわかりますが、 testProp()がfc.assert()とfc.property()をtest()内で実行するようなラッパーになっています testProp()を使うと記述量を抑え、よりjestの記法に近い形でProperty based testingのコードを記述できます 参考記事 今回Property based testingを学習する際にこちらの記事を参考にさせていただきました Property based testing を試してみよう

Viewing all articles
Browse latest Browse all 8691

Trending Articles