メインコンテンツにスキップ
バージョン: 29.7

Expect

テストを書く際、多くの場合、値が特定の条件を満たしているかを確認する必要があります。expect を使用すると、さまざまなことを検証できる多数の「マッチャー」にアクセスできます。

ヒント

Jestコミュニティによって管理されている追加のJestマッチャーについては、jest-extendedをご覧ください。

情報

このページの TypeScript の例は、Jest API を明示的にインポートした場合にのみ、ドキュメントどおりに機能します。

import {expect, jest, test} from '@jest/globals';

TypeScript で Jest を設定する方法の詳細については、Getting Startedガイドを参照してください。

リファレンス


Expect

expect(value)

expect 関数は、値をテストするたびに使用されます。expect を単独で呼び出すことはほとんどありません。代わりに、expect を「マッチャー」関数と一緒に使用して、値について何かをアサートします。

例を挙げると理解しやすいでしょう。文字列 'grapefruit' を返すメソッド bestLaCroixFlavor() があるとします。これをテストする方法は次のとおりです。

test('the best flavor is grapefruit', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});

この場合、toBe がマッチャー関数です。さまざまなことをテストできるように、以下にドキュメントされているさまざまなマッチャー関数がたくさんあります。

expect の引数は、コードが生成する値である必要があり、マッチャーへの引数は正しい値である必要があります。引数を混同すると、テストは機能しますが、テストが失敗した場合のエラーメッセージが奇妙に見えます。

修飾子

.not

何かをテストする方法がわかっている場合、.not を使用するとその反対をテストできます。たとえば、このコードは、最高の La Croix フレーバーがココナッツではないことをテストします。

test('the best flavor is not coconut', () => {
expect(bestLaCroixFlavor()).not.toBe('coconut');
});

.resolves

resolves を使用して、fulfilled プロミスの値をアンラップし、他のマッチャーをチェーンできるようにします。プロミスが拒否された場合、アサーションは失敗します。

たとえば、このコードは、プロミスが解決され、結果の値が 'lemon' であることをテストします。

test('resolves to lemon', () => {
// make sure to add a return statement
return expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});
メモ

プロミスをテストしているため、テストは非同期のままです。したがって、アンラップされたアサーションを返すことによって、Jest に待機するように指示する必要があります。

または、async/await.resolves と組み合わせて使用することもできます。

test('resolves to lemon', async () => {
await expect(Promise.resolve('lemon')).resolves.toBe('lemon');
await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus');
});

.rejects

.rejects を使用して、拒否されたプロミスの理由をアンラップし、他のマッチャーをチェーンできるようにします。プロミスが fulfilled の場合、アサーションは失敗します。

たとえば、このコードは、プロミスが理由 'octopus' で拒否されることをテストします。

test('rejects to octopus', () => {
// make sure to add a return statement
return expect(Promise.reject(new Error('octopus'))).rejects.toThrow(
'octopus',
);
});
メモ

プロミスをテストしているため、テストは非同期のままです。したがって、アンラップされたアサーションを返すことによって、Jest に待機するように指示する必要があります。

または、async/await.rejects と組み合わせて使用することもできます。

test('rejects to octopus', async () => {
await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus');
});

マッチャー

.toBe(value)

.toBe を使用して、プリミティブ値を比較するか、オブジェクトインスタンスの参照同一性を確認します。Object.is を呼び出して値を比較します。これは、=== の厳密等価演算子よりもテストに適しています。

たとえば、このコードは can オブジェクトのいくつかのプロパティを検証します。

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');
});
});

浮動小数点数で .toBe を使用しないでください。たとえば、丸めが原因で、JavaScript では 0.2 + 0.10.3 と厳密に等しくありません。浮動小数点数がある場合は、代わりに .toBeCloseTo を試してください。

.toBe マッチャーは参照同一性をチェックしますが、アサーションが失敗した場合は値の深い比較をレポートします。特にレポートが大きい場合、プロパティ間の違いがテストが失敗した理由を理解するのに役立たない場合は、比較を expect 関数に移動できます。たとえば、要素が同じインスタンスであるかどうかをアサートするには

  • expect(received).toBe(expected)expect(Object.is(received, expected)).toBe(true) として書き直します。
  • expect(received).not.toBe(expected)expect(Object.is(received, expected)).toBe(false) として書き直します。

.toHaveBeenCalled()

エイリアス: .toBeCalled()

モック関数が呼び出されたことを確認するには、.toHaveBeenCalled を使用します。

たとえば、drinkAll(drink, flavour) 関数があり、drink 関数を受け取り、利用可能なすべての飲み物に適用するとします。drink'lemon' に対して呼び出されているかどうかを確認したいが、'octopus' に対しては呼び出されていないとします。これは、'octopus' フレーバーは非常に奇妙であり、なぜタコ風味のものがあるのか​​と思うからです。このテストスイートでそれを行うことができます

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)

エイリアス: .toBeCalledTimes(number)

モック関数が正確に呼び出された回数を確認するには、.toHaveBeenCalledTimes を使用します。

たとえば、drinkEach(drink, Array<flavor>) 関数があり、drink 関数を受け取り、渡された飲み物の配列に適用するとします。drink 関数が正確に呼び出された回数を確認したい場合があります。このテストスイートでそれを行うことができます

test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});

.toHaveBeenCalledWith(arg1, arg2, ...)

エイリアス: .toBeCalledWith()

モック関数が特定の引数で呼び出されたことを確認するには、.toHaveBeenCalledWith を使用します。引数は .toEqual と同じアルゴリズムでチェックされます。

例えば、register 関数で飲料を登録でき、applyToAll(f) が登録されたすべての飲料に関数 f を適用するとします。これが動作することを確認するには、次のように記述できます。

test('registration applies correctly to orange La Croix', () => {
const beverage = new LaCroix('orange');
register(beverage);
const f = jest.fn();
applyToAll(f);
expect(f).toHaveBeenCalledWith(beverage);
});

.toHaveBeenLastCalledWith(arg1, arg2, ...)

エイリアス: .lastCalledWith(arg1, arg2, ...)

モック関数がある場合、.toHaveBeenLastCalledWith を使用して、最後に呼び出されたときの引数をテストできます。例えば、applyToAllFlavors(f) という関数があり、それがいくつかのフレーバーに f を適用するとします。この関数を呼び出したときに、最後に操作するフレーバーが 'mango' であることを確認したいとします。次のように記述できます。

test('applying to all flavors does mango last', () => {
const drink = jest.fn();
applyToAllFlavors(drink);
expect(drink).toHaveBeenLastCalledWith('mango');
});

