テスト自動化の戦略
どのテストを自動化すべきか?
- テストは大きく以下の2つのカテゴリに分類できる。
- 機能ごとのテスト:ある入力に対してSUTがどのように振る舞うかを検証するもの
- 機能横断のテスト:機能を横断してシステムの様々な振る舞いを検証するもの
機能ごとのテスト
Customer Tests
- Customer tests は、ビジネス視点でシステムやアプリケーション全体の振る舞いを検証する。
- functional tests, acceptance tests, end-user tests などと呼ばれる。
- テストは開発者によって自動化されることが多いが、エンドユーザが(テストの記述が読めなかったとしても)テストによって特定された振る舞いを仕様として理解できるべき。
- 様々なツールで自動化すべき。
Unit Tests
- Unit tests は、開発者視点で1つのクラスやメソッドの振る舞いを検証する。
- 検証する振る舞いは、必ずしも要求事項と直接関連があるとは限らない。
- これらのテストは、開発者によって記述され、開発者がコードの振る舞いを把握するのに役立つ。
- xUnitによって自動化すべき。
Component Tests
- Component tets は、いくつかのサービスが提供するクラス群からなるコンポーネントの振る舞いをアーキテクチャ視点で検証する。
- Unit tests と Customer tests の中間に位置し、 integration tests, subsystem tests などと呼ばれる。
- xUnitによって自動化すべき。
Fault Insertion Tests
- Fault insertion tests は、欠陥や障害を与えた際の振る舞いを検証する。
- 上記の3種類のテストであらわれるテストであるが、テスト自動化の視点では、上記の3種類のテストとは別のテストセットとしている。
- アプリケーションのある部分を置き換えることなく障害を与えるということを自動化することは難しい。
機能横断のテスト
Property Tests
- パフォーマンステストや、セキュリティテストなどの非機能テストが該当する。
- 人手のテストが困難であるため自動化すべきだが、xUnit framework は適さないことが多く、それ用の特別なツールを使うべき。
- アジャイル開発では、ある程度設計が固まった早い段階からこれらのテストを実行できる。プロジェクトを通して、新しい機能が追加されるごとにこれらのテストを継続的に実施できる。
Usability Tests
- Usability tests は、実際のユーザがソフトウェアを利用できるかを確認することで"fitness for purpose"を検証する。
- 人間が主観的に評価する必要があるため自動化は非常に難しく人手で確認すべき。
Exploratory Testing
- Explorattory testing は、プロダクトが首尾一貫したものになっているかを確認する方法。
- テスターがプラダクトを使い、振る舞いを観測し、仮設を立てて、テストをデザインし、仮設が正しいかを検証する。
- テスト前にSUTをセットアップする部分は自動化できるが、人間が考えながらテストするため自動化できない。
どのテストをどのツールを使って自動化すべきか?
(一般的な話なので割愛)
どのテストフィクスチャー戦略を使うべきか?
フィクスチャーとは
- テストフィクスチャー(または フィクスチャー) とは、"テストの事前条件" という意味。
- テストケースクラスとは、テストメソッドやテストフィクスチャーをセットアップするのに必要なコードを含んだクラスという意味。
Transient Fresh Fixture
- Transient Fresh Fixture とは、メモリ上にだけ存在しテスト終了後には消えてなくなるフィクスチャ。
- Set Up Code を必要とする。Teard Down Code や Setupt/Teardown Triggering は、必要となしない。
- フィクスチャーが1つのテストで独立しているため、他のテストに影響を与えることがない。
- Shared Fixture や Persistent Fresh Fixture を使うよりはテスト実行に時間がかかることがある。
Persistent Fresh Fixture
- Persistent Fresh Fixture とは、1つの Test Method の処理を超えて永続しつづけるフィクスチャー。
- Set Up Code と Tear Down Code を必要とする。Setup/Teardown Triggeringは、必要としない。
- データベース、ファイルシステム、レイテンシーが高いシステムに依存する場合、Slow Tests になることがある。
- 対処法としては、Minimal Fixuture, Test Double, Immutable Shared Fixutureなどを活用する方法がある。
Shared Fixuture
- Shared Fixutre とは、多くのテスト間で故意に再利用して使われるフィクスチャー。
- Set Up Code, Tear Down Code, Setup/Teardown Triggering をともに必要とする。
- フィクスチャーのセットアップやテアーダウンの実行コストを抑えられる。
- テスト間の依存関係が増えるため、Interacting Tests, Unrepeatable Tests, TestRun War といった問題を生む欠点がある。
- Shared Fixture Setup の手法として以下がある。
- Prebuild Fixture
- Lazy Setup
- Detup Decorator
- Suite Fixture Setup
- Chained Tests
どのようにテスタビリティを保証するか?
Control Points と Observation Points
- テストはインターフェース(interaction points)を通してソフトウェアとやりとりをする。
- テストの観点では、このインターフェースには、control points と observation points がある。
- control points は、テストがソフトウェアに対して何かをするようにするポイントである。
- control points を通して、テストフィクスチャの設定しソフトウェアをある状態にする。また、control points を通して、SUTを実行する。
- observation points は、テストの妥当性を検証する際にテストがSUTの振る舞いについて確認するポイントである。
- obsevation points は、SUTやDOCのテスト後の結果を取得するのに使われる。
- control points も observation points もSUTの同期メソッド呼び出しとして提供される。このことを "going in the front door" と呼ぶ。いくつかの interaction points では "back door" を通して利用することがあり、このことを Back Door Manipulation と呼ぶ。
Interaction Styles と Testability Patterns
- ソフトウェアのある部分をテストするときに、基本的なテスト方法として round-trip test と layer-crossing test がある。
- round-trip test は、SUTのパブリックインターフェース(つまり、front door)を通してやりとりする方法。DOC を Fake Object で置き換える場合もある。この方法は、カプセル化に従っているのでシンプルである。パブリックインターフェースさえ知っていればよく、SUTがどのようにビルドされたかを知る必要性はない。
- layer-crossing testは、APIを通してSUTとやりとりするだけでなく、Test Spy や Mock Object といった Test Double を使って back door でどんなやりとりがあるかを確認する。これは、強力なテストテクニックではあるが、ソフトウェアの変更にともない Oberspecified Software の結果を招く可能性がある。
- layer-crossing testを書くときは、SUTが依存するコンポーネントに対して代わりとなる dpendency mechanism でビルドする必要がある。例えば、Dependency Injection, Dependency Lookup (Object Factory / Service Locator)などである。別の手法としては、Test-Specific Subclass を利用する方法や、Test Hook を利用する方法がある。Test Hooks は、Test Logic in Production を引き起こすため注意が必要。
- 非同期テストの場合は、Humble Executable という手法がある。
//