きり丸の技術日記

技術・エンジニアのイベント・資格等はこちらにまとめる予定です

【JS】JestでJsonを比較する(特定の項目を無視する)

JavaJsonを比較する記事」を職場のSlackに貼ったところ、フロントでもjsonをいい感じに比較したいという依頼が来ました。

ちゃんとヒアリングしていないので、相手の環境を妄想する。

妄想環境


  • ソースコードは自動生成で、テストが一切ない
  • IN/OUTだけでもコスト低くテストしたい

妄想環境へ解決方法の模索


ソースコードが自動生成だという環境だと、あまりガッツリテストも書けないだろう。
書いても、破壊的修正が大量に発生しそうです。

wiremockやExpress等でモックサーバを立てておく。
モックサーバへのRequestBodyが期待通りではなかった場合にBadRequestを出す方法もあるが、サーバの上げ下げコストも考えるとちょっとイマイチ?
E2Eとしてならいいけど、自動化できるかといわれるとちょっと微妙。

まずは、Jsonを比較できる手段が大掛かりな環境構築も必要なく、できる方法があればよさそう。
フロント世界の話なので、JavaScriptで有名なテストフレームワークのJestでいい感じに比較できそう。

なので、JestでJsonの比較する方法を探してみました。

閑話休題Jsonの比較)


下記のRequestBodyを比較するようにする。

const response = 
{
    "animals": [
      "gorilla",
      "human"
    ],
    "generate_date": "2020091910011234",
    "update_date": "2020092010023456"
};

完全一致を行う


Requestと完全に一致させたJsonを用意して、Jestのexpect().toEqual()を使用する。

test('完全比較', () => {
  const expected =
  {
    "animals": [
      "gorilla",
      "human"
    ],
    "generate_date": "2020091910011234",
    "update_date": "2020092010023456"
  };
  expect(response).toEqual(expected);
});

特定の項目を完全無視する


無視したい項目に対して、expect.anything()を設定します。
これで、キーが不足していた場合はエラーだが、値は完全無視できます。

有効活用できる箇所:サーバー応答時間、ランダムな値等の中身に深い価値が無い場合。

test('特定の項目を完全に無視', () => {
  const expected =
  {
    "animals": [
      "gorilla",
      "human"
    ],
    "generate_date": expect.anything(),
    "update_date": expect.anything()
  };
  expect(response).toEqual(expected);
});

正規表現で特定の項目を無視する


正規表現で比較したい項目に対して、expect.stringMatching(value)を設定します。

完全に値の無視はしたくないが、もうちょっと強固に比較したい等に使用できます。

test('正規表現で特定の項目を比較', () => {
  const expected =
  {
    "animals": [
      "gorilla",
      "human"
    ],
    "generate_date": expect.stringMatching("2020091910.*"),
    "update_date": expect.stringMatching("2020091910.*") 
    // responseのupdate_dateは20200920なので、テスト失敗する
  };
  expect(response).toEqual(expected);
});

配列項目の順番は問いたくない


配列で比較するときによく使いそうです。

  • 「データの返却」はServer
  • 「データをどう表現するか。どうソートして見せるか」等はUI層

等々、責任が分かれている場合、データの順番は意識したくありません。

なので、配列の順番を意識せず、中身だけ確認したい場合はexpect.arrayContaining(array)を使用したほうが良さそうです。

ただし、こちらを使用する場合は配列のサイズも確認すべきです。
arrayContainingは「対象の配列に、対象のオブジェクトが存在するか」という確認方法です。

期待値(expect)が多い時は対象のオブジェクトが存在しないのでエラーになってくれますが、期待値の方が少ない場合はすべて満たせているので、問題にならなくなります。

余計なデータが送られてこないことも確認したほうが良いので、サイズも確認すべきでしょう。

test('順番は無視', () => {
  const expected =
  {
    // humanだけにしてもエラーにならない。
    "animals": expect.arrayContaining([
      "human",
      "gorilla"
    ]),
    "generate_date": expect.anything(),
    "update_date": expect.anything()
  };

  // arrayContainingつかうなら、サイズも見ておいたほうが良さそう
  expect(response.animals.length).toBe(2);
  expect(response).toEqual(expected);
});

終わりに


JSON (JavaScript Object Notation)って名前ですから、Javascriptと親和性高いだろう。
そう考えてはいましたが、Jestだけで簡単に比較できるとは思いませんでした。

Javaの場合は、skyscreamer.JSONAssertとかJUnitと異なる別ライブラリを使う必要があったので、JSでも別ライブラリが必要かと考えてました。

Jestは日本語の公式ページもあるので、ぜひ参考にしてください。

私が興味なくて紹介しなかったが、有効なのもあるはずなので公式見てください。


もしこの記事が役に立ったのであれば、はてぶ、Twitterでの記事の拡散、twitterのフォローもよろしくお願いします。

私の励みになります。

ソースコード


正確なソースコードGitHubを見てください。
requireを使って別ファイルから期待値や実値を読み込めるから、覚えると便利。

github.com

参考記事


過去記事:JavaJsonを比較する記事
https://nainaistar.hatenablog.com//entry/2020/06/15/Java%E3%81%A7Json%E3%82%92%E6%AF%94%E8%BC%83%E3%81%99%E3%82%8B%EF%BC%88%E7%89%B9%E5%AE%9A%E3%81%AE%E9%A0%85%E7%9B%AE%E3%82%92%E7%84%A1%E8%A6%96%E3%81%99%E3%82%8B%E3%82%84%E3%82%8A%E6%96%B9%EF%BC%89nainaistar.hatenablog.com

Jest公式
https://jes.js .io/docs/ja/expectjes.js .io

Jestの曖昧な比較
qiita.com