Jestで「Received: serializes to the same string」となりテストが失敗する原因

Jestでテストを実行した際に「Received: serializes to the same string」となりテストが失敗することがあります。「Received: serializes to the same string」はJestでオブジェクトやインスタンスの比較を行うときに発生することが多いメッセージです。

本記事では、「Received: serializes to the same string」の原因と解決方法を、サンプルコードを使ってわかりやすく解説します。

「Received: serializes to the same string」の原因

「Received: serializes to the same string」は、Jestが比較しているオブジェクトやインスタンスが同じ内容を持っているにも関わらず、toBe()を使用しているために発生します。

toBe()は「参照の一致」をチェックしており、プリミティブな値(文字列、数値など)を比較する際に使用するマッチャーです。オブジェクトや配列は、同じ内容を持っていても、別々に定義されると異なる参照を持つため、toBe()での比較は失敗します。

では実際にサンプルコードを見てみましょう。

test('オブジェクトの比較', () => {
  const obj1 = { key: 'value' };
  const obj2 = { key: 'value' };

  expect(obj1).toBe(obj2); // テスト失敗
});

このテストを実行すると、次のようなエラーメッセージが表示され、テストは失敗します。

  × オブジェクトの比較 (8 ms)

  ● オブジェクトの比較

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

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

    Expected: {"key": "value"}
    Received: serializes to the same string

      3 |   const obj2 = { key: 'value' };
      4 |
    > 5 |   expect(obj1).toBe(obj2); // テスト失敗
        |                ^
      6 | });
      7 |

      at Object.toBe (sample.test.js:5:16)

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

obj1obj2はオブジェクトの参照が異なるため、toBe()での比較は失敗します。また、obj1obj2シリアライズすると同じ文字列になるため、「Received: serializes to the same string」というメッセージが表示されます。

「Received: serializes to the same string」の解決方法

この問題を解決するには、オブジェクトの内容を比較するtoStrictEqual()またはtoEqual()を使用します。

先ほどテストを実行した際のメッセージにもIf it should pass with deep equality, replace "toBe" with "toStrictEqual"toStrictEqual()を使用してくださいと書いています。

解決策1: toStrictEqual()を使用する

toStrictEqual()は、オブジェクトや配列の内容を厳密に比較するマッチャーです。では実際にサンプルコードを見てみましょう。

test('オブジェクトの比較', () => {
  const obj1 = { key: 'value' };
  const obj2 = { key: 'value' };

  expect(obj1).toStrictEqual(obj2); // テスト成功
});

このテストを実行すると、以下のようになりテストが成功します。

 PASS  ./sample.test.js
  √ オブジェクトの比較 (3 ms)

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

解決策2: toEqual()を使用する

toEqual()もオブジェクトの内容を比較するマッチャーですが、toEqual()toStrictEqual()よりも柔軟で、undefinedのフィールドを無視して比較を行うマッチャーです。では実際にサンプルコードを見てみましょう。

test('オブジェクトの比較', () => {
  const obj1 = { key: 'value' };
  const obj2 = { key: 'value' };

  expect(obj1).toEqual(obj2); // テスト成功
});

このテストを実行すると、以下のようになりテストが成功します。

 PASS  ./sample.test.js
  √ オブジェクトの比較 (3 ms)

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

toEqual()とtoStrictEqual()の違い

toStrictEqual()toEqual()よりも厳密に値の比較を行っています。toEqual()に対して、以下の2点に違いがあります。

  • undefinedが指定されているプロパティも等価であるかチェックする
  • インスタンスの生成元クラスが同じであるかもチェックする

toStrictEqual()toEqual()の違いが分かるサンプルコードを以下に示します。

test('toStrictEqualとtoEqualの違い', () => {
  const obj1 = { key: 'value' };
  const obj2 = { key: 'value', extra: undefined };

  expect(obj1).toEqual(obj2); // テスト成功
  expect(obj1).toStrictEqual(obj2); // テスト失敗
});

toEqual()ではundefinedのフィールドが無視されるためテストは成功しますが、toStrictEqual()ではundefinedもチェック対象となるため、テストは失敗します。このように、toStrictEqual(value)は厳密な判定をしているので、厳密性が求められるテストの際には、toStrictEqual(value)を使うことをおすすめします。

あわせて読みたい

toEqual()toStrictEqual()の違い』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

解決策3: JSON.stringify()を使用する

toBe()toStrictEqual()またはtoEqual()に置き換える代わりに、オブジェクトをJSON形式に変換して文字列として比較することもできます。では実際にサンプルコードを見てみましょう。

test('JSON.stringifyを使った比較', () => {
  const obj1 = { key: 'value' };
  const obj2 = { key: 'value' };

  expect(JSON.stringify(obj1)).toBe(JSON.stringify(obj2)); // テスト成功
});

ただし、この方法では、オブジェクトのプロパティの順序が異なるとテストが失敗するため、注意が必要です。

test('プロパティの順序が異なる場合', () => {
  const obj1 = { key: 'value', anotherKey: 'anotherValue' };
  const obj2 = { anotherKey: 'anotherValue', key: 'value' };

  expect(JSON.stringify(obj1)).toBe(JSON.stringify(obj2)); // テスト失敗
});

インスタンスの比較

インスタンスを比較する際にも、「Received: serializes to the same string」となりテストが失敗することがあります。これは、インスタンスの内容が同じでも参照が異なるためです。では実際にサンプルコードを見てみましょう。

class MyClass {
  constructor(value) {
    this.value = value;
  }
}

test('クラスインスタンスの比較', () => {
  const instance1 = new MyClass('test');
  const instance2 = new MyClass('test');

  expect(instance1).toBe(instance2); // テスト失敗
});

このテストを実行すると、次のようなエラーメッセージが表示され、テストは失敗します。

  × クラスインスタンスの比較 (6 ms)

  ● クラスインスタンスの比較

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

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

    Expected: {"value": "test"}
    Received: serializes to the same string

       9 |   const instance2 = new MyClass('test');
      10 |
    > 11 |   expect(instance1).toBe(instance2); // テスト成功
         |                     ^
      12 | });
      13 |

      at Object.toBe (sample.test.js:11:21)

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

この場合も同様に、toStrictEqual()toEqual()JSON.stringify()を使って解決します。

class MyClass {
  constructor(value) {
    this.value = value;
  }
}

test('クラスインスタンスの比較', () => {
  const instance1 = new MyClass('test');
  const instance2 = new MyClass('test');

  expect(instance1).toStrictEqual(instance2); // テスト成功
});

instance1instance2が同じ内容を持っているため、toStrictEqual()での比較は成功します。

本記事のまとめ

この記事では、Jestでテストを実行した際に表示される「Received: serializes to the same string」について、以下の内容を説明しました。

  • 「Received: serializes to the same string」の原因
  • 「Received: serializes to the same string」の解決方法

お読み頂きありがとうございました。