.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)

エイリアス: .nthCalledWith(nthCall, arg1, arg2, ...)

モック関数がある場合、.toHaveBeenNthCalledWith を使用して、n 番目に呼び出されたときの引数をテストできます。例えば、drinkEach(drink, Array<flavor>) という関数があり、それがいくつかのフレーバーに f を適用するとします。この関数を呼び出したときに、最初に操作するフレーバーが 'lemon' で、次に操作するフレーバーが 'octopus' であることを確認したいとします。次のように記述できます。

test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenNthCalledWith(1, 'lemon');
expect(drink).toHaveBeenNthCalledWith(2, 'octopus');
});
メモ

n 番目の引数は、1 から始まる正の整数でなければなりません。

.toHaveReturned()

エイリアス: .toReturn()

モック関数がある場合、.toHaveReturned を使用して、モック関数が少なくとも 1 回正常に返された (つまり、エラーをスローしなかった) ことをテストできます。例えば、true を返すモック drink があるとします。次のように記述できます。

test('drinks returns', () => {
const drink = jest.fn(() => true);

drink();

expect(drink).toHaveReturned();
});

.toHaveReturnedTimes(number)

エイリアス: .toReturnTimes(number)

.toHaveReturnedTimes を使用して、モック関数が正確な回数だけ正常に返された (つまり、エラーをスローしなかった) ことを確認します。エラーをスローするモック関数への呼び出しは、関数が返された回数にはカウントされません。

例えば、true を返すモック drink があるとします。次のように記述できます。

test('drink returns twice', () => {
const drink = jest.fn(() => true);

drink();
drink();

expect(drink).toHaveReturnedTimes(2);
});

.toHaveReturnedWith(value)

エイリアス: .toReturnWith(value)

.toHaveReturnedWith を使用して、モック関数が特定の値を返したことを確認します。

例えば、消費された飲料の名前を返すモック drink があるとします。次のように記述できます。

test('drink returns La Croix', () => {
const beverage = {name: 'La Croix'};
const drink = jest.fn(beverage => beverage.name);

drink(beverage);

expect(drink).toHaveReturnedWith('La Croix');
});

.toHaveLastReturnedWith(value)

エイリアス: .lastReturnedWith(value)

.toHaveLastReturnedWith を使用して、モック関数が最後に返した特定の値をテストします。モック関数の最後の呼び出しでエラーがスローされた場合、このマッチャーは期待される戻り値として提供した値に関係なく失敗します。

例えば、消費された飲料の名前を返すモック drink があるとします。次のように記述できます。

test('drink returns La Croix (Orange) last', () => {
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)

エイリアス: .nthReturnedWith(nthCall, value)

.toHaveNthReturnedWith を使用して、モック関数が n 番目の呼び出しで返した特定の値をテストします。モック関数の n 番目の呼び出しでエラーがスローされた場合、このマッチャーは期待される戻り値として提供した値に関係なく失敗します。

例えば、消費された飲料の名前を返すモック drink があるとします。次のように記述できます。

test('drink returns expected nth calls', () => {
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)');
});
メモ

n 番目の引数は、1 から始まる正の整数でなければなりません。

.toHaveLength(number)

.toHaveLength を使用して、オブジェクトに .length プロパティがあり、それが特定の数値に設定されていることを確認します。

これは、配列または文字列のサイズを確認するのに特に役立ちます。

expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);

.toHaveProperty(keyPath, value?)

.toHaveProperty を使用して、提供された参照 keyPath のプロパティがオブジェクトに存在するかどうかを確認します。オブジェクト内の深くネストされたプロパティを確認するには、ドット表記または深い参照のための keyPath を含む配列を使用できます。

オプションの value 引数を指定して、受信したプロパティの値 (オブジェクトインスタンスのすべてのプロパティに対して再帰的に、toEqual マッチャーのような深い等価性としても知られています) を比較できます。

次の例には、ネストされたプロパティを持つ houseForSale オブジェクトが含まれています。toHaveProperty を使用して、オブジェクト内のさまざまなプロパティの存在と値を確認しています。

// Object containing house features to be tested
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
'nice.oven': true,
},
livingroom: {
amenities: [
{
couch: [
['large', {dimensions: [20, 20]}],
['small', {dimensions: [10, 10]}],
],
},
],
},
'ceiling.height': 2,
};

test('this house has my desired features', () => {
// Example Referencing
expect(houseForSale).toHaveProperty('bath');
expect(houseForSale).toHaveProperty('bedrooms', 4);

expect(houseForSale).not.toHaveProperty('pool');

// Deep referencing using dot notation
expect(houseForSale).toHaveProperty('kitchen.area', 20);
expect(houseForSale).toHaveProperty('kitchen.amenities', [
'oven',
'stove',
'washer',
]);

expect(houseForSale).not.toHaveProperty('kitchen.open');

// Deep referencing using an array containing the keyPath
expect(houseForSale).toHaveProperty(['kitchen', 'area'], 20);
expect(houseForSale).toHaveProperty(
['kitchen', 'amenities'],
['oven', 'stove', 'washer'],
);
expect(houseForSale).toHaveProperty(['kitchen', 'amenities', 0], 'oven');
expect(houseForSale).toHaveProperty(
'livingroom.amenities[0].couch[0][1].dimensions[0]',
20,
);
expect(houseForSale).toHaveProperty(['kitchen', 'nice.oven']);
expect(houseForSale).not.toHaveProperty(['kitchen', 'open']);

// Referencing keys with dot in the key itself
expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall');
});

.toBeCloseTo(number, numDigits?)

toBeCloseTo を使用して、浮動小数点数を近似的な等価性で比較します。

オプションの numDigits 引数は、小数点以下のチェックする桁数を制限します。デフォルト値 2 の場合、テスト基準は Math.abs(expected - received) < 0.005 (つまり、10 ** -2 / 2) です。

直感的な等価性比較は、10 進数 (基数 10) 値での算術演算が、精度が制限されたバイナリ (基数 2) 表現で丸め誤差を伴うことが多いため、失敗することがよくあります。例えば、このテストは失敗します。

test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBe(0.3); // Fails!
});

これは、JavaScript では、0.2 + 0.1 が実際には 0.30000000000000004 であるため、失敗します。

例えば、このテストは 5 桁の精度で合格します。

test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});

浮動小数点誤差は toBeCloseTo が解決する問題であるため、大きな整数値をサポートしていません。

.toBeDefined()

.toBeDefined を使用して、変数が未定義ではないことを確認します。例えば、関数 fetchNewFlavorIdea()何かを返すことを確認したい場合は、次のように記述できます。

