Jestの公式ドキュメントの(自分的には)基本の部分を読んで勉強した内容を投稿させていただきます
私が個人的に勉強した部分を書いていくだけなので、詳細に説明しているというわけでもなく、網羅的にやったというわけでもなく、本当にただの私のブログですので、注意してください。
間違いなどありましたら、ご指摘お願いします
また、この記事中で私が試してみたコードは全て こちら にあります
Introduction
Getting Started
jestのInstall
yarn add -D jest
# または
npm i -D yarn
2つの数を追加する関数のテストを書いてみる
function sum(a, b) {
return a + b;
}
module.exports = sum;
const sum = require("./sum");
test("adds 1 + 2 to equal 3", () => {
expect(sum(1, 2)).toBe(3);
});
この状態で、yarn run jest
または、 npx jest
とする、
jestを使ったテストができました
または、以下のようにpackage.json
にnpm script
を追加して実行してもOK
{
"scripts": {
"test": "jest"
}
}
Running from command line
設定ファイルとしてconfig.jsonを使用し、実行後にネイティブOSの通知を表示しながら、my-testに一致するファイルに対してJestを実行する方法は次のとおりです。
jest my-test --notify --config=config.json
Additional Configuration
以下のコマンドで基本設定ファイルを生成できる
yarn run jest --init
Using Babel
babelを使う設定
yarn add -D babel-jest @babel/core @babel/preset-env
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
function sum(a, b) {
return a + b;
}
- module.exports = sum;
+ export default sum;
- const sum = require("./sum");
+ import sum from "./sum";
test("adds 1 + 2 to equal 3", () => {
expect(sum(1, 2)).toBe(3);
});
babelを使用して、import、exportを使っていても問題なくテストができました。
Using Matchers
Common Matchers
test("two plus two is four", () => {
// expect(2 + 2)と.toBe(4)で等しいことをテストしてくれます
expect(2 + 2).toBe(4);
});
test("object assignment", () => {
const data = { one: 1 };
data["two"] = 2;
// オブジェクトの値を確認したい場合は、toBeの代わりにtoEqualを使用する
expect(data).toEqual({ one: 1, two: 2 });
});
test("numbers is not zero", () => {
// 反対をテストすることもできます
expect(2 + 1).not.toBe(0);
});
Truthiness
テストでは、
undefined
、null
、およびfalse
を区別する必要がある場合がありますが、これらを異なる方法で扱いたくない場合があります。 Jestにはあなたが欲しいものについてあなたが明確になることを可能にするヘルパーが含まれています。
- toBeNull ->
null
にのみ一致 - toBeUndefined ->
undefined
- toBeDefined ->
toBeUndefined
の反対 - toBeTruthy -> ifで
true
と見なすものすべてに一致する - toBeFalsy -> ifで
false
と見なすものすべてに一致する
test("null", () => {
const n = null;
expect(n).toBeNull();
expect(n).toBeDefined();
expect(n).not.toBeUndefined();
expect(n).not.toBeTruthy();
expect(n).toBeFalsy();
});
test("zero", () => {
const z = 0;
expect(z).not.toBeNull();
expect(z).toBeDefined();
expect(z).not.toBeUndefined();
expect(z).not.toBeTruthy();
expect(z).toBeFalsy();
});
Numbers
test("two plus two", () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);
// 等しいか確認
expect(value).toBe(4);
expect(value).toEqual(4);
});
test("adding floating point numbers", () => {
const value = 0.1 + 0.2;
//expect(value).toBe(0.3); <- 丸め誤差が原因でうまくテストできない
expect(value).toBeCloseTo(0.3); // This works.
});
Strings
// toMatchを使って文字列を正規表現と照合することができる
test("there is no I in team", () => {
expect("team").not.toMatch(/I/);
});
test('but there is a "stop" in Christoph', () => {
expect("Christoph").toMatch(/stop/);
});
Arrays and iterables
// toContainを使用して、配列または反復可能オブジェクトに特定の項目が含まれているかどうかを確認できる
const shoppingList = [
"diapers",
"kleenex",
"trash bags",
"paper towels",
"beer"
];
test("the shopping list has beer on it", () => {
expect(shoppingList).toContain("beer");
expect(new Set(shoppingList)).toContain("beer");
});
Exceptions
// 特定の関数が呼び出されたときにエラーをスローすることをテストしたい場合は、toThrowを使用する
function compileAndroidCode() {
throw new Error("you are using the wrong JDK");
}
test("compiling android goes as expected", () => {
expect(compileAndroidCode).toThrow();
expect(compileAndroidCode).toThrow(Error);
// 正確なエラーメッセージや正規表現を使うこともできます
expect(compileAndroidCode).toThrow("you are using the wrong JDK");
expect(compileAndroidCode).toThrow(/JDK/);
});
Testing Asynchronous Code
Callbacks
function fetchData(callback) {
// 非同期処理を実行 -> しかし1秒待ってくれずにテストが終了してしまうので、このテストはうまくいかない
setTimeout(() => callback("peanut butter"), 1000);
}
test("the data is peanut butter", () => {
function callback(data) {
// コールバック関数の引数の文字列が「peanut butter」かテスト
expect(data).toBe("peanut butter");
}
fetchData(callback);
});
このテストを想定通りに動かすには、テストを空の引数を持つ関数に入れる代わりに、done
という単一の引数を使用する。テストを終了する前に、Jestはdone
コールバックが呼び出されるまで待機します
function fetchData(callback) {
setTimeout(() => callback("peanut butter"), 1000);
}
test("the data is peanut butter", done => {
function callback(data) {
expect(data).toBe("peanut butter");
done();
}
fetchData(callback);
});
Promises
function fetchData() {
return new Promise(resolve => {
setTimeout(() => resolve("peanut butter"), 1000);
});
}
function fetchDataErr() {
return new Promise((resolve, reject) => {
setTimeout(() => reject("error"), 1000);
});
}
test("the data is peanut butter", () => {
// 必ずreturnしましょう、でないと、promiseの解決を待たずにテストが終了してしまう
return fetchData().then(data => {
expect(data).toBe("peanut butter");
});
});
test("the fetch fails with an error", () => {
// アサーションが1回行われることを確認しないと、間違ってresolveが実行されてしまった場合に
// catchに処理が到達せず、テストになりません
expect.assertions(1);
// 必ずreturnしましょう、でないと、promiseの解決を待たずにテストが終了してしまう
return fetchDataErr().catch(e => expect(e).toMatch("error"));
});
test("the data is peanut butter", () => {
// resolvesマッチャーを使うことで、同じようにテストしてくれます
return expect(fetchData()).resolves.toBe("peanut butter");
});
test("the fetch fails with an error", () => {
// rejectsマッチャーを使うことで、同じようにテストしてくれます
return expect(fetchDataErr()).rejects.toMatch("error");
});
Async/Await
// async, awaitを使って、同じようにテストすることもできます
test("the data is peanut butter", async () => {
expect.assertions(1);
const data = await fetchData();
expect(data).toBe("peanut butter");
});
test("the fetch fails with an error", async () => {
expect.assertions(1);
try {
await fetchDataErr();
} catch (e) {
expect(e).toMatch("error");
}
});
// resolves, rejectsマッチャーでも同じようにテストできます
test("the data is peanut butter", async () => {
await expect(fetchData()).resolves.toBe("peanut butter");
});
test("the fetch fails with an error", async () => {
await expect(fetchDataErr()).rejects.toMatch("error");
});
Setup and Teardown
テストを実行する前、テストの実行後に実行してくれる関数
// 各テストの実行前に実行される関数
beforeEach(() => {
console.log("beforeEach");
});
// 各テストの実行後に実行される関数
afterEach(() => {
console.log("afterEach");
});
// ファイルの先頭で一回だけ実行される関数
beforeAll(() => {
console.log("beforeAll");
});
// ファイルの最後で一回だけ実行される関数
afterAll(() => {
console.log("afterAll");
});
test("city database has Vienna", () => {
expect(true).toBeTruthy();
});
test("city database has San Juan", () => {
expect(true).toBeTruthy();
});
// コンソールの出力↓
// beforeAll
// beforeEach
// afterEach
// beforeEach
// afterEach
// afterAll
// ファイルの先頭で一回だけ実行される関数
beforeAll(() => {
// promiseが返る処理の場合はreturnしないといけない
return new Promise(resolve => {
setTimeout(() => {
console.log("beforeAll");
resolve();
}, 1000);
});
});
// ファイルの最後で一回だけ実行される関数
afterAll(() => {
// promiseが返る処理の場合はreturnしないといけない
return new Promise(resolve => {
setTimeout(() => {
console.log("afterAll");
resolve();
}, 1000);
});
});
Scoping
describeブロックを使用してテストをまとめてグループ化することもできます。記述ブロック内にある場合、前後のブロックはその記述ブロック内のテストにのみ適用されます。
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
Mock Functions
Using a mock function
function forEach(items, callback) {
for (let index = 0; index < items.length; index++) {
callback(items[index]);
}
}
test("using a mock function sample", () => {
const mockCallback = jest.fn(x => 42 + x);
forEach([0, 1], mockCallback);
// モック関数が2回呼び出されたか
expect(mockCallback.mock.calls.length).toBe(2);
// 関数の最初の呼び出しの最初の引数が0か
expect(mockCallback.mock.calls[0][0]).toBe(0);
// 関数への2番目の呼び出しの最初の引数は1か
expect(mockCallback.mock.calls[1][0]).toBe(1);
// 関数への最初の呼び出しの戻り値は42か
expect(mockCallback.mock.results[0].value).toBe(42);
});
Mock Return Values
テスト中にモック関数を使ってコードにテスト値を挿入することもできます。
test("mock return values sample", () => {
const myMock = jest.fn();
console.log(myMock()); // undefined
myMock
.mockReturnValueOnce(10)
.mockReturnValueOnce("x")
.mockReturnValue(true);
console.log(myMock(), myMock(), myMock(), myMock()); // 10 'x' true true
});
Mocking Modules
import axios from "axios";
class Users {
static all() {
return axios.get("/users.json").then(resp => resp.data);
}
}
export default Users;
↑のaxios.get
をモックします
.get
に対してmockResolvedValueを指定して、データを返すようにする↓
import axios from "axios";
import Users from "./users";
jest.mock("axios");
test("should fetch users", () => {
const users = [{ name: "Bob" }];
const resp = { data: users };
axios.get.mockResolvedValue(resp);
return Users.all().then(data => expect(data).toEqual(users));
});
Mock Implementations
戻り値を指定してモック関数の実装を完全に置き換える機能
test("mock implementations sample 1", () => {
const myMockFn = jest.fn(cb => cb(null, true));
myMockFn((err, val) => console.log(val));
});
mockImplementationメソッドは、他のモジュールから作成されたモック関数のデフォルト実装を定義する必要がある場合に便利です。
module.exports = function() {
// some implementation;
};
test("mock implementations sample 2", () => {
jest.mock("./foo");
const foo = require("./foo");
foo.mockImplementation(() => 42);
console.log(foo()); // 42
});
複数呼び出しで異なる結果とする場合
test("mock implementations sample 3", () => {
const myMockFn = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));
myMockFn((err, val) => console.log(val)); // true
myMockFn((err, val) => console.log(val)); // false
});
mockImplementationOnce
で定義された実装を使い果たした後は、デフォルトが実行される
通常チェーン化されたメソッドがある(したがって常にこれを返す必要がある)場合は、すべてのモックにも存在する
.mockReturnThis()
関数の形でこれを単純化するためのおすすめのAPIがあります。
const myObj = {
myMethod: jest.fn().mockReturnThis(),
};
// ↑と同じ意味
const otherObj = {
myMethod: jest.fn(function() {
return this;
}),
};
↓this
が返ってきていることが確認できました
test("mock implementations sample 5", () => {
const myObj = {
myMethod: jest.fn().mockReturnThis()
};
console.log(myObj.myMethod() === myObj); // true
});
Mock Names
テストでの出力時に表示されるモック関数名を指定でき、エラーが見やすくなる
const myMockFn = jest
.fn()
.mockReturnValue("default")
.mockImplementation(scalar => 42 + scalar)
.mockName("add42");
Custom Matchers
test("custom matchers sample 1", () => {
const mockFunc = jest.fn();
mockFunc();
expect(mockFunc).toBeCalled(); // モック関数が少なくとも一度呼び出されました
let [arg1, arg2] = [1, 2];
mockFunc(arg1, arg2);
expect(mockFunc).toBeCalledWith(arg1, arg2); // 指定された引数を使用して、モック関数が少なくとも1回呼び出されました
expect(mockFunc).lastCalledWith(arg1, arg2); // モック関数への最後の呼び出しは指定された引数で呼ばれました
expect(mockFunc).toMatchSnapshot(); // すべての呼び出しとモックの名前はスナップショットとして書き込まれます
});
以下のように書いても↑のようにテストしてくれます
test("custom matchers sample 2", () => {
const mockFunc = jest.fn().mockName("sampleFunc");
mockFunc();
expect(mockFunc.mock.calls.length).toBeGreaterThan(0); // モック関数が少なくとも一度呼び出されました
let [arg1, arg2] = [1, 2];
mockFunc(arg1, arg2);
expect(mockFunc.mock.calls).toContainEqual([arg1, arg2]); // 指定された引数を使用して、モック関数が少なくとも1回呼び出されました
// モック関数への最後の呼び出しは指定された引数で呼ばれました
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([
arg1,
arg2
]);
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(1); // モック関数への最後の呼び出しの最初の引数は `1`でした
expect(mockFunc.getMockName()).toBe("sampleFunc"); // モックネーム
});
Snapshot Testing
スナップショットテストは、UIが予期せずに変更されないようにしたい場合に非常に便利なツールです。
Snapshot Testing with Jest
- Reactコンポーネントをテストする例
import React from "react";
const STATUS = {
HOVERED: "hovered",
NORMAL: "normal"
};
export default class Link extends React.Component {
constructor() {
super();
this.state = {
class: STATUS.NORMAL
};
this._onMouseEnter = this._onMouseEnter.bind(this);
this._onMouseLeave = this._onMouseLeave.bind(this);
}
_onMouseEnter() {
this.setState({ class: STATUS.HOVERED });
}
_onMouseLeave() {
this.setState({ class: STATUS.NORMAL });
}
render() {
return (
<a
className={this.state.class}
href={this.props.page || "#"}
onMouseEnter={this._onMouseEnter}
onMouseLeave={this._onMouseLeave}
>
{this.props.children}
</a>
);
}
}
import React from "react";
import Link from "./Link.react";
import renderer from "react-test-renderer";
it("renders correctly", () => {
const tree = renderer
.create(<Link page="http://www.facebook.com">Facebook</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});
yarn add -D react react-test-renderer @babel/preset-react
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
yarn run jest
↑を実行すると、./__snapshots__/Link.test.js.snap
が自動生成され、以下のようなファイルになっています
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;
スナップショットが一致しない場合、テストが失敗し、バグが発見しやすくなります。
Updating Snapshots
意図的にテストの内容を変更した場合
import React from "react";
import Link from "./Link.react";
import renderer from "react-test-renderer";
it("renders correctly", () => {
const tree = renderer
- .create(<Link page="http://www.facebook.com">Facebook</Link>)
+ .create(<Link page="http://www.instagram.com">Instagram</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});
スナップショットが一致しなくなったためテストが通らなくなりました
変更が意図的なものであった場合はスナップショットを更新したらOK
yarn run jest --updateSnapshot #または、-u
Interactive Snapshot Mode
失敗したスナップショットは、監視モードでインタラクティブに更新することもできます。
yarn run jest --watch
u
を押すと、失敗したスナップショットを更新してくれるようでした
Inline Snapshots
スナップショット値が自動的にソースコードに書き戻されることを除けば、インラインスナップショットは外部スナップショット(.snapファイル)と同じように動作します。 つまり、正しい値が書き込まれたことを確認するために外部ファイルに切り替える必要なしに、自動的に生成されたスナップショットの利点を享受することができます。
yarn add prettier
- 引数を付けずに
.toMatchInlineSnapshot()
を呼び出してテストを作成
it("renders correctly by prettier", () => {
const tree = renderer
.create(<Link page="https://prettier.io">Prettier</Link>)
.toJSON();
expect(tree).toMatchInlineSnapshot();
});
- ↑の状態で、
yarn run jest
を実行すると、
スナップショットのファイルを作るのではなく、インラインで展開してくれたのを確認できました。
An Async Example
JestでBabelサポートを有効にする必要があります -> https://jestjs.io/docs/en/getting-started#using-babel
import request from "./request";
export function getUserName(userID) {
return request("/users/" + userID).then(user => user.name);
}
const http = require("http");
export default function request(url) {
return new Promise(resolve => {
// このモジュールを__mocks__/request.jsでモックします
http.get({ path: url }, response => {
let data = "";
response.on("data", _data => (data += _data));
response.on("end", () => resolve(data));
});
});
}
↑request.js
をモックします
__mocks__
フォルダにモックのファイルを置く
const users = {
4: { name: "Mark" },
5: { name: "Paul" }
};
export default function request(url) {
return new Promise((resolve, reject) => {
const userID = parseInt(url.substr("/users/".length), 10);
process.nextTick(() =>
users[userID]
? resolve(users[userID])
: reject({
error: "User with " + userID + " not found."
})
);
});
}
jest.mock("../request");
import * as user from "../user";
// promiseが返る場合はreturnする必要がある
it("works with promises", () => {
expect.assertions(1);
return user.getUserName(4).then(data => expect(data).toEqual("Mark"));
});
__mocks__/request.js
を使用して、テストがうまく実行されたことを確認できました
.resolves
.resolves
マッチャーを使った例
it("works with resolves", () => {
expect.assertions(1);
return expect(user.getUserName(5)).resolves.toEqual("Paul");
});
async/await
を使った例
it("works with async/await", async () => {
expect.assertions(1);
const data = await user.getUserName(4);
expect(data).toEqual("Mark");
});
it("works with async/await and resolves", async () => {
expect.assertions(1);
await expect(user.getUserName(5)).resolves.toEqual("Paul");
});
Error handling
test("tests error with promises", () => {
expect.assertions(1);
return user.getUserName(2).catch(e =>
expect(e).toEqual({
error: "User with 2 not found."
})
);
});
it("tests error with async/await", async () => {
expect.assertions(1);
try {
await user.getUserName(1);
} catch (e) {
expect(e).toEqual({
error: "User with 1 not found."
});
}
});
.rejects
it("tests error with rejects", () => {
expect.assertions(1);
return expect(user.getUserName(3)).rejects.toEqual({
error: "User with 3 not found."
});
});
it("tests error with async/await and rejects", async () => {
expect.assertions(1);
await expect(user.getUserName(3)).rejects.toEqual({
error: "User with 3 not found."
});
});
Timer Mocks
setTimeout、setIntervalの時間の経過をコントロールしてテストすることが可能
"use strict";
function timerGame(callback) {
console.log("Ready....go!");
setTimeout(() => {
console.log("Times up -- stop!");
callback && callback();
}, 1000);
}
module.exports = timerGame;
"use strict";
jest.useFakeTimers();
test("waits 1 second before ending the game", () => {
const timerGame = require("../timerGame");
timerGame();
// setTimeoutが期待通りに実行されているかテスト
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
});
ここではjest.useFakeTimers();を呼び出して偽のタイマーを有効にします。 これはsetTimeoutと他のタイマー関数をモック関数で模擬します。 1つのファイルまたはディスクリプションブロック内で複数のテストを実行する場合は、jest.useFakeTimers(); 各テストの前に手動で、またはbeforeEachなどのセットアップ機能を使用して呼び出すことができます。 そうしないと、内部使用状況カウンターがリセットされません。
Run All Timers
timerGame.js
は↑と同じ
test("calls the callback after 1 second", () => {
const timerGame = require("../timerGame");
const callback = jest.fn();
timerGame(callback);
// この時点では、コールバックはまだ呼び出されていない
expect(callback).not.toBeCalled();
// すべてのタイマーが実行されるまで早送り
jest.runAllTimers();
// callbackが呼び出されている
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
Run Pending Timers
再帰的にタイマー処理をする場合
"use strict";
function infiniteTimerGame(callback) {
console.log("Ready....go!");
setTimeout(() => {
console.log("Times up! 10 seconds before the next game starts...");
callback && callback();
setTimeout(() => {
infiniteTimerGame(callback);
}, 10000);
}, 1000);
}
module.exports = infiniteTimerGame;
"use strict";
jest.useFakeTimers();
describe("infiniteTimerGame", () => {
test("schedules a 10-second timer after 1 second", () => {
const infiniteTimerGame = require("../infiniteTimerGame");
const callback = jest.fn();
infiniteTimerGame(callback);
// モック関数が呼び出されたことを確認
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
// 現在保留中のタイマーだけを早送りして使い尽くす
jest.runOnlyPendingTimers();
// この時点で、1秒タイマーがコールバックを起動している
expect(callback).toBeCalled();
// 新しいタイマーが作成されている
expect(setTimeout).toHaveBeenCalledTimes(2);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
});
});
Advance Timers by Time
it("calls the callback after 1 second via advanceTimersByTime", () => {
const timerGame = require("../timerGame");
const callback = jest.fn();
timerGame(callback);
expect(callback).not.toBeCalled();
// タイマーを1秒進める
jest.advanceTimersByTime(1000);
// コールバックが呼び出されている確認
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
Manual Mocks
Mocking user modules
user.js
というファイルを作成し、それを__mocks__
ディレクトリに配置すると、モックとして定義できます
※テストでそのモジュールが必要な場合は、明示的にjest.mock('./ moduleName')
を呼び出す
ES6 Class Mocks
Jestを使用して、テストしたいファイルにインポートされているES6クラスをモックすることができます
An ES6 Class Example
export default class SoundPlayer {
constructor() {
this.foo = "bar";
}
playSoundFile(fileName) {
console.log("Playing sound file " + fileName);
}
}
import SoundPlayer from "./sound-player";
export default class SoundPlayerConsumer {
constructor() {
this.soundPlayer = new SoundPlayer();
}
playSomethingCool() {
const coolSoundFileName = "song.mp3";
this.soundPlayer.playSoundFile(coolSoundFileName);
}
}
↑をモックにしていきます
ES6クラスのモックを作成する4つの方法
1. Automatic mock
import SoundPlayer from "./sound-player";
import SoundPlayerConsumer from "./sound-player-consumer";
jest.mock("./sound-player"); // モック作成
beforeEach(() => {
// すべてのインスタンスをクリア
SoundPlayer.mockClear();
});
it("We can check if the consumer called the class constructor", () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it("We can check if the consumer called a method on the class instance", () => {
// mockClear()が機能しているため、まだ関数が実行されていないことを確認できる
expect(SoundPlayer).not.toHaveBeenCalled();
const soundPlayerConsumer = new SoundPlayerConsumer();
// constructor が再度呼び出されている
expect(SoundPlayer).toHaveBeenCalledTimes(1);
const coolSoundFileName = "song.mp3";
soundPlayerConsumer.playSomethingCool();
const mockSoundPlayerInstance = SoundPlayer.mock.instances[0];
const mockPlaySoundFile = mockSoundPlayerInstance.playSoundFile;
expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName);
// ↑と同じ内容
expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
expect(mockPlaySoundFile).toHaveBeenCalledTimes(1);
});
2. Manual mock
モックの実装を__mocks__フォルダーに保存して手動モックを作成する
export const mockPlaySoundFile = jest.fn();
const mock = jest.fn().mockImplementation(() => {
return { playSoundFile: mockPlaySoundFile };
});
export default mock;
import SoundPlayer, { mockPlaySoundFile } from "./sound-player";
import SoundPlayerConsumer from "./sound-player-consumer";
jest.mock("./sound-player"); // モック作成
beforeEach(() => {
// すべてのインスタンスをクリア
SoundPlayer.mockClear();
mockPlaySoundFile.mockClear();
});
it("We can check if the consumer called the class constructor", () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it("We can check if the consumer called a method on the class instance", () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
const coolSoundFileName = "song.mp3";
soundPlayerConsumer.playSomethingCool();
expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
});
3. Calling jest.mock()
with the module factory parameter
モジュールファクトリパラメータを使って
jest.mock()
を呼び出す
import SoundPlayer from "./sound-player";
import SoundPlayerConsumer from "./sound-player-consumer";
const mockPlaySoundFile = jest.fn();
jest.mock("./sound-player", () => {
return jest.fn().mockImplementation(() => {
return { playSoundFile: mockPlaySoundFile };
});
});
it("We can check if the consumer called a method on the class instance", () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
const coolSoundFileName = "song.mp3";
soundPlayerConsumer.playSomethingCool();
expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
});
4. Replacing the mock using mockImplementation()
or mockImplementationOnce()
既存のモックで
mockImplementation()
を呼び出すことによって、単一のテストまたはすべてのテストの実装を変更するために、上記のモックをすべて置き換えることができます。
import SoundPlayer from "./sound-player";
import SoundPlayerConsumer from "./sound-player-consumer";
jest.mock("./sound-player");
describe("When SoundPlayer throws an error", () => {
beforeAll(() => {
SoundPlayer.mockImplementation(() => {
return {
playSoundFile: () => {
throw new Error("Test error");
}
};
});
});
it("Should throw an error when calling playSomethingCool", () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(() => soundPlayerConsumer.playSomethingCool()).toThrow();
});
});
Globals
テストファイルで、Jestはこれらの各メソッドとオブジェクトをグローバル環境に配置します。使用するために何かを要求したりインポートしたりする必要はありません。
- afterAll(fn, timeout)
- afterEach(fn, timeout)
- beforeAll(fn, timeout)
- beforeEach(fn, timeout)
- describe(name, fn)
- describe.each(table)(name, fn, timeout)
- describe.only(name, fn)
- describe.only.each(table)(name, fn)
- describe.skip(name, fn)
- describe.skip.each(table)(name, fn)
- test(name, fn, timeout)
- test.each(table)(name, fn, timeout)
- test.only(name, fn, timeout)
- test.only.each(table)(name, fn)
- test.skip(name, fn)
- test.skip.each(table)(name, fn)
- test.todo(name)
afterAll(fn, timeout)
このファイル内のすべてのテストが完了した後に実行される関数
afterAll(() => {
console.log("afterAll");
});
test("test 1", () => {
console.log("test 1");
});
test("test 2", () => {
console.log("test 2");
});
// 出力
// test 1
// test 2
// afterAll
afterEach(fn, timeout)
このファイルの各テストが完了した後に実行される関数
afterEach(() => {
console.log("afterEach");
});
test("test 1", () => {
console.log("test 1");
});
test("test 2", () => {
console.log("test 2");
});
// 出力
// test 1
// afterEach
// test 2
// afterEach
beforeAll(fn, timeout)
このファイルのテストが実行される前に実行する関数
beforeAll(() => {
console.log("beforeAll");
});
test("test 1", () => {
console.log("test 1");
});
test("test 2", () => {
console.log("test 2");
});
// 出力
// beforeAll
// test 1
// test 2
beforeEach(fn, timeout)
このファイルの各テストが実行前に実行される関数
beforeEach(() => {
console.log("beforeEach");
});
test("test 1", () => {
console.log("test 1");
});
test("test 2", () => {
console.log("test 2");
});
// 出力
// beforeEach
// test 1
// beforeEach
// test 2
describe(name, fn)
いくつかの関連テストをまとめたブロックを作成する
const myBeverage = {
delicious: true,
sour: false
};
describe("my beverage", () => {
test("is delicious", () => {
expect(myBeverage.delicious).toBeTruthy();
});
test("is not sour", () => {
expect(myBeverage.sour).toBeFalsy();
});
});
describe.each(table)(name, fn, timeout)
データセットを渡すことで、複数テストを行える。
例えば足し算するだけのテストを、値だけ変えてテストしてくれることができる
describe.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
".add(%i, %i)",
(a, b, expected) => {
test(`returns ${expected}`, () => {
expect(a + b).toBe(expected);
});
test(`returned value not be greater than ${expected}`, () => {
expect(a + b).not.toBeGreaterThan(expected);
});
test(`returned value not be less than ${expected}`, () => {
expect(a + b).not.toBeLessThan(expected);
});
}
);
describe.only(name, fn)
describeブロックを1つだけ実行したい場合は、describe.onlyを使用する
describe.only("my beverage", () => {
test("is delicious", () => {
expect(true).toBeTruthy();
});
test("is not sour", () => {
expect(false).toBeFalsy();
});
});
describe("my other beverage", () => {
// ... will be skipped
});
describe.only.each(table)(name, fn)
データセットを渡すことで、複数テストを行い、かつdescribeブロックを1つだけ実行したい場合に使う
describe.only.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
".add(%i, %i)",
(a, b, expected) => {
test(`returns ${expected}`, () => {
expect(a + b).toBe(expected);
});
}
);
test("will not be ran", () => {
expect(1 / 0).toBe(Infinity);
});
describe.skip(name, fn)
特定のブロックをスキップしたい場合に使う
describe("my beverage", () => {
test("is delicious", () => {
expect(true).toBeTruthy();
});
test("is not sour", () => {
expect(false).toBeFalsy();
});
});
describe.skip("my other beverage", () => {
// ... will be skipped
});
describe.skip.each(table)(name, fn)
一連のデータセットのテストをスキップしたい場合に使う
describe.skip.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
".add(%i, %i)",
(a, b, expected) => {
test(`returns ${expected}`, () => {
expect(a + b).toBe(expected); // will not be ran
});
}
);
test("will be ran", () => {
expect(1 / 0).toBe(Infinity);
});
test(name, fn, timeout)
テストを実行する
test("test", () => {
expect(1 - 1).toBe(0);
});
// エイリアスのitを使ってもOK
it("it", () => {
expect(1 - 1).toBe(0);
});
test.each(table)(name, fn, timeout)
データセットを渡すことで、複数テストを行える。
test.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
".add(%i, %i)",
(a, b, expected) => {
expect(a + b).toBe(expected);
}
);
test.only(name, fn, timeout)
テストを1つだけ実行したい場合に使う
test.only("it is raining", () => {
expect(1).toBeGreaterThan(0);
});
test("it is not snowing", () => {
expect(0).toBe(0);
});
test.only.each(table)(name, fn)
データセットを渡すことで、複数テストを行い、テストを1つだけ実行したい場合に使う
test.only.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
".add(%i, %i)",
(a, b, expected) => {
expect(a + b).toBe(expected);
}
);
test("will not be ran", () => {
expect(1 / 0).toBe(Infinity);
});
test.skip(name, fn)
テストをスキップしたいときに使う
test("it is raining", () => {
expect(1).toBeGreaterThan(0);
});
test.skip("it is not snowing", () => {
expect(1).toBe(0);
});
test.skip.each(table)(name, fn)
一連のデータセットのテストをスキップしたい場合に使う
test.skip.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
".add(%i, %i)",
(a, b, expected) => {
expect(a + b).toBe(expected); // will not be ran
}
);
test("will be ran", () => {
expect(1 / 0).toBe(Infinity);
});
test.todo(name)
テストを書く予定がある場合は、test.toを使用してください。 これらのテストは最後のサマリー出力で強調表示されるので、まだいくつのテストが必要であるかがわかります。
test.todo("add should be associative");
Expect
expect(value)
値をテストするたびに、expect関数を使用する。値について何かをアサートするためにマッチャー関数と共にexpectを使用する。
it("test", () => {
expect(1 + 2).toBe(3);
});
expect.extend(matchers)
マッチャーを自作できる。toBeWithinRange
を自作した例↓
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
if (pass) {
return {
message: () =>
`expected ${received} not to be within range ${floor} - ${ceiling}`,
pass: true
};
} else {
return {
message: () =>
`expected ${received} to be within range ${floor} - ${ceiling}`,
pass: false
};
}
}
});
test("numeric ranges", () => {
expect(100).toBeWithinRange(90, 110);
expect(101).not.toBeWithinRange(0, 100);
expect({ apples: 6, bananas: 3 }).toEqual({
apples: expect.toBeWithinRange(1, 10),
bananas: expect.not.toBeWithinRange(11, 20)
});
});
expect.anything()
nullまたは未定義以外のものにマッチする。
toEqual
またはtoBeCalledWith
でリテラル値の代わりに使うことができる
モック関数がnull以外の引数で呼び出されることを確認したい場合は、次のようにする
test("map calls its argument with a non-null argument", () => {
const mock = jest.fn();
[1].map(x => mock(x));
expect(mock).toBeCalledWith(expect.anything());
});
expect.any(constructor)
toEqual
またはtoBeCalledWith
でリテラル値の代わりに使うことができる
モック関数が番号付きで呼び出されていることを確認したい場合は、次のようにする
function randocall(fn) {
return fn(Math.floor(Math.random() * 6 + 1));
}
test("randocall calls its callback with a number", () => {
const mock = jest.fn();
randocall(mock);
expect(mock).toBeCalledWith(expect.any(Number));
});
expect.arrayContaining(array)
配列内の全ての要素を含む配列と一致する
describe("Beware of a misunderstanding! A sequence of dice rolls", () => {
const expected = [1, 2, 3, 4, 5, 6];
it("matches even with an unexpected number 7", () => {
expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(
expect.arrayContaining(expected) // 1, 2, 3, 4, 5, 6が含まれているので一致
);
});
it("does not match without an expected number 2", () => {
expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual(
expect.arrayContaining(expected) // 2が含まれていないので一致しない
);
});
});
expect.assertions(number)
テスト中に一定数のアサーションが呼び出されることを確認する
test("assertions sample", () => {
expect.assertions(2);
expect(true).toBeTruthy();
expect(false).toBeFalsy();
});
expect.hasAssertions()
テスト中に少なくとも1つのアサーションが呼び出されることを確認する
test("hasAssertions sample", () => {
expect.hasAssertions();
expect(true).toBeTruthy();
expect(false).toBeFalsy();
});
expect.not.arrayContaining(array)
配列内の値が一致していないことを確認する
test("not arrayContaining sample", () => {
expect(["Alice", "Bob", "Eve"]).toEqual(
expect.not.arrayContaining(["Samantha"])
);
});
expect.not.objectContaining(object)
期待されるプロパティに再帰的にマッチしないかを確認する
test("not objectContaining sample", () => {
expect({ bar: "baz" }).toEqual(expect.not.objectContaining({ foo: "bar" }));
});
expect.not.stringContaining(string)
文字列を一致しないか確認する
test("not stringContaining sample", () => {
expect("How are you?").toEqual(expect.not.stringContaining("Hello world!"));
});
expect.not.stringMatching(string | regexp)
受け取った値が文字列ではない、または期待される文字列または正規表現と一致しないか確認する
test("not stringMatching sample", () => {
expect("How are you?").toEqual(expect.not.stringMatching(/Hello world!/));
});
expect.objectContaining(object)
期待されるプロパティに再帰的にマッチするかを確認する
test("objectContaining sample", () => {
expect({ bar: "baz", foo: "foo" }).toEqual(
expect.objectContaining({ bar: "baz" })
);
});
expect.stringContaining(string)
文字列を含む文字列であるか確認する
test("stringContaining sample", () => {
expect("How are you?").toEqual(expect.stringContaining("you"));
});
expect.stringMatching(string | regexp)
文字列または正規表現に一致する文字列かを確認する
test("stringMatching sample", () => {
expect("Roberto").toEqual(expect.stringMatching(/^[BR]ob/));
});
.not
反対をテストする
test("not sample", () => {
expect("abc").not.toBe("def");
});
.resolves
Promiseのresolveを確認する場合
test("resolves to lemon", () => {
return expect(Promise.resolve("lemon")).resolves.toBe("lemon");
});
test("resolves to lemon", async () => {
await expect(Promise.resolve("lemon")).resolves.toBe("lemon");
await expect(Promise.resolve("lemon")).resolves.not.toBe("octopus");
});
.rejects
Promiseのrejectを確認する場合
test("rejects to octopus", () => {
return expect(Promise.reject(new Error("octopus"))).rejects.toThrow(
"octopus"
);
});
test("rejects to octopus", async () => {
await expect(Promise.reject(new Error("octopus"))).rejects.toThrow("octopus");
});
.toBe(value)
プリミティブ値などを確認する
const can = {
name: "pamplemousse",
ounces: 12
};
describe("the can", () => {
test("has 12 ounces", () => {
expect(can.ounces).toBe(12);
});
test("has a sophisticated name", () => {
expect(can.name).toBe("pamplemousse");
});
});
.toHaveBeenCalled()
モック関数が呼び出されたことを確認
function drinkAll(callback, flavour) {
if (flavour !== "octopus") {
callback(flavour);
}
}
describe("drinkAll", () => {
test("drinks something lemon-flavoured", () => {
const drink = jest.fn();
drinkAll(drink, "lemon");
expect(drink).toHaveBeenCalled();
});
test("does not drink something octopus-flavoured", () => {
const drink = jest.fn();
drinkAll(drink, "octopus");
expect(drink).not.toHaveBeenCalled();
});
});
.toHaveBeenCalledTimes(number)
モック関数が正確な数呼び出されたことを確認
test("toHaveBeenCalledTimes sample", () => {
const drink = jest.fn();
["lemon", "octopus"].map(drink);
expect(drink).toHaveBeenCalledTimes(2);
});
.toHaveBeenCalledWith(arg1, arg2, ...)
モック関数が特定の引数で呼び出されたことを確認
test("toHaveBeenCalledWith sample", () => {
const drink = jest.fn();
["lemon", "octopus"].map(drink);
expect(drink).toHaveBeenCalledWith("lemon", 0, ["lemon", "octopus"]);
});
.toHaveBeenLastCalledWith(arg1, arg2, ...)
モック関数が最後に呼び出された時の引数を確認
test("toHaveBeenLastCalledWith sample", () => {
const drink = jest.fn();
["lemon", "octopus"].map(drink);
expect(drink).toHaveBeenLastCalledWith("octopus", 1, ["lemon", "octopus"]);
});
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
モック関数のn番目に呼び出された時の引数を確認
test("toHaveBeenNthCalledWith sample", () => {
const drink = jest.fn();
["lemon", "octopus"].map(drink);
expect(drink).toHaveBeenNthCalledWith(1, "lemon", 0, ["lemon", "octopus"]);
expect(drink).toHaveBeenNthCalledWith(2, "octopus", 1, ["lemon", "octopus"]);
});
.toHaveReturned()
モック関数が少なくとも1回正常に戻った(つまり、エラーをスローしなかった)ことを確認
test("toHaveReturned sample", () => {
const drink = jest.fn(() => true);
drink();
expect(drink).toHaveReturned();
});
.toHaveReturnedTimes(number)
モック関数が正常に戻った正確な回数を確認
test("toHaveReturnedTimes sample", () => {
const drink = jest.fn(() => true);
drink();
drink();
expect(drink).toHaveReturnedTimes(2);
});
.toHaveReturnedWith(value)
モック関数が特定の値を返すか確認
test("toHaveReturnedWith sample", () => {
const beverage = { name: "La Croix" };
const drink = jest.fn(beverage => beverage.name);
drink(beverage);
expect(drink).toHaveReturnedWith("La Croix");
});
.toHaveLastReturnedWith(value)
モック関数が最後に返した値が特定の値か確認
test("toHaveLastReturnedWith sample", () => {
const beverage1 = { name: "La Croix (Lemon)" };
const beverage2 = { name: "La Croix (Orange)" };
const drink = jest.fn(beverage => beverage.name);
drink(beverage1);
drink(beverage2);
expect(drink).toHaveLastReturnedWith("La Croix (Orange)");
});
.toHaveNthReturnedWith(nthCall, value)
モック関数がn番目に返した値が特定の値か確認
test("toHaveNthReturnedWith sample", () => {
const beverage1 = { name: "La Croix (Lemon)" };
const beverage2 = { name: "La Croix (Orange)" };
const drink = jest.fn(beverage => beverage.name);
drink(beverage1);
drink(beverage2);
expect(drink).toHaveNthReturnedWith(1, "La Croix (Lemon)");
expect(drink).toHaveNthReturnedWith(2, "La Croix (Orange)");
});
.toBeCloseTo(number, numDigits?)
浮動小数点数を確認
test("toBeCloseTo sample", () => {
// expect(0.2 + 0.1).toBe(0.3); // 0.3ではなく、0.30000000000000004でテストがうまくいかない
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});
.toBeDefined()
未定義でないことを確認
test("toBeDefined sample", () => {
function fetchNewFlavorIdea() {
return true; // もし何も返さなければテストは失敗する
}
expect(fetchNewFlavorIdea()).toBeDefined();
});
.toBeFalsy()
falseと評価されるか確認する
test("toBeFalsy sample", () => {
expect(0).toBeFalsy();
expect("").toBeFalsy();
expect(false).toBeFalsy();
});
.toBeGreaterThan(number)
超えるか確認
test("toBeGreaterThan sample", () => {
expect(11).toBeGreaterThan(10);
});
.toBeGreaterThanOrEqual(number)
以上か確認
test("toBeGreaterThanOrEqual sample", () => {
expect(10).toBeGreaterThanOrEqual(10);
expect(11).toBeGreaterThanOrEqual(10);
});
.toBeLessThan(number)
未満か確認
test("toBeLessThan sample", () => {
expect(9).toBeLessThan(10);
});
.toBeLessThanOrEqual(number)
以下か
test("toBeLessThanOrEqual sample", () => {
expect(9).toBeLessThanOrEqual(10);
expect(10).toBeLessThanOrEqual(10);
});
.toBeInstanceOf(Class)
オブジェクトがクラスのインスタンスであることを確認
test("toBeInstanceOf sample", () => {
class A {}
expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
});
.toBeNull()
nullか確認
test("toBeNull sample", () => {
expect(null).toBeNull();
});
.toBeTruthy()
trueと評価されるか
test("toBeTruthy sample", () => {
expect(1).toBeTruthy();
expect("a").toBeTruthy();
expect(true).toBeTruthy();
});
.toBeUndefined()
undefindeか確認
test("toBeUndefined sample", () => {
expect({}.hoge).toBeUndefined();
});
.toBeNaN()
NaNか確認
test("toBeNaN sample", () => {
expect("a" / "b").toBeNaN();
});
.toContain(item)
項目が配列内にあるか確認
test("toContain sample", () => {
expect([1, 2, 3]).toContain(2);
});
.toContainEqual(item)
特定の構造と値を持つ項目が配列に含まれていることを確認
test("toContainEqual sample", () => {
expect([
{ delicious: true, sour: false },
{ hoge: false, fuga: false }
]).toContainEqual({ delicious: true, sour: false });
});
.toEqual(value)
オブジェクトインスタンスのすべてのプロパティを再帰的に比較
test("toEqual sample", () => {
expect({ flavor: "grapefruit", ounces: 12 }).toEqual({
flavor: "grapefruit",
ounces: 12
});
});
.toHaveLength(number)
.length
プロパティが特定の数値か確認
test("toHaveLength sample", () => {
expect([1, 2, 3]).toHaveLength(3);
expect("abc").toHaveLength(3);
expect("").not.toHaveLength(5);
});
.toMatch(regexpOrString)
文字列が正規表現と一致するか確認
test("toMatch sample", () => {
expect("abcgrapefruitxyz").toMatch(/grapefruit/);
expect("abcgrapefruitxyz").toMatch(new RegExp("grapefruit"));
});
.toMatchObject(object)
オブジェクトのプロパティを確認
test("toMatchObject sample", () => {
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ["oven", "stove", "washer"],
area: 20,
wallColor: "white"
}
};
const desiredHouse = {
bath: true,
kitchen: {
amenities: ["oven", "stove", "washer"],
wallColor: expect.stringMatching(/white|yellow/)
}
};
expect(houseForSale).toMatchObject(desiredHouse);
});
.toHaveProperty(keyPath, value?)
指定のkeyPath
がオブジェクトに存在するか確認
test("toHaveProperty sample", () => {
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ["oven", "stove", "washer"],
area: 20,
wallColor: "white",
"nice.oven": true
},
"ceiling.height": 2
};
expect(houseForSale).toHaveProperty("bath");
expect(houseForSale).toHaveProperty("bedrooms", 4);
expect(houseForSale).not.toHaveProperty("pool");
expect(houseForSale).toHaveProperty("kitchen.area", 20);
expect(houseForSale).toHaveProperty("kitchen.amenities", [
"oven",
"stove",
"washer"
]);
});
.toMatchSnapshot(propertyMatchers?, hint?)
スナップショットが一致している確認
test("toMatchSnapshot sample", () => {
const myMockFn = jest
.fn()
.mockReturnValue("default")
.mockImplementation(scalar => 42 + scalar)
.mockName("add42");
expect(myMockFn).toMatchSnapshot();
});
.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)
__snapshots__
フォルダ内ではなく、インラインにスナップショットを書き込んで一致を確認
.toStrictEqual(value)
オブジェクトの構造と型が同じであることをテストします。
test("toStrictEqual sample", () => {
class LaCroix {
constructor(flavor) {
this.flavor = flavor;
}
}
expect(new LaCroix("lemon")).toEqual({ flavor: "lemon" });
expect(new LaCroix("lemon")).not.toStrictEqual({ flavor: "lemon" });
});
.toThrow(error?)
関数が呼び出されたときにスローされることをテストする
test("toThrow sample", () => {
expect(() => {
throw new Error();
}).toThrow();
});
.toThrowErrorMatchingSnapshot(hint?)
エラーをスナップショットで確認
test("toThrowErrorMatchingSnapshot sample", () => {
expect(() => {
throw new Error("hogehoge");
}).toThrowErrorMatchingSnapshot();
});
.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
エラーをインラインスナップショットで確認
Mock Functions
jest.fn()
でモック関数を作成できる
mockFn.getMockName()
モック名文字列を返す
const a = jest.fn();
console.log(a.getMockName()); // "jest.fn()"
const b = jest.fn().mockName("hoge");
console.log(b.getMockName()); // "hoge"
mockFn.mock.calls
このモック関数に対して行われたすべての呼び出しの呼び出し引数を含む配列
const c = jest.fn();
c("a", 1);
c("b", 2);
console.log(c.mock.calls); // [ [ 'a', 1 ], [ 'b', 2 ] ]
mockFn.mock.results
このモック関数に対して行われたすべての呼び出しの結果を含む配列
const d = jest.fn(x => x + 1);
d(1);
d(2);
console.log(d.mock.results); // [ { type: 'return', value: 2 }, { type: 'return', value: 3 } ]
mockFn.mock.instances
newを使用してこのモック関数からインスタンス化されたすべてのオブジェクトインスタンスを含む配列
const e = jest.fn();
const mock1 = new e();
const mock2 = new e();
console.log(e.mock.instances[0] === mock1); // true
console.log(e.mock.instances[1] === mock2); // true
mockFn.mockClear()
mockFn.mock.callsおよびmockFn.mock.instances配列に格納されているすべての情報をリセットします
const f = jest.fn();
console.log(f.mock.calls); // []
f(1, 2, 3);
console.log(f.mock.calls); // [ [ 1, 2, 3 ] ]
f.mockClear();
console.log(f.mock.calls); // []
mockFn.mockImplementation(fn)
const h = jest.fn().mockImplementation(scalar => 42 + scalar);
// or: jest.fn(scalar => 42 + scalar);
console.log(h(0) === 42); // true
console.log(h(1) === 43); // true
console.log(h.mock.calls[0][0] === 0); // true
console.log(h.mock.calls[1][0] === 1); // true
mockFn.mockImplementationOnce(fn)
複数の関数呼び出しが異なる結果を生み出すように連鎖することができる
const i = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));
i((err, val) => console.log(val)); // true
i((err, val) => console.log(val)); // false
mockFn.mockName(value)
モック関数を示す文字列を設定する
const j = jest.fn().mockName("fuga");
console.log(j.getMockName()); // "fuga"
mockFn.mockReturnThis()
↓と同じ意味
jest.fn(function() {
return this;
});
mockFn.mockReturnValue(value)
モック関数の返り値を設定できる
const k = jest.fn();
k.mockReturnValue(42);
console.log(k()); // 42
k.mockReturnValue(43);
console.log(k()); // 43
mockFn.mockReturnValueOnce(value)
モック関数の1回のみの返り値を設定できる
const l = jest
.fn()
.mockReturnValue("default")
.mockReturnValueOnce("first call")
.mockReturnValueOnce("second call");
// 'first call', 'second call', 'default', 'default'
console.log(l(), l(), l(), l());
mockFn.mockResolvedValue(value)
↓と同じ意味
jest.fn().mockImplementation(() => Promise.resolve(value));
mockFn.mockResolvedValueOnce(value)
↓と同じ意味
jest.fn().mockImplementationOnce(() => Promise.resolve(value));
mockFn.mockRejectedValue(value)
↓と同じ意味
jest.fn().mockImplementation(() => Promise.reject(value));
mockFn.mockRejectedValueOnce(value)
↓と同じ意味
jest.fn().mockImplementationOnce(() => Promise.reject(value));
test("async test", async () => {
const m = jest.fn().mockResolvedValue(43);
console.log(await m()); // 43
const n = jest
.fn()
.mockResolvedValue("default")
.mockResolvedValueOnce("first call")
.mockResolvedValueOnce("second call");
console.log(await n()); // first call
console.log(await n()); // second call
console.log(await n()); // default
console.log(await n()); // default
const o = jest.fn().mockRejectedValue(new Error("Async error"));
try {
await o();
} catch (e) {
console.log(e.message); // Async error
}
const p = jest
.fn()
.mockResolvedValueOnce("first call")
.mockRejectedValueOnce(new Error("Async error"));
try {
console.log(await p()); // first call
await p();
} catch (e) {
console.log(e.message); // Async error
}
});
読んでいただいてありがとうございましたm(_ _)m