🍃このブログは移転しました。
3秒後、自動的に移動します・・・。

Result outputs go wrong with using async tasks in `{before,after}xxx` #jest

  • beforeAll
  • afterAll
  • beforeEach
  • afterEach

jestではこの4つがあるけど、この中で非同期処理をすると、途端にテスト実行時のログの体裁がおかしくなるという話。

あと、`--forceExit`というオプションを組み合わせるとまた違った体裁になってよーわからんという話。

同期処理だけの正常パターン

beforeAll(() => {
  console.log('beforeAll');
});
beforeEach(() => {
  console.log('beforeEach');
});
afterEach(() => {
  console.log('afterEach');
});
afterAll(() => {
  console.log('afterAll');
});

describe('d1', () => {
  test('t11', () => { expect('ok'); });
});
describe('d2', () => {
  test('t21', () => { expect('ok'); });
  test('t22', () => { expect('ok'); });
});
describe('d3', () => {
  test('t31', () => { expect('ok'); });
});

これを普通に実行すると、ログは想定通りこうなる。

pass  __tests__/foo.test.js
 d1
   ✓ t11 (2ms)
 d2
   ✓ t21
   ✓ t22 (1ms)
 d3
   ✓ t31 (1ms)

 console.log __tests__/foo.test.js:4
   beforeAll

 console.log __tests__/foo.test.js:8
   beforeEach

 console.log __tests__/foo.test.js:13
   afterEach

 console.log __tests__/foo.test.js:8
   beforeEach

 console.log __tests__/foo.test.js:13
   afterEach

 console.log __tests__/foo.test.js:8
   beforeEach

 console.log __tests__/foo.test.js:13
   afterEach

 console.log __tests__/foo.test.js:8
   beforeEach

 console.log __tests__/foo.test.js:13
   afterEach

 console.log __tests__/foo.test.js:16
   afterAll

test suites: 1 passed, 1 total
tests:       4 passed, 4 total
snapshots:   0 total
time:        0.874s, estimated 1s

何の問題もない。

  • テストのファイル別結果
  • `console.log`
  • テスト全体に関する情報

という感じで見やすい。

非同期処理を`{before,after}xxx`でやるパターン

ただし、非同期をまぜると途端に出力されるログがおかしくなる。

const sleep = msec => new promise(resolve => settimeout(resolve, msec));

// ここだけ変えてみる
beforeEach(done => {
  sleep(100).then(() => {
    console.log('beforeEach');
    done();
  });
});

各テストの直前で100ms待つように。

すると、

  console.log __tests__/foo.test.js:4
    beforeAll

  console.log __tests__/foo.test.js:8
    beforeEach

  console.log __tests__/foo.test.js:13
    afterEach

  console.log __tests__/foo.test.js:8
    beforeEach

  console.log __tests__/foo.test.js:13
    afterEach

  console.log __tests__/foo.test.js:8
    beforeEach

  console.log __tests__/foo.test.js:13
    afterEach

 pass  __tests__/foo.test.js
  d1
    ✓ t11 (107ms)
  d2
    ✓ t21 (102ms)
    ✓ t22 (106ms)
  d3
    ✓ t31 (102ms)

test suites: 1 passed, 1 total
tests:       4 passed, 4 total
snapshots:   0 total
time:        1.04s
  console.log __tests__/foo.test.js:8
    beforeEach

  console.log __tests__/foo.test.js:13
    afterEach

  console.log __tests__/foo.test.js:16
    afterAll

というようになる。
さっきとログの体裁が違う!なんで!

Promiseのところは`async / await`にしても同じ結果になる。

// こっちでも
beforeEach(async done => {
  await sleep(100);
  console.log('beforeEach');
  done();
});

// こっちでも一緒だった
beforeEach(async () => {
  await sleep(100);
  console.log('beforeEach');
});

ログが気持ち悪い。

forceExitをつけて実行する

Jest process doesn’t quit after last test completes · Issue #1456 · facebook/jest · GitHub

このissueにもあるように、処理が終わらないとjestの実行も終わらない場合がある。
そんな時に、強制終了したいよねって話で、`--forceExit`ってオプションがある。

さっきの非同期コードにこれを使うと・・、

console.log __tests__/foo.test.js:4
  beforeAll

console.log __tests__/foo.test.js:8
  beforeEach

console.log __tests__/foo.test.js:11
  afterEach

console.log __tests__/foo.test.js:8
  beforeEach

console.log __tests__/foo.test.js:11
  afterEach

console.log __tests__/foo.test.js:8
  beforeEach

console.log __tests__/foo.test.js:11
  afterEach

pass  __tests__/foo.test.js
d1
  ✓ t11 (108ms)
d2
  ✓ t21 (102ms)
  ✓ t22 (105ms)
d3
  ✓ t31 (101ms)

test suites: 1 passed, 1 total
tests:       4 passed, 4 total
snapshots:   0 total
time:        1.043s

と、`afterAll`が呼ばれてないのに終わる・・ように見える。
ただし実際は`afterAll`は動いてるっぽく、適当に`fs.writeFile`とかやるとわかる。
けど、ログには出ない。謎。

これはバグなのかなんなのか・・。