test('there is a new flavor idea', () => {
expect(fetchNewFlavorIdea()).toBeDefined();
});

expect(fetchNewFlavorIdea()).not.toBe(undefined) と記述することもできますが、コード内で直接 undefined を参照しない方が良い習慣です。

.toBeFalsy()

値が何であるかを気にせず、ブールコンテキストで値が false であることを確認したい場合は、.toBeFalsy を使用します。例えば、次のようなアプリケーションコードがあるとします。

drinkSomeLaCroix();
if (!getErrors()) {
drinkMoreLaCroix();
}

getErrors が具体的に何を返すかは気にしないかもしれません。falsenull、または 0 を返す可能性があり、コードはそれでも機能します。したがって、La Croix を飲んだ後にエラーがないことをテストしたい場合は、次のように記述できます。

test('drinking La Croix does not lead to errors', () => {
drinkSomeLaCroix();
expect(getErrors()).toBeFalsy();
});

JavaScript では、6 つの falsy 値があります: false, 0, '', null, undefined, および NaN。その他はすべて truthy です。

.toBeGreaterThan(number | bigint)

toBeGreaterThan を使用して、数値または大きな整数値に対して received > expected を比較します。例えば、ouncesPerCan() が 10 オンスを超える値を返すことをテストします。

test('ounces per can is more than 10', () => {
expect(ouncesPerCan()).toBeGreaterThan(10);
});

.toBeGreaterThanOrEqual(number | bigint)

toBeGreaterThanOrEqual を使用して、数値または大きな整数値に対して received >= expected を比較します。例えば、ouncesPerCan() が 12 オンス以上の値を返すことをテストします。

test('ounces per can is at least 12', () => {
expect(ouncesPerCan()).toBeGreaterThanOrEqual(12);
});

.toBeLessThan(number | bigint)

toBeLessThan を使用して、数値または大きな整数値に対して received < expected を比較します。例えば、ouncesPerCan() が 20 オンス未満の値を返すことをテストします。

test('ounces per can is less than 20', () => {
expect(ouncesPerCan()).toBeLessThan(20);
});

.toBeLessThanOrEqual(number | bigint)

toBeLessThanOrEqual を使用して、数値または大きな整数値に対して received <= expected を比較します。例えば、ouncesPerCan() が最大で 12 オンスの値を返すことをテストします。

test('ounces per can is at most 12', () => {
expect(ouncesPerCan()).toBeLessThanOrEqual(12);
});

.toBeInstanceOf(Class)

.toBeInstanceOf(Class) を使用して、オブジェクトがクラスのインスタンスであることを確認します。このマッチャーは内部で instanceof を使用します。

class A {}

expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws

.toBeNull()

.toBeNull().toBe(null) と同じですが、エラーメッセージが少し優れています。したがって、何かが null であることを確認したい場合は、.toBeNull() を使用します。

function bloop() {
return null;
}

test('bloop returns null', () => {
expect(bloop()).toBeNull();
});

.toBeTruthy()

値が何であるかを気にせず、ブールコンテキストで値が true であることを確認したい場合は、.toBeTruthy を使用します。例えば、次のようなアプリケーションコードがあるとします。

drinkSomeLaCroix();
if (thirstInfo()) {
drinkMoreLaCroix();
}

thirstInfo が具体的に何を返すかは気にしないかもしれません。true または複雑なオブジェクトを返す可能性があり、コードはそれでも機能します。したがって、La Croix を飲んだ後に thirstInfo が truthy になることをテストしたい場合は、次のように記述できます。

test('drinking La Croix leads to having thirst info', () => {
drinkSomeLaCroix();
expect(thirstInfo()).toBeTruthy();
});

JavaScript では、6 つの falsy 値があります: false, 0, '', null, undefined, および NaN。その他はすべて truthy です。

.toBeUndefined()

.toBeUndefined を使用して、変数が未定義であることを確認します。例えば、関数 bestDrinkForFlavor(flavor)'octopus' フレーバーに対して undefined を返すことを確認したい場合、これは良いタコ風味の飲み物がないためです。

test('the best drink for octopus flavor is undefined', () => {
expect(bestDrinkForFlavor('octopus')).toBeUndefined();
});

expect(bestDrinkForFlavor('octopus')).toBe(undefined) と記述することもできますが、コード内で直接 undefined を参照しない方が良い習慣です。

.toBeNaN()

値がNaNであるかを確認する場合は、.toBeNaNを使用します。

test('passes when value is NaN', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});

.toContain(item)

配列内にアイテムがあるかを確認したい場合は、.toContainを使用します。配列内のアイテムのテストには、厳密な等価性チェックである===を使用します。.toContainは、ある文字列が別の文字列の部分文字列であるかどうかを確認することもできます。

たとえば、getAllFlavors()がフレーバーの配列を返し、limeがそこに含まれていることを確認したい場合は、次のように記述できます。

test('the flavor list contains lime', () => {
expect(getAllFlavors()).toContain('lime');
});

このマッチャーは、文字列、セット、ノードリスト、HTMLコレクションなどの他のイテラブルも受け入れます。

.toContainEqual(item)

特定の構造と値を持つアイテムが配列に含まれているかを確認したい場合は、.toContainEqualを使用します。配列内のアイテムのテストには、このマッチャーはオブジェクトの同一性をチェックするのではなく、すべてのフィールドの等価性を再帰的にチェックします。

describe('my beverage', () => {
test('is delicious and not sour', () => {
const myBeverage = {delicious: true, sour: false};
expect(myBeverages()).toContainEqual(myBeverage);
});
});

.toEqual(value)

.toEqualを使用して、オブジェクトインスタンスのすべてのプロパティを再帰的に比較します(「深い」等価性とも呼ばれます)。プリミティブ値を比較するためにObject.isを呼び出しますが、これは===厳密等価演算子よりもテストに適しています。

たとえば、このテストスイートでは.toEqual.toBeの動作が異なるため、すべてのテストがパスします。

const can1 = {
flavor: 'grapefruit',
ounces: 12,
};
const can2 = {
flavor: 'grapefruit',
ounces: 12,
};

describe('the La Croix cans on my desk', () => {
test('have all the same properties', () => {
expect(can1).toEqual(can2);
});
test('are not the exact same can', () => {
expect(can1).not.toBe(can2);
});
});
ヒント

toEqualは、undefinedプロパティを持つオブジェクトキー、undefined配列項目、配列のスパース性、またはオブジェクト型の不一致を無視します。これらを考慮に入れるには、.toStrictEqualを代わりに使用してください。

