JestでToBe()マッチャ−によるテストが「Received: serializes to the same string」となり失敗する

2021.11.11

こんにちは、CX事業本部 IoT事業部の若槻です。

Jestは、JavaScriptのテスティングフレームワークです。Facebookが開発しています。

今回は、JestでToBe()マッチャ−によるテストが「Received: serializes to the same string」となり失敗する際の対処についてです。

ToBe()によるテストが失敗する

下記のようなプロダクトコードとテストコードがあります。

  • プロダクトコード

src/lambda/sampleHandler.ts

export const handler = async () => {
  return { id: 'a001', value: 123 };
};
  • テストコード

test/handler.test.ts

import { handler } from '../src/lambda/sampleHandler';

test('test', async () => {
  const response = await handler();

  expect(response).toBe({ id: 'a001', value: 123 });
});

このテストをJestで実行すると、テストが失敗します。

$  npx jest
 FAIL  test/handler.test.ts
  ✕ handler (15 ms)

  ● handler

    expect(received).toBe(expected) // Object.is equality

    If it should pass with deep equality, replace "toBe" with "toStrictEqual"

    Expected: {"id": "a001", "value": 123}
    Received: serializes to the same string

       5 |   console.log(response);
       6 |
    >  7 |   expect(response).toBe({ id: 'a001', value: 123 }); //true
         |                    ^
       8 |
       9 |   //expect(response).toEqual({ id: 'a001', value: 123 }); //true
      10 |   //expect(response).toStrictEqual({ id: 'a001', value: 123 }); //true

      at Object.<anonymous> (test/handler.test.ts:7:20)

  console.log
    { id: 'a001', value: 123 }

      at Object.<anonymous> (test/handler.test.ts:5:11)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        3.243 s
Ran all test suites.

Expectedが{"id": "a001", "value": 123}であるのに対して、Receivedがserializes to the same stringとアンマッチとなっています。これはtoBe()は文字列型や数値型などのプリミティブ値を対象としたマッチャ−であるためです。上記ではオブジェクト型のため失敗しています。

対処 - toStrictEqual()を使う

この場合はtoStrictEqual()を使用します。先程のエラー中にIf it should pass with deep equality, replace "toBe" with "toStrictEqual"とあった通り、toBe()と置き換えるだけです。

test/handler.test.ts

import { handler } from '../src/lambda/sampleHandler';

test('test', async () => {
  const response = await handler();

  expect(response).toStrictEqual({ id: 'a001', value: 123 }); //成功
});

テストを実行すると成功します。

$  npx jest
 PASS  test/handler.test.ts
  ✓ test (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.224 s, estimated 4 s
Ran all test suites.

toEqual()とtoStrictEqual()の違い

ちなみにtoEqual()に置き換えた場合でもテストは成功します。

test/handler.test.ts

import { handler } from '../src/lambda/sampleHandler';

test('test', async () => {
  const response = await handler();

  expect(response).toEqual({ id: 'a001', value: 123 }); //成功
});

toEqual()toStrictEqual()はどちらもオブジェクトのマッチャ−として使用できます。両者の違いは、undefinedと値なしを同一とみなすかどうかです。

下記のように値がundefiedのキーを持つオブジェクトを指定した場合、toEqual()では無視されるため{ id: 'a001', value: 123 }と一致して成功しますが、toStrictEqual()では厳密に比較するため失敗します。

test/handler.test.ts

import { handler } from '../src/lambda/sampleHandler';

test('test', async () => {
  const response = await handler();

  expect(response).toEqual({ id: 'a001', value: 123, name: undefined }); //成功
  expect(response).toStrictEqual({ id: 'a001', value: 123, name: undefined }); //失敗
});

よってオブジェクトの比較には基本的には厳密な比較ができるtoStrictEqual()を使えば良さそうです。

JSON.stringifyを使う手もある

toBe()を他のマッチャ−に置き換えなくても、JSON.stringifyでexpect側とマッチャ−側の値をJsonシリアライズして文字列化することでも比較は一致しテストは成功となります。

test/handler.test.ts

import { handler } from '../src/lambda/sampleHandler';

test('test', async () => {
  const response = await handler();

  expect(JSON.stringify(response)).toBe(
    JSON.stringify({ id: 'a001', value: 123 })
  );//成功
});
test('test', async () => {
  const response = { value: 123, id: 'a001' }

  expect(JSON.stringify(response)).toBe(
    JSON.stringify({ id: 'a001', value: 123 })
  );//失敗
});

しかしこの場合は文字列の比較となるため、オブジェクト内の要素の順序が入れ替わって一致しない場合にはテストは失敗します。

$  npx jest
 FAIL  test/handler.test.ts
  ✕ test (6 ms)

  ● test

    expect(received).toBe(expected) // Object.is equality

    Expected: "{\"id\":\"a001\",\"value\":123}"
    Received: "{\"value\":123,\"id\":\"a001\"}"

      2 |   const response = { value: 123, id: 'a001' };
      3 |
    > 4 |   expect(JSON.stringify(response)).toBe(
        |                                    ^
      5 |     JSON.stringify({ id: 'a001', value: 123 })
      6 |   ); //失敗
      7 | });

      at Object.<anonymous> (test/handler.test.ts:4:36)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        2.796 s, estimated 4 s
Ran all test suites.

一方でtoEqual()toStrictEqual()の場合はオブジェクト内の要素の順序が異なっている場合でも成功します。一方で配列の場合は順序が異なれば失敗します。

まとめ

JestでToBe()マッチャ−によるテストが「Received: serializes to the same string」となり失敗する際の対処についてでした。

Jestでのオブジェクトの比較には基本的にはtoStrictEqual()マッチャーを使えば良さそうです。

以上