Small Changes

GraphQLのqueryのテストに仕様的な観点もどうしても書きたかった

悩んだこと

最近、GraphQLのqueryのテストでどう書こうかとても悩んだので備忘録として残しておきます。

雑ですが下記のようなレスポンスを返すqueryがあったとします。

queryとしては下記が正しく返ってきているかをテストできれば問題ないとします。

{
  "data": {
    "testQuery": {
      "labels": [
        "ラベル1",
        "ラベル2",
        "ラベル3",
      ],
      "datasets": [
        {
          "label": "項目A",
          "data": [
            1,
            2,
            3
          ]
        },
        {
          "label": "項目B",
          "data": [
            1,
            2,
            3
          ]
        },
        ...
      ]
    }
  }
}

その場合、本来はデータを準備して、そのデータのときに、こういうレスポンスが返ってきているであろうということを確認するテストを書けば良いのですが、細かい仕様がたくさんありこのレスポンスを返すまでに内部ではprivateメソッド内で細かい処理が行われていました。

そのため、次の点で悩みました。

  • ①何かしらの改修でテストが落ちたときにどこの仕様で落ちたか調査に時間がかかる。

  • ②複雑なロジックのため、レスポンスのテストだけだと不安が残る。

特に、漠然とした不安が気になりどうすればいいのか悩みました。

考えたこと

色々調べていたところ、privateメソッドをわざわざテストするのはアンチパターンという記事を見つけました。

4. privateメソッドをわざわざテストしている これについてはコード例までは載せませんでしたが、これもよくあるやらかしです。publicメソッドは既にテストしていますし、その結果はprivateメソッドから生み出されたのですから、privateメソッドをテストする必要などありません。どうしてもprivateメソッドをテストしたいというのであれば、まずコードの方をもっと小さなクラスに分割してから、それらを個別にテストすることを検討しましょう。このような事態を避ける最良の方法は、メソッドを設計するときに「単一責任の原則」を用いることです。 https://techracho.bpsinc.jp/hachi8833/2018_04_19/55015

ここに書いてある通りだと思いました!

細かい仕様についてはprivateメソッドでやっていますが、その最終結果はpublicメソッドの結果に表れますし、publicメソッド自体はテストしているので、二重にテストをやっているみたいなことになると思います。

自分がやろうとしていることはアンチパターンだと分かったのですが、時間が経った後に複雑なロジックを思い出せるかも不安があったので、どうやってこの不安を解消しようか考えました。

結論

調べながら、Kent Beckさんという方のお言葉(正確なソースが不明なので間違っていたらすみません…)で「不安が退屈に変わるまでテストしよう」というお言葉があることを知りました。

本来は重複するという点でアンチパターンだと思いますが、後で不安になるのでコメント入れて仕様的な観点のテストも追加して対応しました。

context 'Aのとき' do
  # queryのレスポンスのテストと重複しますが、仕様を明確にするために追加
  it '細かい仕様のテスト1' do
    # 仕様のテストを書く
  end

  # queryのレスポンスのテストと重複しますが、仕様を明確にするために追加
  it '細かい仕様のテスト2' do
    # 仕様のテストを書く
  end

  it 'queryから正しいレスポンスが返ること' do
    expect(result).to eq(
      {
        "testQuery" => {
          "labels" => labels,
          "datasets" => datasets
        }
      }
    )
  end
end

2022/03/22 追記

上記のテストに対して、同じデータを使い回していたのですがRSpecではitのたびにletを実行し、データを都度作成するためオーバーヘッドが発生していました。そのため、仕様部分のテストは無駄にデータを作ることになっていたため最終的には削除することにしました。今後同じようなケースが出てきたときは仕様ごとに適切なデータを準備してそれに対してテストを書くようにしようと思いました。