情報

.toEqualは、2つのエラーに対して深い等価性チェックを実行しません。Errorのmessageプロパティのみが等価性のために考慮されます。エラーに対するテストには、.toThrowマッチャーを使用することをお勧めします。

プロパティ間の違いが、特にレポートが大きい場合にテストが失敗する理由を理解するのに役立たない場合は、比較をexpect関数に移動することができます。たとえば、バッファーが同じ内容を含むかどうかをアサートするには、Bufferクラスのequalsメソッドを使用します。

  • expect(received).toEqual(expected)expect(received.equals(expected)).toBe(true)として書き換えます
  • expect(received).not.toEqual(expected)expect(received.equals(expected)).toBe(false)として書き換えます

.toMatch(regexp | string)

文字列が正規表現に一致することを確認するには、.toMatchを使用します。

たとえば、essayOnTheBestFlavor()が正確に何を返すかはわからないかもしれませんが、それが非常に長い文字列であり、部分文字列grapefruitがどこかに含まれているはずであることはわかっています。これは次のようにテストできます。

describe('an essay on the best flavor', () => {
test('mentions grapefruit', () => {
expect(essayOnTheBestFlavor()).toMatch(/grapefruit/);
expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit'));
});
});

このマッチャーは、一致させようとする文字列も受け入れます。

describe('grapefruits are healthy', () => {
test('grapefruits are a fruit', () => {
expect('grapefruits').toMatch('fruit');
});
});

.toMatchObject(object)

JavaScriptオブジェクトがオブジェクトのプロパティのサブセットに一致することを確認するには、.toMatchObjectを使用します。予期されるオブジェクトにないプロパティを持つ受信オブジェクトに一致します。

オブジェクトの配列を渡すこともできます。その場合、受信した配列内の各オブジェクトが、予期された配列内の対応するオブジェクト(上記のtoMatchObjectの意味で)に一致する場合にのみ、メソッドはtrueを返します。これは、arrayContainingとは対照的に、受信した配列に追加の要素を許可するため、2つの配列が要素の数で一致するかどうかを確認する場合に便利です。

プロパティを値またはマッチャーに対して一致させることができます。

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/),
},
};

test('the house has my desired features', () => {
expect(houseForSale).toMatchObject(desiredHouse);
});
describe('toMatchObject applied to arrays', () => {
test('the number of elements must match exactly', () => {
expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]);
});

test('.toMatchObject is called for each elements, so extra object properties are okay', () => {
expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([
{foo: 'bar'},
{baz: 1},
]);
});
});

.toMatchSnapshot(propertyMatchers?, hint?)

これにより、値が最新のスナップショットと一致することが保証されます。詳細については、スナップショットテストガイドを確認してください。

受信した値がオブジェクトインスタンスである場合、予期されるプロパティのサブセットの値として非対称マッチャーを持つオプションのpropertyMatchersオブジェクト引数を指定できます。これは、プロパティのサブセットに対して柔軟な基準を持つtoMatchObjectに似ており、残りのプロパティに対する正確な基準としてのスナップショットテストが続きます。

テスト名に追加されるオプションのhint文字列引数を指定できます。Jestは常にスナップショット名の最後に数字を追加しますが、短い説明的なヒントは、単一itまたはtestブロックで複数のスナップショットを区別するのに数字よりも役立つ場合があります。Jestは、対応する.snapファイル内のスナップショットを名前でソートします。

.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)

値が最新のスナップショットと一致することを保証します。

受信した値がオブジェクトインスタンスである場合、予期されるプロパティのサブセットの値として非対称マッチャーを持つオプションのpropertyMatchersオブジェクト引数を指定できます。これは、プロパティのサブセットに対して柔軟な基準を持つtoMatchObjectに似ており、残りのプロパティに対する正確な基準としてのスナップショットテストが続きます。

Jestは、テストが最初に実行されたときに、外部の.snapファイルではなく、テストファイル内のマッチャーにinlineSnapshot文字列引数を追加します。

詳細については、インラインスナップショットのセクションを確認してください。

.toStrictEqual(value)

オブジェクトが同じ構造と型を持つことをテストするには、.toStrictEqualを使用します。

.toEqualとの違い

  • undefinedプロパティを持つキーがチェックされます。例:{a: undefined, b: 2}{b: 2}と等しくなりません。
  • undefined項目が考慮されます。例:[2][2, undefined]と等しくなりません。
  • 配列のスパース性がチェックされます。例:[, 1][undefined, 1]と等しくなりません。
  • オブジェクト型がチェックされます。例:フィールドabを持つクラスインスタンスは、フィールドabを持つリテラルオブジェクトと等しくなりません。
class LaCroix {
constructor(flavor) {
this.flavor = flavor;
}
}

describe('the La Croix cans on my desk', () => {
test('are not semantically the same', () => {
expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'});
expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'});
});
});

.toThrow(error?)

エイリアス.toThrowError(error?)でも利用できます。

関数が呼び出されたときにスローされることをテストするには、.toThrowを使用します。たとえば、タコのフレーバーは飲むにはあまりにも不快であるため、drinkFlavor('octopus')がスローされることをテストしたい場合は、次のように記述できます。

test('throws on octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow();
});
ヒント

コードを関数でラップする必要があります。そうしないと、エラーがキャッチされず、アサーションが失敗します。

特定の型のエラーがスローされることをテストするためのオプションの引数を指定できます。

  • 正規表現:エラーメッセージはパターンに一致します。
  • 文字列:エラーメッセージには部分文字列が含まれます。
  • エラーオブジェクト:エラーメッセージは、オブジェクトのメッセージプロパティと等しくなります。
  • エラークラス:エラーオブジェクトはクラスのインスタンスです。

たとえば、drinkFlavorが次のようにコーディングされているとします。

function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}

このエラーがいくつかの方法でスローされることをテストできます。

test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}

// Test that the error message says "yuck" somewhere: these are equivalent
expect(drinkOctopus).toThrow(/yuck/);
expect(drinkOctopus).toThrow('yuck');

// Test the exact error message
expect(drinkOctopus).toThrow(/^yuck, octopus flavor$/);
expect(drinkOctopus).toThrow(new Error('yuck, octopus flavor'));

// Test that we get a DisgustingFlavorError
expect(drinkOctopus).toThrow(DisgustingFlavorError);
});

.toThrowErrorMatchingSnapshot(hint?)

関数が呼び出されたときに、最新のスナップショットに一致するエラーをスローすることをテストするには、.toThrowErrorMatchingSnapshotを使用します。

