【定規】自動テストの分類に「テストサイズ」の概念を取り入れてみた


こんにちは!クラウドソリューション開発部の西谷です。今回は、チームで取り入れた「テストサイズ」の概念を紹介します。

はじめに

皆さん、普段開発をしていて「テストの定義」でチームメンバーと認識がズレたことはありませんか?
最近私が担当しているプロジェクトで、脱Excelの試験書を目指し、テストコードをもっと書いていこう!という話になりました。
しかし、いざテストコードを書き始めると、「これって単体テスト?それとも結合テスト?」という議論が頻発…
私自身、この「定義のズレ」に直面して四苦八苦していたのですが、そんな中「テストサイズ」の概念を導入したことで、チーム内の認識をすっきり揃えることができたので、今回はそれについて紹介します。

理想形「テストピラミッド」

まず、理想形から確認しましょう。
自動テストを実装するうえで、エンジニアが最終的に目指したいのは、「テストピラミッド」ではないでしょうか?
テストピラミッドとは、下から、単体テスト、結合テスト、E2Eテストの順でテストの量を減らしていくことで、コストとスピードのバランスを考慮し、異なる種類のテストを効率的に配置するための概念です。

私たちもこのピラミッドを建設すべく、「よし!まずは単体だ!」。
そう鼻息を荒くして、私たちはテストコードの実装に着手しました。
ところが、困ったことが起きます。

これって単体?結合?E2E?

何と、「単体テスト」という言葉を1つとっても、メンバー全員で認識が異なることに気が付きます。

  • 「コンポーネント同士を繋いだテストって、単体?結合?」
  • 「複数のプログラムが連携したら結合だよね?」
  • 「え、でもデータベースや外部通信まで通して確認するなら、それってE2Eテストって呼ぶべきじゃないの?」

時間があれば、JSTQBのシラバスを全員で読み込んで、これは何のテストと呼ぶかということについて議論するのも良いでしょう。
しかし実際は工数が決まっているので、こんなことに時間を投入するわけにはいきません。

一方で、「工数がもったいない!とりあえずE2Eテストすれば全部網羅できるだろ!」となってしまうかもしれません。
一度こういった議論にもなったので、本当にE2Eで良いのか、私たちはこれについても検討してみました。

アンチパターン:アイスクリームコーン

色々調べましたが、E2Eだけで網羅しようとするのは、「アイスクリームコーン」というアンチパターンです。

一見、「実際の操作を再現するんだから良いのでは?」と思いますよね。
ですがこれには以下のような重大な欠点があります。

  • テストの信頼性:NWの状況などに影響を受けてテストの結果が変わってしまう。実際は1つの関数がバグっていたという事実に気が付くまでに時間がかかる。
  • 実行速度:NWを介すので当然遅い。機能が増えれば増えるほど遅くなり、開発中に誰もテストを実行しなくなる。
  • 壊れやすい:DOMを更新したらテストが簡単に壊れるといったことが頻発。テストを信用しなくなる。
    等…

こういったことが積もりに積もって、「こんなのやるくらいなら手動でよくね?」となり、testsフォルダは消滅することでしょう。

大事なポイントは、「自動テストにおいて大は小を兼ねない」ということです。

そこで 「テストサイズ」という解決策

大きいサイズのテストですべてを網羅しようとするのはアンチパターンということが分かったかと思います。
ということはやはり「どのようなテストが、テストピラミッドのどの要素になるのか」を分類する必要があるということです。

この分類を解消するために私たちが辿り着いたのが、「テストサイズ」です。
このテストサイズとは「何をしているか(機能)」ではなく「どのリソースを使うか(制約)」で分類する考え方です。
私たちのチームでは、以下のようにシンプルにルール化しました。

特徴 Small (S) Medium (M) Large (L)
実行環境 単一プロセス内(メモリ上) 同一マシン内の複数プロセス 複数マシン、ネットワーク経由
外部アクセス 禁止(DB、ネットワーク、ファイル) 制限あり(ローカルDB等) 許可(実際の外部API等)

そしてそれぞれのサイズを、S=単体テスト、M=結合テスト、L=E2Eテストと定義することにしました。

取り入れて良かったポイント・気づき

1. 無駄な議論の排除

1つ目の大きな変化は、導入前の課題であった「これって、単体テスト?結合テスト?」という不毛な議論が完全に無くなったことです。
この「テストサイズ」の考え方を取り入れることで、「単体か結合か」を不必要に議論するのではなく、以下のように機械的・直感的に判断できるようになります。

  • 「権限の判定処理は、この関数を呼ぶだけでテストできるから Sサイズ だね」
  • 「APIのテストはローカルのDBと接続すれば確認できるから Mサイズ で実装しよう」
  • 「ボタンを押したときに画面へメッセージが表示されるか確かめたいから Lサイズ だね」

といった感じ。
チーム内の会話がこれだけでスムーズに噛み合うようになり、工数削減に繋がりました。

2. テストコードの「設計思想」を共有

これは実際に取り入れてみて後から気がついたのですが、サイズを決めることでチーム内のテストコードに関する設計思想が自然と揃っていくということが起きました。

  • 「Sサイズならモックを使うよね」
  • 「Mサイズならフィクスチャーを使うよね」

といったような、設計思想が暗黙的に共有されている状態になるので、コードレビューをする側も受ける側も、双方楽になりました。

3. 「共通の言葉」を定義することの重要性

今回の体験を通して一番大切だと感じたのは、「意思疎通のための共通言語を定義することの大切さ」という本質的な部分です。
サイズをS〜Lで定義すること自体が絶対的な正解というわけではないと思っています。
大事なのは、いちいち細かい部分で迷ったり議論したりせず、チーム全員がスムーズに意思疎通できる「約束事」を作ることだと実感しました。

チームの規模やプロダクトの性質によっては、XS〜XLまで細かく用意したほうがやりやすい場合もあるでしょうし、従来通り「単体・結合・総合」と呼ぶほうがしっくりくるケースもあると思います。

手法そのものを丸投げして導入するのではなく、「自分たちのチーム全員で認識をそろえるための共通言語をどう作るか?」を考えることこそが、今回私が気づいた最も大切なことです。

まとめ

「単体テスト」「結合テスト」という言葉の揺らぎに悩んでいた私にとって、テストサイズ(S / M / L)という明確な軸を取り入れたことは、チームの会話をスムーズにする大きな一歩になりました!

「テストの分類でメンバーと意見が噛み合わないな……」と感じている方は、ぜひチームのルール作りにテストサイズの概念を取り入れてみてください!開発スピードとテストの信頼性のバランスが格段に取りやすくなるはずです。

もっと詳しく知りたい方は、ぜひ『Googleのソフトウェアエンジニアリング』などの書籍もチェックしてみてくださいね。
Googleのソフトウェアエンジニアリング:https://www.oreilly.co.jp/books/9784873119656/

最後に

エコモットでは一緒にモノづくりをしていく仲間を募集中です。弊社に少しでも興味がある方、生成AIを使った開発に興味がある方はぜひ下記の採用ページをご覧ください!