メインコンテンツへスキップ
Version: 27.x

セットアップと破棄

テストを書いている際にしばしば、テストを実行する前にいくつかのセットアップ作業をしたり、テストが終了した後にいくつかの仕上げ作業をしたい場合があります。 Jest はこれらを処理するヘルパー機能を提供します。

Repeating Setup

If you have some work you need to do repeatedly for many tests, you can use beforeEach and afterEach hooks.

たとえば、いくつかのテストが City のデータベースと関係するとしましょう。 そしてこれらのテストの前に initializeCityDatabase() を呼び出す必要があり、テストの後にはclearCityDatabase() を呼び出す必要があるとします。 その場合、以下のようにできます:

beforeEach(() => {
initializeCityDatabase();
});

afterEach(() => {
clearCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

beforeEachafterEach非同期コードをテストする と同様に非同期コードを扱えます - promise を返すか done パラメータのどちらかを選択します。 たとえば、もし initializeCityDatabase() が promiseを返すのであれば、データベースが初期化された際には promise が返されることが望まれます。

beforeEach(() => {
return initializeCityDatabase();
});

ワンタイムセットアップ

セットアップがファイルの先頭で一回だけ実行されることが必要なケースがあります。 このセットアップが非同期で行われる場合は特に面倒になるので、インラインでは実施できません。 Jest provides beforeAll and afterAll hooks to handle this situation.

For example, if both initializeCityDatabase() and clearCityDatabase() returned promises, and the city database could be reused between tests, we could change our test code to:

beforeAll(() => {
return initializeCityDatabase();
});

afterAll(() => {
return clearCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

スコープ

デフォルトでは、 beforeafter ブロックは、ファイル内のすべてのテストに適用されます。 一方であなたは describe ブロックを使って複数のテストをグループ化することができます。 それらのブロックが describe ブロックの中にあるときは、 beforeafter ブロックは describe ブロックの中のテストにだけに適用されます。

たとえば、都市データベースだけでなく、食品データベースもあるとします。 テストごとに異なるセットアップを行うことができます:

// Applies to all tests in this file
beforeEach(() => {
return initializeCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

describe('matching cities to foods', () => {
// Applies only to tests in this describe block
beforeEach(() => {
return initializeFoodDatabase();
});

test('Vienna <3 veal', () => {
expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true);
});

test('San Juan <3 plantains', () => {
expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true);
});
});

最上位の beforeEachdescribe 内の beforeEach より前に実行されることに注意してください。 以下のコードを実行すると、全フックの順序が理解できると思います。

beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));

test('', () => console.log('1 - test'));

describe('Scoped / Nested block', () => {
beforeAll(() => console.log('2 - beforeAll'));
afterAll(() => console.log('2 - afterAll'));
beforeEach(() => console.log('2 - beforeEach'));
afterEach(() => console.log('2 - afterEach'));

test('', () => console.log('2 - test'));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll

Order of Execution

Jest は、テストファイル内のすべての describe ハンドラを、実際のすべてのテストを実行する前に実行します。 This is another reason to do setup and teardown inside before* and after* handlers rather than inside the describe blocks. Once the describe blocks are complete, by default Jest runs all the tests serially in the order they were encountered in the collection phase, waiting for each to finish and be tidied up before moving on.

次のような説明のためのテストファイルとその出力について見てみましょう。

describe('describe outer', () => {
console.log('describe outer-a');

describe('describe inner 1', () => {
console.log('describe inner 1');

test('test 1', () => console.log('test 1'));
});

console.log('describe outer-b');

test('test 2', () => console.log('test 2'));

describe('describe inner 2', () => {
console.log('describe inner 2');

test('test 3', () => console.log('test 3'));
});

console.log('describe outer-c');
});

// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test 1
// test 2
// test 3

Just like the describe and test blocks Jest calls the before* and after* hooks in the order of declaration. Note that the after* hooks of the enclosing scope are called first. For example, here is how you can set up and tear down resources which depend on each other:

beforeEach(() => console.log('connection setup'));
beforeEach(() => console.log('database setup'));

afterEach(() => console.log('database teardown'));
afterEach(() => console.log('connection teardown'));

test('test 1', () => console.log('test 1'));

describe('extra', () => {
beforeEach(() => console.log('extra database setup'));
afterEach(() => console.log('extra database teardown'));

test('test 2', () => console.log('test 2'));
});

// connection setup
// database setup
// test 1
// database teardown
// connection teardown

// connection setup
// database setup
// extra database setup
// test 2
// extra database teardown
// database teardown
// connection teardown
note

If you are using jasmine2 test runner, take into account that it calls the after* hooks in the reverse order of declaration. To have identical output, the above example should be altered like this:

  beforeEach(() => console.log('connection setup'));
+ afterEach(() => console.log('connection teardown'));

beforeEach(() => console.log('database setup'));
+ afterEach(() => console.log('database teardown'));

- afterEach(() => console.log('database teardown'));
- afterEach(() => console.log('connection teardown'));

// ...

一般的なアドバイス

もしテストが失敗して、まず最初に調べるべきことの一つはそのテストが単体で実行された場合にも失敗するかどうかということです。 Jest で一度だけテストを実行するには、 test コマンドを test.only に一時的に変更します。

test.only('this will be the only test that runs', () => {
expect(true).toBe(false);
});

test('this test will not run', () => {
expect('A').toBe('A');
});

もしあなたがある程度の大きさのテストスイートを実行したときにあるテストが失敗し、そのテストを単独で実行したときには失敗しない場合は、おそらく別のテストがそのテストに干渉していると考えられます。 beforeEach で共有されたステートをクリアすることでしばしばそういった問題を修正できます。 共有されたステートが変更されたかどうか確信がもてない場合は、beforeEach を使ってデータのログを取得することもできます。