テスト名に追加されるオプションのhint文字列引数を指定できます。Jestは常にスナップショット名の最後に数字を追加しますが、短い説明的なヒントは、単一itまたはtestブロックで複数のスナップショットを区別するのに数字よりも役立つ場合があります。Jestは、対応する.snapファイル内のスナップショットを名前でソートします。

たとえば、フレーバーが'octopus'の場合に常にスローし、次のようにコーディングされているdrinkFlavor関数があるとします。

function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}

この関数のテストは、次のようになります。

test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}

expect(drinkOctopus).toThrowErrorMatchingSnapshot();
});

そして、次のスナップショットが生成されます。

exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`;

スナップショットテストの詳細については、React Tree Snapshot Testingを確認してください。

.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)

関数が呼び出されたときに、最新のスナップショットに一致するエラーをスローすることをテストするには、.toThrowErrorMatchingInlineSnapshotを使用します。

Jestは、テストが最初に実行されたときに、外部の.snapファイルではなく、テストファイル内のマッチャーにinlineSnapshot文字列引数を追加します。

詳細については、インラインスナップショットのセクションを確認してください。

非対称マッチャー

expect.anything()

expect.anything()は、nullまたはundefinedを除くすべてに一致します。リテラル値の代わりに、toEqualまたはtoHaveBeenCalledWith内で使用できます。たとえば、モック関数がnull以外の引数で呼び出されていることを確認する場合は、次のようにします。

test('map calls its argument with a non-null argument', () => {
const mock = jest.fn();
[1].map(x => mock(x));
expect(mock).toHaveBeenCalledWith(expect.anything());
});

expect.any(constructor)

expect.any(constructor)は、指定されたコンストラクターで作成されたもの、または渡された型のプリミティブであるものに一致します。リテラル値の代わりに、toEqualまたはtoHaveBeenCalledWith内で使用できます。たとえば、モック関数が数値で呼び出されていることを確認する場合は、次のようにします。

class Cat {}
function getCat(fn) {
return fn(new Cat());
}

test('randocall calls its callback with a class instance', () => {
const mock = jest.fn();
getCat(mock);
expect(mock).toHaveBeenCalledWith(expect.any(Cat));
});

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).toHaveBeenCalledWith(expect.any(Number));
});

expect.arrayContaining(array)

expect.arrayContaining(array)は、予期された配列内のすべての要素を含む受信した配列に一致します。つまり、予期される配列は、受信した配列のサブセットです。したがって、予期される配列にない要素を含む受信した配列に一致します。

リテラル値の代わりに使用できます。

  • toEqualまたはtoHaveBeenCalledWith
  • objectContainingまたはtoMatchObjectのプロパティを一致させる場合
describe('arrayContaining', () => {
const expected = ['Alice', 'Bob'];
it('matches even if received contains additional elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
});
it('does not match if received does not contain expected elements', () => {
expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected));
});
});
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),
);
});
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),
);
});
});

expect.not.arrayContaining(array)

expect.not.arrayContaining(array)は、予期された配列内のすべての要素を含まない受信した配列に一致します。つまり、予期された配列は、受信した配列のサブセットではありません

これは、expect.arrayContainingの逆です。

describe('not.arrayContaining', () => {
const expected = ['Samantha'];

it('matches if the actual array does not contain the expected elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(
expect.not.arrayContaining(expected),
);
});
});

expect.closeTo(number, numDigits?)

expect.closeTo(number, numDigits?)は、オブジェクトプロパティまたは配列項目で浮動小数点数を比較する場合に便利です。数値を比較する必要がある場合は、代わりに.toBeCloseToを使用してください。

オプションのnumDigits引数は、小数点の後でチェックする桁数を制限します。デフォルト値2の場合、テスト基準はMath.abs(expected - received) < 0.005 (つまり、10 ** -2 / 2)です。

例えば、このテストは 5 桁の精度で合格します。

test('compare float in object properties', () => {
expect({
title: '0.1 + 0.2',
sum: 0.1 + 0.2,
}).toEqual({
title: '0.1 + 0.2',
sum: expect.closeTo(0.3, 5),
});
});

expect.objectContaining(object)

expect.objectContaining(object)は、再帰的に予期されたプロパティに一致する任意の受信オブジェクトに一致します。つまり、予期されるオブジェクトは、受信したオブジェクトのサブセットです。したがって、予期されるオブジェクトに存在するプロパティを含む受信したオブジェクトに一致します。

予期されるオブジェクト内のリテラルプロパティ値の代わりに、マッチャー、expect.anything()などを使用できます。

たとえば、onPress関数がEventオブジェクトで呼び出されることを期待しており、検証する必要があるのは、イベントにevent.xおよびevent.yプロパティがあることだけだとします。これは、次のようにして実現できます。

test('onPress gets called with the right thing', () => {
const onPress = jest.fn();
simulatePresses(onPress);
expect(onPress).toHaveBeenCalledWith(
expect.objectContaining({
x: expect.any(Number),
y: expect.any(Number),
}),
);
});

expect.not.objectContaining(object)

expect.not.objectContaining(object)は、再帰的に予期されたプロパティに一致しない任意の受信オブジェクトに一致します。つまり、予期されるオブジェクトは、受信したオブジェクトのサブセットではありません。したがって、予期されるオブジェクトにないプロパティを含む受信したオブジェクトに一致します。

これは、expect.objectContainingの逆です。

describe('not.objectContaining', () => {
const expected = {foo: 'bar'};

it('matches if the actual object does not contain expected key: value pairs', () => {
expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected));
});
});

expect.stringContaining(string)

expect.stringContaining(string)は、受信した値が、予期された文字列を正確に含む文字列である場合に一致します。

expect.not.stringContaining(string)

expect.not.stringContaining(string) は、受け取った値が文字列でない場合、または文字列であっても、期待される文字列と完全に一致するものが含まれていない場合に一致します。

これは、expect.stringContaining の逆です。

describe('not.stringContaining', () => {
const expected = 'Hello world!';

it('matches if the received value does not contain the expected substring', () => {
expect('How are you?').toEqual(expect.not.stringContaining(expected));
});
});

expect.stringMatching(string | regexp)

expect.stringMatching(string | regexp) は、受け取った値が、期待される文字列または正規表現に一致する文字列である場合に一致します。

リテラル値の代わりに使用できます。

  • toEqualまたはtoHaveBeenCalledWith
  • arrayContaining 内の要素に一致させるには
  • objectContainingまたはtoMatchObjectのプロパティを一致させる場合

この例は、expect.stringMatchingexpect.arrayContaining の内部で使用して、複数の非対称マッチャーをネストする方法も示しています。

describe('stringMatching in arrayContaining', () => {
const expected = [
expect.stringMatching(/^Alic/),
expect.stringMatching(/^[BR]ob/),
];
it('matches even if received contains additional elements', () => {
expect(['Alicia', 'Roberto', 'Evelina']).toEqual(
expect.arrayContaining(expected),
);
});
it('does not match if received does not contain expected elements', () => {
expect(['Roberto', 'Evelina']).not.toEqual(
expect.arrayContaining(expected),
);
});
});

expect.not.stringMatching(string | regexp)

expect.not.stringMatching(string | regexp) は、受け取った値が文字列でない場合、または文字列であっても、期待される文字列または正規表現に一致しない場合に一致します。

これは、expect.stringMatching の逆です。

describe('not.stringMatching', () => {
const expected = /Hello world!/;

it('matches if the received value does not match the expected regex', () => {
expect('How are you?').toEqual(expect.not.stringMatching(expected));
});
});

アサーション数

expect.assertions(number)

expect.assertions(number) は、テスト中に特定の数のアサーションが呼び出されることを検証します。これは、コールバック内のアサーションが実際に呼び出されたことを確認するために、非同期コードをテストする際に役立ちます。

たとえば、2つのコールバック callback1callback2 を受け取る関数 doAsync があり、それが非同期的に両方を不明な順序で呼び出すとします。これを以下のようにテストできます。

test('doAsync calls both callbacks', () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}

doAsync(callback1, callback2);
});

expect.assertions(2) を呼び出すことで、両方のコールバックが実際に呼び出されることが保証されます。

expect.hasAssertions()

expect.hasAssertions() は、テスト中に少なくとも1つのアサーションが呼び出されることを検証します。これは、コールバック内のアサーションが実際に呼び出されたことを確認するために、非同期コードをテストする際に役立ちます。

たとえば、すべて状態を扱ういくつかの関数があるとします。prepareState は状態オブジェクトを持つコールバックを呼び出し、validateState はその状態オブジェクトに対して実行され、waitOnState はすべての prepareState コールバックが完了するまで待機するPromiseを返します。これを以下のようにテストできます。

test('prepareState prepares a valid state', () => {
expect.hasAssertions();
prepareState(state => {
expect(validateState(state)).toBeTruthy();
});
return waitOnState();
});

expect.hasAssertions() を呼び出すことで、prepareState コールバックが実際に呼び出されることが保証されます。

ユーティリティの拡張

expect.addEqualityTesters(testers)

expect.addEqualityTesters を使用して、2つのオブジェクトが等しいかどうかをテストするための独自のメソッドを追加できます。たとえば、異なる単位を使用する2つの体積が等しいかどうかを判断できる、体積を表すクラスがコードにあるとします。toEqual(およびその他の等価マッチャー)が、Volumeクラスと比較する際にこのカスタム等価メソッドを使用するようにできます。カスタム等価テスターを追加して、Volumeクラスを比較するときに toEqual がカスタムロジックを検出し、適用するようにできます。

Volume.js
// For simplicity in this example, we'll just support the units 'L' and 'mL'
export class Volume {
constructor(amount, unit) {
this.amount = amount;
this.unit = unit;
}

toString() {
return `[Volume ${this.amount}${this.unit}]`;
}

equals(other) {
if (this.unit === other.unit) {
return this.amount === other.amount;
} else if (this.unit === 'L' && other.unit === 'mL') {
return this.amount * 1000 === other.unit;
} else {
return this.amount === other.unit * 1000;
}
}
}
areVolumesEqual.js
import {expect} from '@jest/globals';
import {Volume} from './Volume.js';

function areVolumesEqual(a, b) {
const isAVolume = a instanceof Volume;
const isBVolume = b instanceof Volume;

if (isAVolume && isBVolume) {
return a.equals(b);
} else if (isAVolume === isBVolume) {
return undefined;
} else {
return false;
}
}

expect.addEqualityTesters([areVolumesEqual]);
__tests__/Volume.test.js
import {expect, test} from '@jest/globals';
import {Volume} from '../Volume.js';
import '../areVolumesEqual.js';

test('are equal with different units', () => {
expect(new Volume(1, 'L')).toEqual(new Volume(1000, 'mL'));
});

カスタム等価テスターAPI

カスタムテスターは、与えられた2つの引数の等価性を比較した結果(true または false)を返すか、テスターが与えられたオブジェクトを処理せず、等価性を他のテスター(たとえば、組み込みの等価テスター)に委譲したい場合は undefined を返す関数です。

カスタムテスターは、比較する2つのオブジェクトとカスタムテスターの配列(再帰的テスターに使用、以下のセクションを参照)の3つの引数で呼び出されます。

これらのヘルパー関数とプロパティは、カスタムテスター内の this で見つけることができます。

this.equals(a, b, customTesters?)

これは、2つのオブジェクトが同じ値(再帰的に)を持つ場合に true を返す深さ優先の等価関数です。オプションで、深さ優先の等価チェックに適用するカスタム等価テスターのリストを受け取ります。この関数を使用する場合は、カスタムテスターを渡してください。これにより、テスト作成者が構成した可能性のあるカスタムテスターを、equals が適用するさらなる等価チェックでも使用できます。詳細については、「再帰的なカスタム等価テスター」セクションの例を参照してください。

マッチャーとテスター

マッチャーは、expect で使用できるメソッドであり、たとえば expect().toEqual() です。toEqual はマッチャーです。テスターは、オブジェクトが同じかどうかを判断するために、等価性チェックを行うマッチャーで使用されるメソッドです。

カスタムマッチャーは、テスト作成者がテストで使用できるカスタムアサーションを提供する場合に便利です。たとえば、expect.extend セクションの toBeWithinRange の例は、カスタムマッチャーの適切な例です。テスト作成者が2つの数値が完全に等しいことをアサートする必要がある場合は、toBe を使用する必要があります。ただし、テスト作成者がテストに柔軟性を持たせたい場合があり、toBeWithinRange がより適切なアサーションである場合があります。

カスタム等価テスターは、すべての等価性比較に対してカスタム等価ロジックを適用するために、Jestマッチャーをグローバルに拡張する場合に便利です。テスト作成者は、特定のアサーションに対してカスタムテスターを有効にし、他のアサーションに対して無効にすることはできません(その動作が必要な場合は、代わりにカスタムマッチャーを使用する必要があります)。たとえば、すべてのマッチャーに対して2つの Volume オブジェクトが等しいかどうかをチェックする方法を定義することは、適切なカスタム等価テスターになります。

再帰的なカスタム等価テスター

カスタム等価テスターが、深さ優先の等価性チェックを実行したいプロパティを持つオブジェクトをテストしている場合は、等価テスターで使用できる this.equals ヘルパーを使用する必要があります。この equals メソッドは、Jestが内部的にすべての深さ優先の等価性比較に使用するのと同じ深さ優先の等価メソッドです。それはカスタム等価テスターを呼び出すメソッドです。これは、3番目の引数としてカスタム等価テスターの配列を受け入れます。カスタム等価テスターには、3番目の引数としてカスタムテスターの配列も指定されます。オブジェクトの奥深くにあるさらなる等価チェックでもカスタム等価テスターを利用できるように、この引数を equals の3番目の引数に渡してください。

たとえば、Author クラスの配列を含む Book クラスがあり、これらのクラスの両方にカスタムテスターがあるとします。Book カスタムテスターは、Author の配列に対して深さ優先の等価性チェックを実行し、渡されたカスタムテスターを渡して、Author のカスタム等価テスターが適用されるようにする必要があります。

customEqualityTesters.js
function areAuthorEqual(a, b) {
const isAAuthor = a instanceof Author;
const isBAuthor = b instanceof Author;

if (isAAuthor && isBAuthor) {
// Authors are equal if they have the same name
return a.name === b.name;
} else if (isAAuthor === isBAuthor) {
return undefined;
} else {
return false;
}
}

function areBooksEqual(a, b, customTesters) {
const isABook = a instanceof Book;
const isBBook = b instanceof Book;

if (isABook && isBBook) {
// Books are the same if they have the same name and author array. We need
// to pass customTesters to equals here so the Author custom tester will be
// used when comparing Authors
return (
a.name === b.name && this.equals(a.authors, b.authors, customTesters)
);
} else if (isABook === isBBook) {
return undefined;
} else {
return false;
}
}

expect.addEqualityTesters([areAuthorsEqual, areBooksEqual]);
メモ

テスターのコンテキストヘルパー(this.equals など)にアクセスするために、等価テスターをアロー関数ではなく、通常の関数として定義することを忘れないでください。

expect.addSnapshotSerializer(serializer)

expect.addSnapshotSerializer を呼び出して、アプリケーション固有のデータ構造をフォーマットするモジュールを追加できます。

個々のテストファイルの場合、追加されたモジュールは、snapshotSerializers 構成からのモジュール、次に組み込みのJavaScript型およびReact要素のデフォルトのスナップショットシリアライザーよりも優先されます。最後に追加されたモジュールが最初にテストされるモジュールです。

import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);

// affects expect(value).toMatchSnapshot() assertions in the test file

snapshotSerializers 構成に追加する代わりに、個々のテストファイルにスナップショットシリアライザーを追加すると

  • 依存関係を暗黙的ではなく明示的にします。
  • create-react-app からイジェクトする可能性のある構成の制限を回避できます。

詳細については、Jestの構成を参照してください。

expect.extend(matchers)

expect.extend を使用して、独自のマッチャーをJestに追加できます。たとえば、数値ユーティリティライブラリをテストしていて、数値が他の数値の特定の範囲内に表示されることを頻繁にアサートしているとします。それを toBeWithinRange マッチャーに抽象化できます。

toBeWithinRange.js
import {expect} from '@jest/globals';

function toBeWithinRange(actual, floor, ceiling) {
if (
typeof actual !== 'number' ||
typeof floor !== 'number' ||
typeof ceiling !== 'number'
) {
throw new TypeError('These must be of type number!');
}

const pass = actual >= floor && actual <= ceiling;
if (pass) {
return {
message: () =>
`expected ${this.utils.printReceived(
actual,
)} not to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${this.utils.printReceived(
actual,
)} to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: false,
};
}
}

