Skip to content

Instantly share code, notes, and snippets.

@pirosikick
Created January 11, 2019 08:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pirosikick/1a92de528a5e1bd36cbdc82ef642b185 to your computer and use it in GitHub Desktop.
Save pirosikick/1a92de528a5e1bd36cbdc82ef642b185 to your computer and use it in GitHub Desktop.

AVAからJestへの移行

大枠の書き方

テストケースの定義

AVAのtest('コメント', () => {/* テスト内容 */});という書き方(xUnit形式)はJestでも可能です。AVAではimport test from 'ava';という感じでtest関数をインポートしていましたが、Jestではグローバル変数として定義されているのでインポートは不要です。test関数だけでなく、Jestが提供するAPIは全てグローバル変数で定義されているので、importせずに参照できます。

// AVA
import test from 'ava';

test('テストケース名など', t => {
  t.true(true);
});

// Jest
// 全てのAPIはグローバルに定義されており、importは不要
test('テストケース名など', () => {
  expect(true).toBe(true);
});

JestではSpec形式のテスト記述が可能

JestではRSpecやmochaのようなdescribe, itを使ったSpec形式のテスト記述が可能です。また、describeの中でtest関数を使ってテストケースを定義することも可能です。

// AVA
// AVAにはxUnit形式しかない

// Jest
describe('コメント', () => {
  describe('コメント', () => {
    it('コメント', () => {/* テスト内容 */});
    
    // test関数も利用できる
    test('コメント', () => {/* テスト内容 */});
  });
});

テストの事前処理、事後処理

テストの事前処理、事後処理については、名前がちょっと違うだけの同じ挙動のAPIが用意されています。

// AVA
import test from 'ava';

// ファイル単位の事前処理、事後処理
test.before(t => { ... });
test.after(t => { ... });
// テストケース単位の事前処理、事後処理
test.beforeEach(t => { ... });
test.afterEach(t => { ... });

// Jest
// ファイル単位の事前処理、事後処理
beforeAll(() => { ... });
afterAll(() => { ... });
// テストケース単位の事前処理、事後処理
beforeEach(() => { ... });
afterEach(() => { ... });

describe内に事前処理、事後処理を書くと、そのdescribe内で有効な事前処理・事後処理を記述できます。

// AVA
// AVAではできない。
// 事前処理・事後処理を分けたければ、ファイルを分けるしかない

// Jest
// ファイル内で一度だけ実行される
beforeAll(() => { ... });
afterAll(() => { ... });
// ファイル内のテストケース単位の事前処理・事後処理
beforeEach(() => { ... });
afterEach(() => { ... });

describe('コメント', () => {
    // このdescribe内で一度だけ実行される
    beforeAll(() => { ... });
    afterAll(() => { ... });
    // このdescribe内のテストケース単位の事前処理・事後処理
    beforeEach(() => { ... });
    afterEach(() => { ... });
});

参考ドキュメント

テスト間のコンテキスト共有

AVAでは、t.context変数を介して事前処理・事後処理・テストケース間でデータを共有することができました。

// AVA
import test from 'ava';

test.before(t => {
  t.context.hoge = 'hoge';
});

test('...', t => {
  t.is(t.context.hoge, 'hoge'); // Pass
});

Jestには、AVAのt.contextのようなAPIはありませんので、letで変数を定義し共有します。

// Jest
let hoge;
beforeAll(() => {
  hoge = 'hoge';
});

test('...', () => {
  expect(hoge).toBe('hoge');
});

非同期処理のテスト

AVAとJestの非同期処理のテストの違いについて書きますが、ほぼ変わらないです。

Promiseの結果を待ちたい場合

Promiseの結果を待ちたい場合、testitに渡す関数をasyncにすれば、awaitしてくれます。

// AVA
import test from 'ava';

test('…', async t => {
  const result = await getData();
  
});

// Jest
test('…', async () => {
  const result = await getData();
  
});
it('…', async () => {
  const result = await getData();
  
});

