📝
ユニットテストの可読性を上げる - Four-Phase Test
はじめに
- この記事ではFour-Phase Testというユニットテストの可読性向上のためのプラクティスを紹介します。
対象読者
- ユニットテストの可読性が悪くて困っている方
- ある程度ユニットテストを実装してきた方
Four-Phase Test とは
- Setup/Exercise/Verify/TearDownの4つのフェーズにわける手法です
- フェーズごとに役割をわけることにより、テストコードの可読性を向上させます。
- 以下にフェーズごとの説明やサンプルコードを記載していきます。
各フェーズの説明
フェーズ | テストステップ数 | 説明 |
---|---|---|
Setup | 0-n 行 | 前準備。テスト実行前の初期化などを行う |
Exercise | 1 行 | テスト対象の実行 |
Verify | 1-n 行 | テスト結果の確認 |
TearDown | 0-n 行 | データ・リソースの破棄 |
サンプルソースの動作環境
- Node.js - 14.x
- jest - 27.x
- TypeScript - 4.5.x
サンプルソース
以下にJest + TypeScriptでFour-Phase Testを意識したサンプルソースを記載します。
test.ts
class TestClass {
calc(a: number, b: number) {
return a + b;
}
destroy() {
// リソース破棄
}
}
describe('TestClass', () => {
it('calc', () => {
// setup
const obj = new TestClass();
// exercise
const actual = obj.calc(1, 2);
// verify
expect(actual).toBe(3);
// teardown
obj.destroy();
});
});
各フェーズのケース別対処法・メモ
Setup
- Setupが肥大化した場合
- Test Frameworkに共通的にSetupが出来る機能がある場合、そこに移動する。(Jestの場合は、beforeEach)
- Test Frameworkがパラメータテストを導入している場合、パラメータテストを導入出来ないか検討する。(Jestの場合は、it.each, test.each)
- 余計な設定をしていないか確認する。
- テスト対象メソッドの実装が悪いケースがあるので実装を見直す。
- TearDownにデータ削除処理を実装すると他のテストが失敗した時にデータ削除が行われない可能性があるため、データの初期化などはSetupに寄せた方が良い。
Exercise
- 1テストケース1回に収まらない場合、異なる観点でテストを実施したり、テストケースを詰め込んでいないか確認する。
- 複数回Exerciseを行いたい場合、パラメータテストを導入する。
- Verifyと含めて複数回Exerciseを行う場合、Assertion Rouletteというアンチパターンなので見直す。
Verify
- 0行にはせず、必ず1項目は確認する。
- Verifyが肥大化した場合
- 複数の観点でテストをしていないか確認する。
- テストケース外の内容を確認していないか確認する。(テストのメンテナンスコストの増加と修正と関係ない箇所が壊れやすくなるので注意が必要。)
- テスト対象メソッドの実装が色々やりすぎてる可能性があるので、実装を見直す。
- カスタムアサーションにするか検討する。
TearDown
- Setupで生成したリソースを破棄する。
- 主にファイルのストリームやDBコネクションなどをcloseするなど。
- GCで破棄されないアンマネージドなオブジェクトもここで破棄する。
- Test Frameworkに共通的にTearDownが出来る機能がある場合、そこに移動する。(Jestの場合は、afterEach)
- Setupで各種初期化を行っている場合は、TearDownの実装を極力少なくする。
おわりに
- Four-Phase Testのプラクティスを導入することで、テストコードの書き方が統一されるのでチーム内で一定品質のテストコードが書けるようになり便利です。
- 他のプラクティスと併用することで、メンテナンスがしやすいテストコードが書けるようになるので積極的にプラクティスを取り入れていきましょう。
Discussion