expect.extend({
toBeWithinRange,
});
__tests__/ranges.test.js
import {expect, test} from '@jest/globals';
import '../toBeWithinRange';

test('is within range', () => expect(100).toBeWithinRange(90, 110));

test('is NOT within range', () => expect(101).not.toBeWithinRange(0, 100));

test('asymmetric ranges', () => {
expect({apples: 6, bananas: 3}).toEqual({
apples: expect.toBeWithinRange(1, 10),
bananas: expect.not.toBeWithinRange(11, 20),
});
});
toBeWithinRange.d.ts
// optionally add a type declaration, e.g. it enables autocompletion in IDEs
declare module 'expect' {
interface AsymmetricMatchers {
toBeWithinRange(floor: number, ceiling: number): void;
}
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
}
}

export {};
ヒント

マッチャーの型宣言は、.d.ts ファイルまたはインポートされた .ts モジュールに存在できます(それぞれ上記のJSおよびTSの例を参照)。宣言を .d.ts ファイルに保持する場合は、プログラムに含まれており、有効なモジュールであることを確認してください。つまり、少なくとも空の export {} があります。

ヒント

テストファイルに toBeWithinRange モジュールをインポートする代わりに、setupFilesAfterEnv スクリプトに expect.extend 呼び出しを移動することで、すべてのテストでマッチャーを有効にできます。

