Jestで単体テストをやっていて、現在実行中のテストケースの名前を取得したいと思ったことはないですか?はい、私は思いました。現在実行中のテストケースの名前が取得できれば例えばこんなことができるはずです。
describe('example', () => {
beforeEach(async () => {
// testCaseName(これから実行するテストケースの名前)が取れれば...
// executeSqlは指定したsqlファイルを実行する関数だと思ってください
await executeSql(testCaseName + '.sql')
})
test('test1', () => {
// test1.sqlが実行された後
})
test('test2', () => {
// test2.sqlが実行された後
})
})
あとは、適当な場所にtest1.sql
やtest2.sql
を用意しておけば、テストケースが実行される前に一律、それぞれのテストケースの前処理のためのSQLを流し込むことができるでしょう。afterEachによる後処理も同様です。
describeやtestの返り値
一応、describe
やtest
は返り値として、名前等を含むメタ情報を取得できます。
const example = describe('example', () => {
beforeEach(() => {
// describeのメタ情報
console.log(example)
})
const test1 = test('test1', () => {
// testのメタ情報
console.log(test1)
expect(1 + 1).toBe(2)
})
})
中を見てみるとdescription
というものがあって、desribe
やtest
の名前がわかります。
// describe(一部)
Suite {
id: 'suite1',
description: 'example',
throwOnExpectationFailure: false
}
// test(一部)
Spec {
resultCallback: [Function],
id: 'spec0',
description: 'test1'
}
ただ、これでは現在実行中のテストケースがどれなのかわかりませんし、正直使い所がわからんです。
Jasmineのカスタムレポーターを使う
実はJestはJasmineを元にしている(最近知った)のでJasmineの機能を使えたりします。なので、Jestが入っていれば追加でJasmineをインストールする必要もありません。そして、Jasmineにはカスタムレポーターというものがあって、テストのライフサイクルの各タイミングでフックすることができます。キーを各フックポイントの名前、値をそのタイミングで実行したい関数にしたオブジェクトを作り、jasmine.getEnv().addReporter
に渡します。
const reporter = {
jasmineStarted(suiteInfo) {
console.log('jasmineStarted')
console.log(suiteInfo)
},
suiteStarted(result) {
console.log('suiteStarted')
console.log(result)
},
specStarted(result) {
console.log('specStarted')
console.log(result)
},
specDone(result) {
console.log('specDone')
console.log(result)
},
suiteDone(result) {
console.log('suiteDone')
console.log(result)
},
jasmineDone() {
console.log('jasmineDone')
}
}
jasmine.getEnv().addReporter(reporter)
各フックポイントは以下のタイミングで実行されます。
フック | タイミング | 関数で受け取る引数 |
---|---|---|
jasmineStarted | テスト全体が開始するとき | suiteInfo(テストケースの合計数) |
suiteStarted | 各describeが開始するとき | result(describeのメタ情報) |
specStarted | 各testが開始するとき | result(testのメタ情報) |
specDone | 各testが終了したとき | result(testのメタ情報、テストの成功・失敗等の情報も追加される) |
suiteDone | 各describeの全てのテストが終了したとき | result(describeのメタ情報、テストの成功・失敗等の情報も追加される) |
jasmineDone | テスト全体が終了したとき | なし |
suiteStarted
やspecStarted
のコールバック関数の引数で受け取れるオブジェクトにはそのdescribeやtestの名前の情報も含まれていて、しかも各テストケースが開始するタイミングで毎回呼び出されるので、これは使えそうですね!
// specStartedのresultの例
{
id: 'spec0',
description: 'test1',
fullName: 'example test1',
failedExpectations: [],
passedExpectations: [],
pendingReason: '',
testPath: '/hogehoge/hoge.test.js'
}
ちなみにdescription
とfullName
と2つありますが、fullName
の方は親以上のdescribeの名前もスペース区切りで含まれるようになっています。「desc1というdescribeの中にあるdesc2というdescribeの中にあるtest1というtest」の場合ですと、fullNameはdesc1 desc2 test1
といった感じになります。
カスタムレポーターで現在実行中のテストケースの名前を取得する
さて、冒頭にお見せしたものをカスタムレポーターで実現してみましょう。
let testCaseName
const reporter = {
// 各testが開始するタイミングで呼び出される
specStarted(result) {
testCaseName = result.description
}
}
jasmine.getEnv().addReporter(reporter)
describe('example', () => {
beforeEach(async () => {
await executeSql(testCaseName + '.sql')
})
test('test1', () => {
// test1.sqlが実行された後!
})
test('test2', () => {
// test2.sqlが実行された後!
})
})
specStarted
で各testが開始するタイミングでフックし、そのtestの名前をtestCaseName
に代入しています。テストケースの名前を保持するいい方法が思いつかなかったので、とりあえずグローバル変数にぶちこんでいます。それをbeforeEach
で参照しているという形ですね。もっとイケてる方法を知っている偉い人いたら教えてください。
いかがでしたでしょうか。今回紹介した各テストケース用のSQLを流し込むという用途以外にもいろいろ使えそうですね。ぜひ活用してみてください。