OpenWork Tech Blog

社員クチコミサービスを運営しているオープンワークエンジニアによるテックブログです。

「機能テスト」でむやみに悩むのをやめたい。テストサイズの導入検討

はじめに

WEBエンジニアの生永です。私は手動テストが苦手です。

決められた項目を間違えずに実行し、そしてその結果に誤りがないかをチェックをする。とてもつもなく気を張る作業でできるならやりたくない。
ならば「自動テストを書いてしまえば」とはいえ、自動テストでどのようにテストするのがいいのかわからず迷い、結局自動テストではなく、手動テストにしてしまうということもなくはありません。

自動テストで真っ先に迷うポイント、それは自動テストの種類です。「機能テストって何?」といったようにテストの定義がよくわからないとなってしまいます。
システムテスト、結合テスト、コンポーネントテストなどなどそれぞれ分類されていたり、ただ呼び方が変わっているだけで中身は変わらないだったりと何を信じればいいかわかりません。
どの種類のテストで何をカバーしないといけないのかがわからず、結局また手動テストになってしまうなんてことも。

そこで、あえてこれらの呼び方をいったんやめて、テストサイズというものを使ってテストを分類、判別することでこの迷うという課題解決を試みました。

テストサイズ

テストサイズとは、Googleが提唱し、活用しているテスト分類手法のひとつです。
測定、評価できないものや直感に頼らないテストのデータに基づいた命名規則というテーマで分類されています。

Small Medium Large
ネットワークアクセス NG ローカルホストのみOK OK
データベースアクセス NG OK OK
ファイルシステムアクセス NG OK OK
外部システムアクセス NG 非推奨 OK
マルチスレッド NG OK OK
sleep文 NG OK OK
システムプロパティ NG OK OK
テスト実行制限時間(秒) 60 300 900以上

スモールテストはいわゆるユニットテスト、ミディアムテストはアプリケーション内部のレイヤー間の接続テスト、ラージテストはE2Eテストやシステムテストに相当するとされています。
また、スモールテストは1プロセスの中で完結できるテスト、ミディアムテストは1マシンの中で完結できるテスト、ラージテストは複数のマシンを使って良いテストとも言い換えることができます。
ちなみにGoogleではこれらの他にEnormous(巨大な)というテストカテゴリを用意しているともされていますが、まずは上記の3つでおおよそのテストをカバーできるはずです。

testing.googleblog.com

テストサイズの考えとSymfonyの共存

テストサイズをそのまますぐに活用し、社内で普及させられるとは限りませんでした。
弊社ではSymfonyフレームワークを使用しており、そのフレームワークで用意されているテストツールを利用しています。急に下手にテストサイズの概念だけを社内に普及しても中途半端になってしまい、かえって混乱を招きかねません。

既存のテストツールとこのテストサイズを無理なく調整して共存させる方法について考え、独自のテストサイズ定義を用意して、社内普及をさせてみるようにしました。

symfony.com

Small

フレームワークやシステム構成に依存しない純粋なロジックをテスト対象とし、一般的なユニットテストを実施します。
Symfonyが用意しているテストツールのUnit Testsを活用するようにしています。
ドメインロジックやプレゼンテーション層の出力制御などは主にここで検証されます。
ユニットテストとしては原則Sociable tests(古典学派単体テスト)を採用とし、よくあるユニットテストとインテグレーションテストの混乱解消を試みました。
モック、スタブは最低限としてできるだけ本番と同じような構成でテストが実施できることを心がけるようにします。

Medium

アプリケーション内の2つのレイヤーが適切に疎通できているかを中心としたテスト、いわゆる統合テストを実施します。
Symfonyが用意しているテストツールのIntegration Testsを活用するようにしています。
dockerコンテナを構築し、コンテナ内であれば1マシン(アプリケーション内)という扱いとして、Mediumサイズのテストの中でデータベースだけでなくElasticsearchも実体を使った疎通テストを可能にしています。
また、WEBアプリケーションフレームワーク依存となっている挙動もMediumサイズでテストをするようにしました。SymfonyのAutowiringによるDIやRouterによるURL生成の挙動チェックもここで行います。

Large

外部システム、マシンも利用可能とし、なるべく本番に近い形でテスト、機能テスト、システムテストを実施します。
Symfonyが用意しているテストツールのApplication Testsを活用するようにしています。
ハッピーパスとエッジケースを意識し、細かい検証はSmall、Mediumで行い、Largeでは振る舞いに問題がないかという点に絞ってテストケースを用意します。
リクエストとレスポンスチェックだけなく、ここでコマンドテストも行います。

E2E

網羅的ではないですがSeleniumテストも存在しています。本来はE2EテストがLargeサイズとして扱われますが、テストサイズ導入タイミングではいったんテストサイズとは別管理にしました。
テストサイズに馴染みがない中でひとつのサイズ内に複数のツールや判断基準が混ざることでテストの書き方の迷いが発生することを懸念した上での判断です。
まずは暫定的な判断基準で整理をし、ある程度揃い、普及したタイミングで見直しをかけることを前提として運用するようにしました。

Symfonyとの共存の工夫

SymfonyではIntegration Test用、Application Test用それぞれにスーパークラスが用意されています。
これまでこれらを活用してきており、それを継続して使うことを考慮して、テスト対象クラスに対してサイズごとに複数のテストクラスが作られても良いようにしました。
細かい検証はSmallで、フレームワーク依存の検証はMediumという形でわけ、開発者自身がそのクラスの責務と依存をはっきりと意識して実装ができるようにという狙いも含んでいます。

テストサイズ運用の追加利点

テストサイズでの運用ではテストの書き方に迷わない、「機能テストって?」というチーム内のコミュニーケーション齟齬が減るということだけでなく、CIでのテスト実行自動化の柔軟性向上にも寄与できます。
例えばSmallは、デプロイが不要であることは確かでありテスト実行時間も短いです。そのためSmallはビルド度やプッシュの度などに簡単なランタイム環境で確認ができます。一方、Largeはデプロイが必要になるため、プルリクエスト上やリリース前に実行すると切り分けられます。
また、テストサイズが大きいほどフレーキーであるということでもあるため、CIのステップを分け、Smallは確実にパスしなければならないが、Largeは失敗しても管理者が確認して問題なければスキップしても良いというようにCIルールも柔軟に対応することができるようになります。

自動テスト、機能テストと丸め込まれていたものを分割し、役割ごとに整理することでこのようにテスト運用自体に柔軟性を持たせることができるようになります。

最後に

E2Eの項目でも言及していますが、決してこのテスト区分方法が最終形態としてベストプラクティスだとは考えていません。
現在のメンバーの習熟度と現在のプロダクトの状態の中で踏み出せる第一歩目のテスト区分方法だと思っています。
Googleのテストサイズにおいても、初期段階ではSmall、Medium、Largeの3カテゴリだけでしたが、テスト拡充にしたがってEnormousが検討、追加されています。
同様に、メンバーの習熟度やプロダクト状況が変化するとともに今回整理したテスト区分をもとにまた最適化を続けていくことが必要です。

www.openwork.co.jp