import {expect} from '@jest/globals';
// remember to export `toBeWithinRange` as well
import {toBeWithinRange} from './toBeWithinRange';

expect.extend({
toBeWithinRange,
});

非同期マッチャー

expect.extend は、非同期マッチャーもサポートしています。非同期マッチャーはPromiseを返すため、返された値を待機する必要があります。使用法を示すために、例のマッチャーを使用してみましょう。外部ソースから可除数がプルされる toBeDivisibleByExternalValue というマッチャーを実装します。

expect.extend({
async toBeDivisibleByExternalValue(received) {
const externalValue = await getExternalValueFromRemoteSource();
const pass = received % externalValue == 0;
if (pass) {
return {
message: () =>
`expected ${received} not to be divisible by ${externalValue}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${received} to be divisible by ${externalValue}`,
pass: false,
};
}
},
});

test('is divisible by external value', async () => {
await expect(100).toBeDivisibleByExternalValue();
await expect(101).not.toBeDivisibleByExternalValue();
});

カスタムマッチャーAPI

マッチャーは、2つのキーを持つオブジェクト(またはオブジェクトのPromise)を返す必要があります。pass は一致があったかどうかを示し、message は失敗した場合のエラーメッセージを返す引数なしの関数を提供します。したがって、pass がfalseの場合、messageexpect(x).yourMatcher() が失敗した場合のエラーメッセージを返す必要があります。また、pass がtrueの場合、messageexpect(x).not.yourMatcher() が失敗した場合のエラーメッセージを返す必要があります。

