結論
csvで作ったデシジョンテーブルっぽいcsvから値を取得し、指定された項目に設定するカスタムコマンドを作成する。
やりたいこと
こんな感じのフォームをつくったとします。
今回はVue.jsとElementで作りました。
入力項目が5つあり、Sign Upを押したときに入力項目にエラーがなければ登録されます(今回は登録の処理は抜きにして、代わりに一番下のラベルに結果が出るようになっています)。
これに対するテストを考えたとき、デシジョンテーブルを書いてみると大体こんな感じ。
〇がついている項目がそのケースで採用される値で、暗い部分がエラーケースです。
当たり前ですが、これを手作業でテストすると面倒。
Vue.jsではNightwatchでe2eテストが自動でできる環境が整っているので、それを使ってみると下記のようになります。
browser.currentTest.name
は現在のテストケース名(下記の場合case1)を取得しています。
'case1': function test(browser) {
const devServer = browser.globals.devServerURL;
browser
.url(devServer)
.waitForElementVisible('#app', 5000)
.setValue('#name input', 'abcdefghijkl')
.setValue('#age input', '100')
.setValue('#password input', 'abcdefgh')
.setValue('#passconf input', 'abcdefgh')
.setValue('#desc textarea', 'abcdefghij')
.click('#btn-signup')
.assert.containsText('#result span', 'success')
.saveScreenshot('./ss/' + browser.currentTest.name + '.png')
.end();
},
毎回setValue
を書かなきゃないのが結構面倒なので、下記のようにcsvのパスとケース名を指定したらその部分の値を自動で各項目に設定してくれるsetCsvValue
というコマンドを作ります。
'case1': function test(browser) {
const devServer = browser.globals.devServerURL;
browser
.url(devServer)
.waitForElementVisible('#app', 5000)
.setCsvValue('test/e2e/decision_table.csv', browser.currentTest.name)
.click('#btn-signup')
.assert.containsText('#result span', 'success')
.saveScreenshot('./ss/' + browser.currentTest.name + '.png')
.end();
},
setCsvValueコマンドを作る
csv読み込みを準備する
読み込む用のCSVを作成します。
といっても、selectorの行を追加して、そこに各要素のセレクタを記述したくらいです。
#name input
のようになっているのはElementを使っているためinput要素に直接idを付与できなかったためです。
また、npm install csv --save-dev
コマンドを使って、csvを入れておいてください。
カスタムコマンドを作る
test/e2e
の下にcustom_commands
フォルダを作り、そこにカスタムコマンドを作っていきます。
カスタムコマンドでは、ファイル名がそのままコマンド名になるのでsetCsvValue.js
ファイルを作成し、処理を記述していきます。
const fs = require('fs');
const csvPerser = require('csv-parse/lib/sync');
exports.command = function(filePath, caseName, callback) {
// 列名の設定
const selectorColumn = 'selector';
const valueColumn = 'value';
const enableMark = '〇';
// csv読み込み
let csv;
try {
let stream = fs.readFileSync(filePath);
csv = csvPerser(stream, {columns: true});
} catch (err) {
console.log(err);
throw 'Unable to open file: ' + filePath;
}
// テストデータの整形
let testData = {};
csv.forEach((row, index, csv) => {
if (row[selectorColumn] !== '') {
this.selector = row[selectorColumn];
}
if (row[caseName] === enableMark) {
testData[this.selector] = row[valueColumn];
}
}, {selector: ''});
// 値の設定
Object.keys(testData).forEach((key) => {
this.setValue(key, testData[key]);
});
}
処理は大きく4つに分かれます。
列名の設定
CSVで必要となる列名を定義します。
selectorColumnはセレクタ列、valueColumnは入力値の列を指定します。
また、enableMarkにはどの値を使うか表している文字(今回の場合は〇)を指定します。
CSVの読み込み
CSVファイルからデータを読み込みます。
csvParseのオプションで{columns:true}
を使うことで1行目を列名として扱い、オブジェクトの配列としてCSVを取得することができます。
例えば、2行目のデータは以下のようなオブジェクトになります。
{ item: 'Name',
selector: '#name input',
description: '12文字',
value: 'abcdefghijkl',
case1: '〇',
case2: '',
case3: '',
case4: '',
case5: '',
case6: '〇',
case7: '〇' },
テストデータの整形
読み込んだCSVからテストデータを扱いやすい形に整形します。
各行の情報を見ていき、引数で渡されたcaseName
のプロパティ値がenableMark
と一致するselector
とvalue
を格納していきます。
この時、selector
が空の場合に一番近い上の行にある値を取得するために、一時的なオブジェクトに値を保持しながら繰り返します。
最終的に以下のようなオブジェクトが得られます。
{ '#name input': 'abcdefghijkl',
'#age input': '100',
'#password input': 'abcdefgh',
'#passconf input': 'abcdefgh',
'#desc textarea': 'abcdefghij' }
値の設定
setValue
を使って各項目に値を設定します。
カスタムコマンドを使う
nightwatch.conf.js
にcustom_commands_path
を指定します。
custom_commands_path: ['test/e2e/custom-commands'],
custom_assertions_path: ['test/e2e/custom-assertions'],
後はテストケースとして追加していくだけ。
'case1': function test(browser) {
const devServer = browser.globals.devServerURL;
browser
.url(devServer)
.waitForElementVisible('#app', 5000)
.setCsvValue('test/e2e/decision_table.csv', browser.currentTest.name)
.click('#btn-signup')
.assert.containsText('#result span', 'success')
.saveScreenshot('./ss/' + browser.currentTest.name + '.png')
.end();
},
このテストケースは、csvのケース名とテストケース名を合わせているので、setCsvValueの第二引数をbrowser.currentTest.name
にしています。
そのため、テストケース名だけを変えるだけで各テストケースを実行することができます。
saveScreenshot
で各テストケースの入力後の状態のスクリーンショットが自動で保存されるようになっています。
まとめ
CSVから値をセットしてみました。
毎回テストケースを書くよりは楽にテストできるようになるのではないでしょうか。
毎回CSVを読み込んだり、テストケース内で共通な部分も多くあるため、もっと作りこんで効率化することができそうですね。