日々常々

ふつうのプログラマがあたりまえにしたいこと。

「自動受け入れテスト」を考えてみる

きっかけは XP祭り関西2013 の @StoneGuitar777 さんのLTからです。

「継続的デリバリー」に貼付けた付箋を抜き出してみる

【大阪】継続的デリバリー読書会(8回目) - connpassの範囲でもありました。

  • 受け入れ=ビジネス的な受け入れ基準=ユーザーの価値
  • ユニットテストとの色分け
  • うまくやらないとコストが高すぎる
    • 適切に作成して保守すれば自動のほうがはるかに安上がりになる
  • ユニットテストコンポーネントテストではどれほど包括的にやっても検出出来ない問題がある
  • 手動テストはアプリケーションの複雑さに関わらずきわめて高くつく
    • 手動では "頻繁なリリース" が実質的に不可能
    • 簡単操作でも人間が実施するものは高コストと捉えられる?
  • 受け入れテストが後になるとそこで見つかった問題の修正に十分な時間が割けなくなる
    • 修正に時間がかかる "起こっちゃいけない問題" を(意識するしないは別にして)テスト対象から避けるフォースが働きかねない
  • "ユーザーの求める価値" はテストは受け入れテスト以外でテストされない
  • シナリオにおける欠陥が見つけられる←これが目的
  • アプリケーションの大規模な変更でも受け入れテストは変更されない
    • ユニットテストコンポーネントテストはドメインにあわせて大幅に変更される
    • ここでは "ユーザーの価値に影響を与えない大規模な変更" で、価値が揺らぐなら受け入れテスト自体もがらっと変わるはず
  • "協業" を促進する
    • これに本当に価値を見るかが重要かなと
  • 自動受け入れテストを使う戦略では良い構成になりやすい
    • アーキテクチャレベルからテスタビリティを考える必要が出てくる
    • (UIレイヤーが薄いなど)一般的に良いと言われている構造に寄る
  • 受け入れテストスイートはドメインの言葉を使い、アプリケーションとのやり取りを含まない
    • アプリケーションとのやりとりはアプリケーションドライバレイヤに押し込める
  • 記録再生型のせいで自動受け入れテストが高くつくと言う汚名を着せられている
  • 質問に対する答えが知りたいのであって、操作に対する反応を知りたいのではない
  • 本当はGUIを使いたいけど、使いにくい
    • 頻繁に変わるし、シナリオのセットアップ、結果へアクセスする処理が複雑になる
  • PDSを守るように」というプレッシャーが働く
    • 守れていれば使いやすいUIレイヤの下に対するテストが書けるようになる
    • アプリケーションを動かすためのAPIが用意されたりする
  • テスターのテスティングは要件が満たされている事が確認されてから行われる
  • 自動受け入れテストは実行可能な仕様
    • 仕様書は陳腐化するが、実行可能な仕様は陳腐化しない
  • チーム全員(アナリストや顧客も含めて)が協業できるようにドメインの言葉のみで書く
  • プロダクションデータの誘惑に負けない
    • やってしまいがちだけど……
    • 膨大なプロダクションデータを使ってもパターンが足りてないなんて結構あるし
  • アプリケーションの公開APIを使って適切な状態にする
    • 直接データベースに投入せず、テストデータの投入もアプリケーションから行う
  • 正しく動くとわかっている開始地点を確立する
    • 出来ないなら「期待通りじゃなかったら即座に失敗させる」など防衛的に作る
  • テストしやすくするために設計を変える必要がある……までは正しい
    • テストのためにバックドアを作るは間違い
    • 「DBを直接参照する」もある意味バックドアだよなーと(ユーザーからは見れないわけだし)
  • 自動受け入れテストとユーザー受け入れテストとは違う
    • 自動受け入れテストはテストダブルを使用するなど「制御可能な環境」で行う
    • 外部の依存対象による影響を最少にする
      • 一方で問題を早期に発見するのが目標という矛盾がある
  • 外部との統合点を注意深く、効率的にテストする
    • 発生しうる問題を検出するテストで挑む→完璧ではないが効果が高い
    • "収穫逓減の法則": コストと収穫はある程度までは比例するが……
  • 受け入れテストに曖昧な余地はない
    • 受け入れテストが通ると言うことは次のステージに進めていいと言うこと
  • 開発プロセスをスムーズに進めるには厳しく複雑な受け入れテストを動かし続けなければならない
    • そこに投資すると言う判断が出来るか?
  • 受け入れテストにはデプロイメントテスト(スモークテスト)が含まれる
  • 受け入れテストステージは時間がかかってもいい
    • 短くする事も出来る。適度に。

結局「自動受け入れテスト」って?

上の箇条書きの強調部分を無理矢理まとめると「自動受け入れテストは、シナリオに対して行うもので、操作に対する反応ではなくビジネスの価値を検証するものであり、GUIに対してするのが理想だけど出来ないならAPI直叩きでも良い」って感じでしょうか。PDSなど内部品質への影響はビジネスの価値って文脈なのであえてスルーしておきます。
そんなこんなで私の解釈は「ユーザー受け入れテストの露払い」だったりします。高コストなユーザー受け入れテストがつまらない不具合でやり直しになったりするのは勿体ないので、自動で確認出来る部分は自動でやっとこうぜ、くらいのノリ。だからGUIじゃなくてAPI経由もそれが妥当ならそれで良いし、テストダブルについても同様。「問題を早期発見したいけどAPIやテストダブル使ったら見つけられない部分もあるじゃん意味ないじゃん」と言う矛盾とはそんな所で折り合いを付けておきます。物凄く軽く書いたので、ややもすれば自動テストを軽視しているように見えるかもしれませんが、私にとっての自動テストは基本的に手動テストを一発でOKにするためのものだったりするので、これでいいんです。「手動テストが必ず一発でOKになる」とかになれば、そのテストは徐々に省略されて行くだろうし。
あとはテスト自動化の本命でもあるリグレッション検出について。自動化すれば「はるかに安上がり」とか書かれていたけど、それは真面目に都度都度手動テストを行った場合の話。実際はコスト見合いやなぁなぁなどで手動テストが省略されたりする事がまかり通っていて*1、天秤にかけるのは非常に難しくなります。自動テストによりトラブルが起こる前に対処してしまうと、トラブルの対処にかかるコストはかからないので、これまた測るのが難しくなります*2。この辺は少ない労力で多くの効果を得るために、ピンポイントで網を張るような感じで進めるのが良いのかなーとか。
もう一つ、ドメインの言葉で書くだとかアプリケーションドライバレイヤとかについて。ここがあまりに複雑化していくと、アプリケーションに書かれているドメインロジックのようなもの*3 *4がここにも出てきてDRYじゃなくなる感じがしてる。実際そこまで作り込んだことが無いのでなんともなのだけど、SeleniumのPageObjectくらいが丁度いいバランスじゃないかなーと思ってたり。

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

*1:ダメなんですけど。

*2:TABOKではこの場合のROIも計算出来るような事も書かれていましたが、正直まだ理解出来ていません……orz

*3:逆転させたものの一部かな。アプリケーション側はUIの操作をドメインの言葉に変換してドメインオブジェクトに働きかけるもので、テスト側ではドメインの言葉をUIの操作に変換する感じになるわけだから。

*4:私はユビキタス言語を使う価値は「翻訳による情報劣化を減らすために翻訳回数を減らす」にあると思っているので、テストのために二回翻訳するとかなんかヤダ。