マッチャーは、expect(x) に渡された引数の後に、.yourMatcher(y, z) に渡された引数を続けて呼び出されます。

expect.extend({
yourMatcher(x, y, z) {
return {
pass: true,
message: () => '',
};
},
});

これらのヘルパー関数とプロパティは、カスタムマッチャー内の this で見つけることができます。

this.isNot

このマッチャーが否定された .not 修飾子で呼び出されたことを知らせるブール値で、明確で正しいマッチャーヒントを表示できます(コード例を参照)。

this.promise

明確で正しいマッチャーヒントを表示できる文字列

  • マッチャーがPromiseの .rejects 修飾子で呼び出された場合は 'rejects'
  • マッチャーがPromiseの .resolves 修飾子で呼び出された場合は 'resolves'
  • マッチャーがPromiseの修飾子で呼び出されなかった場合は ''

this.equals(a, b, customTesters?)

これは、2つのオブジェクトが(再帰的に)同じ値を持っている場合にtrueを返す、ディープイコール関数です。オプションで、ディープイコールチェックに適用するカスタムイコールテスターのリストを受け取ることができます(下記のthis.customTestersを参照)。

this.expand

このマッチャーがexpandオプション付きで呼び出されたかどうかを示すブール値です。Jestが--expandフラグ付きで呼び出された場合、this.expandを使用して、Jestが完全な差分とエラーを表示することが期待されているかどうかを判断できます。

this.utils

this.utilsには、主にjest-matcher-utilsからのエクスポートで構成される、多くの便利なツールが公開されています。

最も便利なのは、エラーメッセージをきれいにフォーマットするためのmatcherHintprintExpectedprintReceivedです。たとえば、toBeマッチャーの実装を見てください。

const {diff} = require('jest-diff');
expect.extend({
toBe(received, expected) {
const options = {
comment: 'Object.is equality',
isNot: this.isNot,
promise: this.promise,
};

const pass = Object.is(received, expected);

const message = pass
? () =>
// eslint-disable-next-line prefer-template
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
`Expected: not ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`
: () => {
const diffString = diff(expected, received, {
expand: this.expand,
});
return (
// eslint-disable-next-line prefer-template
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
(diffString && diffString.includes('- Expect')
? `Difference:\n\n${diffString}`
: `Expected: ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`)
);
};

return {actual: received, message, pass};
},
});

これは、次のようなものを出力します。

  expect(received).toBe(expected)

Expected value to be (using Object.is):
"banana"
Received:
"apple"

アサーションが失敗した場合、エラーメッセージは、ユーザーが問題を迅速に解決できるように、必要なだけのシグナルを提供する必要があります。カスタムアサーションのユーザーが良好な開発者エクスペリエンスを得られるように、正確な失敗メッセージを作成する必要があります。

this.customTesters

マッチャーがthis.equalsを使用してディープイコールチェックを行う場合、ユーザーが提供したカスタムテスターをthis.equalsに渡したい場合があります。ユーザーがaddEqualityTesters APIを使用して提供したカスタムイコールテスターは、このプロパティで利用できます。組み込みのJestマッチャーは、this.customTesters(および他の組み込みテスター)をthis.equalsに渡してディープイコールを実行します。カスタムマッチャーも同様に実行したい場合があります。

カスタムスナップショットマッチャー

カスタムマッチャー内でスナップショットテストを使用するには、jest-snapshotをインポートして、マッチャー内から使用できます。

以下は、指定された長さに文字列をトリムして保存するスナップショットマッチャーです:.toMatchTrimmedSnapshot(length)

const {toMatchSnapshot} = require('jest-snapshot');

expect.extend({
toMatchTrimmedSnapshot(received, length) {
return toMatchSnapshot.call(
this,
received.slice(0, length),
'toMatchTrimmedSnapshot',
);
},
});

it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10);
});

/*
Stored snapshot will look like:

exports[`stores only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`;
*/

インラインスナップショット用のカスタムマッチャーを作成することも可能です。スナップショットはカスタムマッチャーに正しく追加されます。ただし、インラインスナップショットは常に最初の引数または、最初の引数がプロパティマッチャーの場合は2番目の引数に追加しようとするため、カスタムマッチャーでカスタム引数を受け入れることはできません。

const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
toMatchTrimmedInlineSnapshot(received, ...rest) {
return toMatchInlineSnapshot.call(this, received.slice(0, 10), ...rest);
},
});

it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot(
`"extra long"`
);
*/
});

非同期

カスタムインラインスナップショットマッチャーが非同期(つまり、async-awaitを使用)の場合、「同じ呼び出しに対する複数のインラインスナップショットはサポートされていません」のようなエラーが発生する可能性があります。Jestは、スナップショットを適切に更新するために、カスタムインラインスナップショットマッチャーがどこで使用されたかを見つけるための追加のコンテキスト情報が必要です。

const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
async toMatchObservationInlineSnapshot(fn, ...rest) {
// The error (and its stacktrace) must be created before any `await`
this.error = new Error();

// The implementation of `observe` doesn't matter.
// It only matters that the custom snapshot matcher is async.
const observation = await observe(async () => {
await fn();
});

return toMatchInlineSnapshot.call(this, recording, ...rest);
},
});

it('observes something', async () => {
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot(`"async action"`);
*/
});

中断

通常、jestはテストで期待されるすべてのスナップショットを一致させようとします。

以前のスナップショットが失敗した場合、テストを続行するのが意味がない場合があります。たとえば、さまざまな遷移後の状態マシンのスナップショットを作成する場合、1つの遷移で誤った状態が生成されたら、テストを中止できます。

その場合、すべての一致しないものを収集するのではなく、最初の一致しないものが発生した場合に例外をスローするカスタムスナップショットマッチャーを実装できます。

const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
toMatchStateInlineSnapshot(...args) {
this.dontThrow = () => {};

return toMatchInlineSnapshot.call(this, ...args);
},
});

let state = 'initial';

function transition() {
// Typo in the implementation should cause the test to fail
if (state === 'INITIAL') {
state = 'pending';
} else if (state === 'pending') {
state = 'done';
}
}

it('transitions as expected', () => {
expect(state).toMatchStateInlineSnapshot(`"initial"`);

transition();
// Already produces a mismatch. No point in continuing the test.
expect(state).toMatchStateInlineSnapshot(`"loading"`);

transition();
expect(state).toMatchStateInlineSnapshot(`"done"`);
});