事前処理・事後処理も同様にasync関数にしPromiseを返す処理にawaitをつければ、Promiseの完了まで待ってくれます。

// Jest
beforeEach(async () => {
  await someAsyncFunc();
});

また、Jestではexpect(…).resolves、または、expect(…).rejectsを使うことで、アサーション単位でPromiseの解決を待ってくれます。

// Jest
test('…', () => {
  // getDataAsyncが返すPromiseがresolveするのを待って比較
  expect(getDataAsync()).resolves.toEqual();
});

コールバックの実行を待ちたい場合

コールバックの実行を待ちたい場合、AVAではtest.cbを利用すると、t.endが呼び出されるまでテストケースの終了を待ちます。

// AVA
import test from 'ava';

test.cb('…', t => {
  someAsyncFunction(() => {
    
    t.end(); // テストケースが終了
  });
});

Jestでは、testitに渡す関数の引数を受け取るように記述すると、その引数が実行されるまでテストケースの終了を待ちます。

// Jest
test('…', done => {
  someAsyncFunction(() => {
    
    done(); // テストケースが終了
  });
});

Assertion Planning

Assertion Planningとは、テストケース内で何度アサーションが呼ばれるかを事前に設定し、設定した回数と実際にアサーションが呼ばれた回数に違いがあった場合にテストケースを失敗させる機能です。

なぜAssertion Planningが必要なのかについては、以下のブログが詳しいです。ざっくり言うと、JSは非同期処理が多いため、テストケースが成功したと思っても実は想定していた実行パスを通っておらずアサーションが実行されなかっただけということが起こりえます。Assersion Planningによってアサーションの実行回数を定義することで、そのような「想定していた実行パスを通過せず、アサーションが実行されなかった」という状態を検知することが可能になります。

おまえは今まで実行したassertの回数を覚えているのか?あるいは新しいアサーションユーティリティのご提案 - teppeis blog

AVAでは、t.planを使ってアサーションの回数を宣言します。

// AVA
import test from 'ava';

test('…', t => {
  t.plan(1);
  try {
    
  } catch(err) {
    // エラーがthrowされない場合でもt.planによってテストが落ちる
    t.is(err.message, '…');
  }
});

Jestでは、expect.assertionsを使います。

// Jest
test('…', () => {
  expect.assertions(1);
  try {
    
  } catch(err) {
    // エラーがthrowされない場合でも
    // expect.assertionsによってテストが落ちる
    expect(err.message).toBe('…');
  }
});

その他のJestの便利なAPI

test.each

test.eachを使うと、テーブル駆動テスト(Table Driven Testing)が可能です。

test.each([
  // [actual, expected]
  [1, 1],
  [1, 2]
])('case %#', (actual, expected) => {
  expect(actual).toBe(expected);
});

describe.eachもあります。

describe.each([
  // [actual, expected]
  [1, 1],
  [1, 2]
])((actual, expected) => {
  it('…', () => {  });
  it('…', () => {  });
  it('…', () => {  });
  
});

参考ドキュメント

アサーション

ドキュメント

だいたい同じAPIがあるアサーション

  • AVAJest
  • t.truthy(value)expect(value).toBeTruthy()
  • t.falsy(value)expect(value).toBeFalsy()
  • t.true(value)expect(value).toBe(true)
  • t.false(value)expect(value).toBe(false)
  • t.is(value, expected)expect(value).toBe(expected)
  • t.not(value, expected)expect(value).not.toBe(expected)
  • t.deepEqual(value, expected)expect(value).toEqual(expected)
  • t.notDeepEqual(value, expected)expect(value).not.toEqual(expected)
  • t.throws(fn)expect(fn).toThrows()
  • t.notThrows(fn)expect(fn).not.toThrows()
  • t.regex(contents, regex)expect(contents).toMatch(regex)
  • t.notRegex(contents, regex)expect(contents).not.toMatch(regex)

toEqualを使った曖昧な比較

Jestの曖昧な比較 - Qiita

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment