JestのTips集10選。サーバーサイドでNode.jsのJestを書いたことない人向け
対象
業務レベルでサーバーサイドでJestを書いたことはないけれど、新プロジェクトでは書くことになったみたいな方を想定して記述しています。
Jestについては中々ベストプラクティスが集まりにくいので、経験的にこう書くと「きれいに」・「早く」・「正確に」書けるよというTipsを集めてみました。もし、よろしければお読みください。
前提
- TypeScript
- Node.js
- Jest
- DBアクセスありの状態を想定しています
1. it文内では、必ず1回は、expectをつかって検証をする
JestのPRをレビューしてるとたまに見受けるのですが、expectを使ってないケースがあります。
// NG
it('userを正常に、作成できること', async() => {
await createUser({ name: 'Mike' });
});
// OK
it('pdfが正常に削除できること', async() => {
const user = await createUser({ name: 'Mike' });
expect(user.name).toBe('Mike');
});
// OK
it('pdfが正常に削除できること', async() => {
await createUser({ name: 'Mike' });
const user = findOneUser({ name: 'Mike' });
expect(user.name).toBe('Mike');
});
2. Objectの比較には、toStrictEqualを基本的に使う
詳細はかなりむずかしいのですが、脳死で、まずはtoStrictEqualを使いましょう。
厳密な比較になると覚えておけば問題ないです。
理由も交えて、より詳細に知りたい方はこちらが詳しいです。
type User = {
userId: number;
name: string;
};
// NG
expect(user).toEqual({ userId: 1, name: 'Mike' });
// OK
expect(user).toStrictEqual<User>({ userId: 1, name: 'Mike' });
3. expect.anyを使う時は、toEqualにする
createdAt, updatedAt, uuid形式のidの自動生成などはテストしにくいですが、expect.anyを使えば、簡単にかけます。その場合は、toStrictEqualで厳密な比較ができないので、toEqualにしましょう。
type User = {
userId: number;
name: string;
createdAt: Date;
};
// OK
expect(user).toEqual({ userId: 1, name: 'Mike', createdAt: expect.any(Date) });
4. プリミティブな比較には、toBeを使う。
type User = {
userId: number;
name: string;
};
// NG
expect(user.userId).toStrictEqual(1);
expect(user.name).toStrictEqual('Mike');
// OK
expect(user.userId).toBe(1);
expect(user.name).toBe('Mike');
5. 外部ライブラリをテストする時は、基本的にはspyOnを使ってmock化する
もちろんspyOn以外の書き方もたくさんあるのですが、少し理解が難しいのと、import周りで並列実行時に悪さをすることがあるので、直感的にわかりやすいspyOn
を使うのがオススメです。
例えば、以下のサンプルコードは、
fileというClassのdeleteFileFromS3というメソッドをmockします。その返り値はtrueです。といった感じでかなりわかりやすくかけると思います。
const deleteFileFromS3Mock =
jest.spyOn(file, 'deleteFileFromS3').mockResolvedValue(true);
そして、できれば、mock化しやすいようなfunctionの粒度にしちゃうとJestが書きやすいコードになります。
また、mockを使う時には、意図したmockがコールされたこと、意図してないmockがコールされてないことを確認するテストを書くことが重要です。.not.toHaveBeenCalled();``toHaveBeenCalled();
というmatcherがあるので利用しましょう。
mockは、使い終わったら、restoreして、元に戻してあげましょう。
mockをどこで定義してどのように使いまわしてるかによりますが、各it文内でmockをつくってあげて、it文の最後に元に戻すのであれば、restore()で十分です。
こちらが詳しいです。
import * as file from '/lib/file';
// some.ts
export const deletePdf = async (pdfId: number) => {
await prisma.pdf.delete({ where: { id: pdfId } });
const result = file.deleteFileFromS3(pdfId);
return result
}
// some.test.ts
it('pdfが正常に削除できること', async() => {
// fileのdeleteFileFromS3をSpyOnして、常にtrueをreturnさせる
const deleteFileFromS3Mock = jest.spyOn(file, 'deleteFileFromS3').mockResolvedValue(true);
const result = await deletePdf(pdfId);
expect(result).toBe(true);
// deletePdf内でfile.deleteFileFromS3のモックがコールされたことを確認する
expect(deleteFileFromS3Mock).toHaveBeenCalled();
// mockをrestoreする
deleteFileFromS3Mock.restore();
});
6. 網羅したい系のテストでは、describe.eachやit.eachを使う
describeやitをコピペして使うのではなく、.each
を使うときれいにかけるのでオススメです。
// some.ts
export const add = (a: number, b: number) => {
return a + b;
}
// some.test.ts
describe('test', () => {
it.each([
[1, 1, 2],
[1, 2, 3],
[2, 2, 4],
])("%i + %i = %i", (inputA, inputB, expected) => {
expect(add(inputA, inputB)).toEqual(expectedResult)
})
})
結果はこのように表示されます。
test
✓ 1 + 1 = 2
✓ 1 + 2 = 3
✓ 2 + 2 = 4
7. seedは必要なときに、必要な分を最低限つくる
DBアクセスを実際にしてる場合は、必要なタイミングで必要な分だけテスト用のseedデータをつくる。
IOを発生させればさせるほど、Jestの速度は遅くなります。
8. メールアドレスは、自社ドメインを使用すること
たまに、@hoge.jp
, @test.com
みたいなドメインを勝手につかってテストを書いてしまうケースがありますが、メールのご配信などのリスクがあるので、やめましょう。
自社、自分で獲得してるドメインのみを使いましょう。
参考:
9. PromiseのErrorのテストには、expect().rejects.toThrow()を使う。
// some.ts
export const createUser = async(email: string) => {
const user = findOneUser(email);
if(user.email) {
throw new Error('メールアドレスが重複しています。')
}
//... ユーザー作成処理
}
// some.test.ts
it('メールアドレスが重複してる場合エラーになること', () => {
await expect(
createUser({ email: '[email protected]' })
).rejects.toThrow('メールアドレスが重複しています。');
});
10. 大量レコードを作成して、境界値テストを書きたいときは、mockをつかって、DBに大量データがあるような状態にしてテストを書く
たとえば、以下の場合では、ユーザーがすでに101回以上作成されていれば、エラーが発生することのテストをしたい状況です。その場合に、愚直に101個ユーザーを実際に作成するのではなく、ユーザーをカウントしてるところをメソッド化して、それをspyOnにする。そして、そのmockはDBアクセス無しにただ101というnumberをreturnしてあげるようにすれば、Jestの速度が格別に早くなります。
// some.test.ts
it('ユーザーがすでに100人作成されてる時、エラーになること', () => {
// mockで対応する。実際に101回createUserをコールしたりしない
const findAllUsersMock = jest.spyOn(user, 'findAllUsers').mockResolvedValue(101);
await expect(
createUser({ email: '[email protected]' })
).rejects.toThrow('ユーザー作成の上限になりました。');
// モックがコールされたことを確認する
expect(findAllUsersMock).toHaveBeenCalled();
// mockをrestoreする
findAllUsersMock.restore();
});
Author And Source
この問題について(JestのTips集10選。サーバーサイドでNode.jsのJestを書いたことない人向け), 我々は、より多くの情報をここで見つけました https://zenn.dev/kanasugi/articles/b49bd464311053著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Collection and Share based on the CC protocol