🐧
【jest/vitest】toBe と toEqual の違い
JavaScriptで以下のようなobjectを返す関数を考えます。ManyKeyObjectとあるように、非常に多くのkeyを持つobjectを返します。
type ManyKeyObject = {
a: string
b: string
...
z: string
};
const returnObject = ({
object1,
object2,
isFirst,
}: {
object1: ManyKeyObject;
object2: ManyKeyObject;
isFirst: boolean;
}) => {
return isFirst ? object1 : object2;
};
returnObjectのテストを以下のように書くときに、toBeとtoEqualのどちらが適切でしょうか?ManyKeyObjectはkeyが非常に多いため、テストデータを作成せずにobject1とobject2のそれぞれが返されることをテストしたいです。
const object1 = {} as ManyKeyObject;
const object2 = {} as ManyKeyObject;
describe('returnObject', () => {
describe('when isFirst is true', () => {
it('returns object1', () => {
// toBe or toEqual ?
expect(returnObject({ object1, object2, isFirst: true })).toBe(object1);
expect(returnObject({ object1, object2, isFirst: true })).toEqual(
object1,
);
});
});
describe('when isFirst is false', () => {
it('returns object2', () => {
// toBe or toEqual ?
expect(returnObject({ object1, object2, isFirst: false })).toBe(object2);
expect(returnObject({ object1, object2, isFirst: false })).toEqual(
object2,
);
});
});
});
正解はこちら
describe('returnObject', () => {
describe('when isFirst is true', () => {
it('returns object1', () => {
expect(returnObject({ object1, object2, isFirst: true })).toBe(object1);
});
});
describe('when isFirst is false', () => {
it('returns object2', () => {
expect(returnObject({ object1, object2, isFirst: false })).toBe(object2);
});
});
});
のようにtoBeになります。
それぞれの違いについて見ていきましょう。
toBe
プリミティブが等しいか、もしくはobjectの参照が同じかを検証します。Object.is
の結果と同等になります。
今回、object1とobject2は同じ構造であり、object1が返されることをテストしたいため、objectの参照を検証するtoBeを使用するのが適切でした。
toEqual
実際の値が同じか、objectの場合は再帰的に同じ構造かどうかを検証します。
describe('toEqual', () => {
it('works', () => {
expect({ a: 1 }).toEqual({ a: 1 });
expect({
a: {
b: 1,
},
}).toEqual({
a: {
b: 1,
},
});
});
});
冒頭の例では、object1とobject2は同じ構造となり、object1とobject2のどちらと比較してもテストが通ってしまうので、toEqualを使用するのは適切ではありませんでした。
まとめ
toBeとtoEqualはそれぞれ以下のように使い分けができます。
- toBe
- プリミティブの検証
- objectの参照が同じかの検証
- toEqual
- objectの値が同じかの検証
番外編
toBeCloseTo
浮動小数点数の比較に使用されます。
describe('toBe', () => {
it('works', () => {
expect(0.2 + 0.1).toBe(0.3);
});
});
のようなテストをした場合、
Expected: 0.3
Received: 0.30000000000000004
とテストが落ちてしまうので、toBeCloseToでテストしたい桁数を指定することができます。
describe('toBeCloseTo', () => {
it('works', () => {
// Passed!!!!
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});
});
toStrictEqual
toEqualとは基本的に同じです。以下の違いがあります。
- undefinedとなるkeyを持つobjectかどうかをチェックします
describe('toStrictEqual', () => {
it('works', () => {
expect({ a: 1, b: undefined }).toEqual({ a: 1 });
expect({ a: 1, b: undefined }).not.toStrictEqual({ a: 1 });
});
});
- 配列にundefinedが含まれるかどうかをチェックします
describe('toStrictEqual', () => {
it('works', () => {
expect([1, undefined]).toEqual([1]);
expect([1, undefined]).not.toStrictEqual([1]);
});
});
- objectのtypeが等しいかどうかをチェックします
例えば、フィールドa
とb
を持つクラスのインスタンスは、フィールドa
とb
を持つobjectと等しくなりません。
class Stock {
constructor(type) {
this.type = type;
}
}
describe('toStrictEqual', () => {
test('structurally the same, but semantically different', () => {
expect(new Stock('apples')).toEqual({ type: 'apples' });
expect(new Stock('apples')).not.toStrictEqual({ type: 'apples' });
});
});
Discussion