Jestで現在実行中のテストケースの名前を取得する

Jestで単体テストをやっていて、現在実行中のテストケースの名前を取得したいと思ったことはないですか？はい、私は思いました。現在実行中のテストケースの名前が取得できれば例えばこんなことができるはずです。

```javascript
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`は返り値として、名前等を含むメタ情報を取得できます。

```javascript
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`に渡します。

```javascript
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`といった感じになります。

## カスタムレポーターで現在実行中のテストケースの名前を取得する
さて、冒頭にお見せしたものをカスタムレポーターで実現してみましょう。

```javascript
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を流し込むという用途以外にもいろいろ使えそうですね。ぜひ活用してみてください。