- 私が普段実施しているバックエンドのテスト戦略の練り方をご紹介
- 特に、責務ごとに構成コンポーネントが分割されているシステムに適しています
システム開発において、切っても切り離せないテスト。
特に、サーバーで動作するバックエンドシステムの開発において、自分のなかで戦略の練り方が固まってきたため、将来への備忘として残しておきます。
この記事での「テスト戦略」の意味
要件の探索〜本番環境で安定運用させるまでの一連の開発フローにおいて
- どのようなテストを
- どの環境で実施するのか
を決めたものをテスト戦略と呼ぶことにします。利用するテストフレームワークや具体実装は範囲外です。
テスト戦略を練るタイミング
私の場合は、「具体的にどのようなサービスを使うのか」までを含めた「具体アーキテクチャ」を決めたあとに実施することが多いです。
例えば、AWS上にシステムを構成するのなら、LambdaやECSをどのような責務単位で配置するかを決めたあとです。
私の考え方は、「責務はなにか」に注力する方法のため、コンポーネントの責務単位がある程度固まったこのタイミングで実施しています。
テスト戦略の決め方フロー
大きく
- STEP1 テストの重みのバランス感をチームで合意する
- STEP2 責務が変わる範囲を見極める
- STEP3 責務境界ごとにテスト内容を判断する
- STEP4 テストの実行環境を決める
の流れで決めていきます。
STEP1 テストの重みのバランス感をチームで合意する
まずは、具体的な検討に入る前にチームとして、テストをどの程度の重みで実施するか荒く考えを合意しておきます。
テストにおいて重要なのは、自分たちにとって最もメリットのある重さを見極めることです。
過剰にテストを実装しすぎれば、初期実装とメンテナンスコストが過度にかかり、開発速度を悪化させます。逆にテストが少なすぎれば、バグが流出する可能性が高くなります。
一方、テストがあることで安心して実装できるので、速度が早くなるという面もあります。
これらを踏まえて、テストプロセスをどの程度の重さとするのか、まずはチームで荒く合意しておきましょう。
この段階は極めて粗い粒度の合意でOKです。
(インセプションデッキに出てくるトレードオフスライダーのようなものを作るとよいでしょう)
STEP2 責務が変わる範囲を見極める
つづいて、アーキテクチャ図をみながら、システムの中での責務範囲の切り替わりポイントを見極めていきます。
責務とはなにか
責務
= その単位に期待される振る舞い
です。
責務は、ブラックボックスの責務とホワイトボックスの責務に分けられます。
ブラックボックス
= 内部がどうなっているかを気にしない(みてはダメ)ホワイトボックス
= 内部がどうなっているかまで気にする(みてよい)
です。
例えば、それぞれ以下のようなものが該当します。
- ブラックボックスの責務
- 入力を与えると、期待した出力が得られる
- ホワイトボックスの責務
- ↑のすべてのブラックボックスの責務
- 自チーム管理の内部状態が変わる(DB状態など)
- 外部チーム管理の状態が変わる(API呼び出しで状態が記録されるなど)
それぞれのテストで確認したい項目は、これらの責務となります。
そのため、まずはシステム全体を通じて責務が切り替わるポイントがどこなのかを見定めていきます。
最小粒度の責務から少しずつ広げて考える
例えば、↓のようなアーキテクチャを考えてみましょう。
この場合、まずは最も小さい責務範囲である「単一関数」に目を向けます。
そこから段階的に範囲を拡大し、切り替わりポイントを洗っていきます。
例えば、このような感じです(考え方によって多少増減します)。
範囲を広げる際は、以下のようなポイントに着目するとよいです。
- 接続先のリソースが増える
- 接続先リソースの管理者が変わる
- 処理の呼び出し元が変わる
- 扱うAPIが単一から複数になる
STEP3 責務を書き出す
責務境界が明らかにできたら、続いてすべての責務単位について、ブラックボックス/ホワイトボックスそれぞれの考えられる責務をすべてあげていきます。
例えば、単一関数単位で言えば
- ブラックボックスの責務
- ある入力を与えられると、期待した出力を返す
- ホワイトボックスの責務
- 関数内で別の関数を期待される引数で呼んでいる
- 関数内で別の関数を期待される回数呼んでいる
- etc
のようになるでしょう。
STEP4 責務境界ごとにテスト内容を判断する
責務が書き出せたら、次はどの責務をテスト対象とするかをジャッジします。
なお、それぞれの責務範囲の外にあるブロックはテスト時にはモック化します。
全体として検査したい範囲をカバーできればよい
ここで重要なのが、個々の責務範囲単体で評価せず、全体としてどこまでカバーできているか、に着目することです。構築しているシステムで担保したい品質基準を、すべてのテストを合計したら満たせているかを軸にそれぞれ要否を判断していきます。
例えば、
- 今回作ったコンテナは責務が極めて単純なので、
単一コンテナ単位のテスト
を都度作るのは過剰だな。自チーム管理をすべて連結した単位のテスト
でカバーすることにして、なしにしよう 複数関数単位のテスト
のうち、すべての関数をつなぐテストは単一コンテナ単位のテスト
の範囲と一緒だな。単一コンテナ単位のテスト
にまとめてしまおう
のような具合です。
ここの判断のバランスは、
- STEP1で決めたテストの重み
- チーム構成
- システムアーキテクチャ
に左右されます。
一部だけ実施するのはあり?
ブラックボックス/ホワイトボックスの一部だけを実施する、という判断ももちろんありです。
例えば、 単一コンテナ単位のテスト
は基本的に自チーム管理をすべて連結した単位のテスト
にまとめるけれど、接続先のDBが落ちているケースは自チーム管理をすべて連結した単位
だとやりづらい。DBが落ちている異常系だけは、単一コンテナ単位のテスト
を実施しよう。といった具合です。
STEP5 テストの実行環境を決める
このSTEPは、STEP5としていますが実際はSTEP4といったり来たりしながらになるかと思います。
実施することにしたテストを、自分たちの持っているどの環境でテストするのかを決定します。
よくある選択肢としては、
- ローカル環境(個人PC or CI基盤)
- 開発環境
- 検証環境
- 本番環境
といった具合です。ここでの開発/検証/本番環境は、実際のサーバー上だと考えてください。クラウドを利用しているのであれば、クラウド環境上です。
ここまで決めた内容を、チームで合意すればテスト戦略は決定です。
併せて、各テストにわかりやすい名前をつけておきましょう。
(おまけ) 意思決定をドキュメントに残しておこう
テスト戦略は、プロダクトの開発サイクルに大きな影響を与えるため、「なぜこのように決定したのか」は必ず残したほうがよいです。
おすすめは、議論の内容を文字起こししておいて、あとからまとめなおすことです。まとめだけだとあとから見返した際に知りたい小さな内容が漏れることがあるため、できれば文字起こしとまとめの両方を残しておけるとベストです。