※ 時折こぼけを挟んでおります。
UnitTestを書く上で注意すべきこと
誰に向けた記事か
- プログラミングは基本的に行える(2~3年程の経験)
- まだアーキテクチャについては良く分かってない
つまり私
モチベーション
- クリーンアーキテクチャ 238p から引用
テスト容易性のための設計
- システムと強く結合したテストは、システムに合わせて変化する必要がある。システムコンポーネントに対する小さな変更であっても、結合した多くのシステムが壊れたり、変更が必要になったりする。中略。共通のシステムコンポーネントを変更すると、何百や何千というテストが壊れる可能性がある。これは、脆弱なテストの問題(Fragile Tests Problem)と呼ばれている。
- 脆弱なテストは、システムを硬直化させるという悪影響を及ぼす。システムを少し変更しただけで大量のテストが失敗するとなると、開発者は変更するのをためらうだろう。
なんてことだ。 UTを書けば良いシステムを作れているという信念に揺さぶりを掛けられてしまった。
新卒2年目の若輩者として 実装プラスUT = OK という図式で安心していたのに。
とはいえ脆弱なテストの問題
は、自分がUTを書いたり、システムの部品をコーディングする経験を積む中で、感覚としてなんとなく気づいていた箇所を明確に取り上げられてて、とても爽快だったし面と向き合いたいと思い、ここに自分なりの考えを書くことにした。
注意すべき要素
UTのお作法
理想: チームでポリシーを設ける
理由:
私個人がUTを書くにあたって注意している項目を列挙したい
- なるべくモックオブジェクトを用いる。
- UTで担保すべきは 外部仕様としての振る舞い > 内部仕様としての振る舞い
- DIを意識した実装。
1 について
下図でいう A のUTを書いている時はBをモックする。
なぜならAのUTにおいてB及びCの振る舞いでテストが壊れてほしくないため。(インターフェースの変更は仕方ない)
デメリットは単純に手間が掛かること。Aの依存先が多ければ多いほどUTの工数が多くなる。
しかし、そこで悪魔の囁きに負けてしまうと、依存先の変更に引きずられて改修コストが大きくなってしまう。
2 について
UT = 内部仕様書 となるようなケース設定がベストだと考えている。
しかし、外部仕様としての振る舞い(正確に言うならユースケースと呼ぶべきか)はユーザーがシステムを利用するパターンな訳だから、ユースケース外は考慮しなくても良いと考える。(もちろん非機能要件は守らないといけない)
3 について
一番基本的な要素で重要すぎる要素では?
プログラマーの端くれだった(ピュアだった)頃の自分にまっさきに伝えられるならこれだろう。
シングルトンオブジェクトなんてシステム制約や非機能要件以外の理由では用いるべきではないよね。
しかしながらUTを書かなければ気づけなかった要素であり、アーキテクチャへの入り口となる箇所だと思っている。
簡単に言えば DI を正しく行えていれば、テスタブル
だと言える。そう思いたい。
関連して気になるのは、関数型プログラミングのパラダイムだとこのパターンをどう表現しているんだろうか。
脆弱なテストの問題(Fragile Tests Problem)について
この問題に対して テストAPI
を作成するのが良い解決策だとクリーンアーキテクチャでは述べられている。
こちらの記事に分かりやすいサンプルを見つけたのでぜひ見てほしい。
(
AVOIDING FRAGILE TESTS WITH BETTER DESIGN
要するに、プロダクトコードとテストコードの価値はイコールであること。
両者の保守性が低いとそれだけ機能の追加・修正のコストの重みが増す。
(お作法の 2 で述べた内容を普遍的なUTから切り離したのがテストAPIな気がする。)
まとめ
- UTもプロダクト開発と同様に扱う必要がある
- テストAPIを設けるなどのテスト戦略を設定すること
- DI 大事だね
読んでくださった方、ありがとうございました。
誤りやツッコミありましたら遠慮せずお願いします。