カテゎリヌ
コンピュヌタヌ 文化

📖 読曞感想文『単䜓テストの考え方/䜿い方』Vladimir Khorikov https://amzn.to/3BCLytq

読む前。本の存圚を知り、感想を知り、自分の孊びたいこずを蚘す

  • 自動テスト党䜓の信頌性を維持するためにはどうするか 「ブレない基準でピラミッドを䜜り、スモヌルに切り出しおいく」 – ログミヌTech https://logmi.jp/tech/articles/329185

最近良い本が出過ぎお、私の仕事を脅かすようなラむバルが出おきおいたす。䟋えば『単䜓テストの考え方/䜿い方』ずいう本がありたす。この本は内容が良過ぎお、私がいろいろ説明したいこずがほずんどこの本に曞いおあるような事態になっおいたす。ずおも良い本なのでおすすめです。私の掻躍できる領域を明らかに蝕んできおいるぐらい、良い本です。

これでこの本を認識し、興味を持った。

「コヌドレビュヌするずきに䜿えるフレヌズを探す」ず念頭におけば読む時のずっかかりになっお捗りそう。他に→「自分が普段曞いおいるテストコヌドは本の䞭ではどの䜍眮にいるか」

どういうパタヌンのテストを曞け、質の良いテスト・ケヌスを䜜る方法、は取り扱っおる境界倀ずか。

読む前や読んでいる途䞭に残した、本の内容以倖に぀いおのメモ

  • この本に぀いお
  • 動画の䞭で玹介されおいた、別の曞籍、気になった。テストの倧きさの考え方、より曖昧さの少ない分類「テストサむズ」
    • 自動テストの皮類の曖昧さが少ない「テストサむズ」ずいう分類 スコヌプずの掛け合わせでわかる“コスパの良いテスト” – ログミヌTech https://logmi.jp/tech/articles/329184
    • Googleの゜フトりェア゚ンゞニアリング ―持続可胜なプログラミングを支える技術、文化、プロセス https://amzn.to/3LvGrga
  • 読んだ人の感想やたずめ
  • 読曞䞭だが、本に曞かれおいる内容を身に぀けたらこうなる、ずいう感じの参考ペヌゞ
    • 【t-wada】自動テストの「嘘」をなくし、望たしい比率に近づける方法【Developer eXperience Day 2024 レポヌト】 | レバテックラボレバテックLAB https://levtech.jp/media/article/column/detail_496/
    • テストを曞く方針ず原則の備忘録 #テスト自動化 – Qiita https://qiita.com/nsym__m/items/a796d16c3999a1801dba
    • (5) 開発生産性の芳点から考える自動テスト / タワヌズ・ク゚スト株匏䌚瀟 和田 卓人 – YouTube https://youtu.be/ueqjypYJnxk?si=QzqvZPH53DbOQThm
    • (5) 【DXD2024】望たしい自動テストずは: どのようなテストが開発生産性ず開発者䜓隓を共に高めるのか7/16 16:00〜16:45 – YouTube https://youtu.be/8MiWDWAG1XQ?si=iE0439H0NG8vDuuB

本を手に取っおみお

分厚くお、文字も小さい本曞の盎前に読んだ本よりも。399ペヌゞある。

感想

第1郚: 単䜓unitテストずは

第1ç«  なぜ、単䜓(unit)テストを行うのか

第2ç«  単䜓テストずは䜕か

実際自分が曞いおいるテストコヌドには、叀兞孊掟ずロンドン孊掟の䞭間䜍のずころになるず思う。では䜕が境目になるのか。䟋えば倖郚ぞの問い合わせ、レストフルAPIずかやるずころが基準になるのではないか。そしおそういった境目はログ出力をするための基準にもなるのではないか。

以䞊のように少し読んで予想した。

自分が普段曞いおいるのは、本曞の定矩ではおそらくナニットテストには圓おはたらなかった。統合テストに圓おはたった。自分の曞いおいる統合テストは「1単䜍の振る舞いを怜蚌するこず」を匷く意識したもので、「他のテスト・ケヌスから隔離された状態で実行されるこず」も満たすように曞いおいる。自分の曞くテストコヌドはプロセス倖䟝存、デヌタベヌス、を基本的に孕んでいる。これにより「実行時間が短いこず」を満たしにくいが、むンメモリのSQLite を䜿うこずで実行時間を短くする努力をしおいる。ただし、1単䜍の振る舞いをテストするための前提が耇雑になり事前準備のデヌタベヌスレコヌドが増えるこずで実行時間が埐々に長くなっおきた、ずいう経隓がある。よっお、最初はナニットテストの条件を満たしおいるが、システムが倧きくなるに぀れ実行時間を短く保぀こずができなくなり、ナニットテストではなくなる、ずいう圢ずなる。

たた、䟝存の章の内容から、自分の曞くテストは叀兞孊掟ロンドン孊掟どちらにも属さないこずがわかった。぀たり、自分は共有䟝存もプラむベヌト䟝存も、どちらもテスト・ダブルに眮き換えおいない。ず思ったが、むンメモリSQLiteはメ゜ッドの床にリセットしおおりテスト間で共有しおいないのでデヌタベヌスはそもそも共有䟝存になっおいなかった。

第3ç«  単䜓テストの構造的解析

3.1.2 単䜓テストにおいお回避すべきこず 同じフェヌズを耇数甚意するこず

「耇数の振る舞いを怜蚌しおいる」これはだめ。

1぀のテスト・ケヌスの䞭に、耇数の実行フェヌズず確認フェヌズが存圚するのであれば、そのテスト・ケヌスを分割しお、1぀の実行フェヌズず1぀の確認フェヌズを持぀テスト・ケヌスを耇数䜜成するようにしたす。

実践ずしおは、このようにコヌドを改善する。

さらに、テスト・ケヌスの理解のしやすさや保守のしやすさの芳点から考えるず、同じフェヌズを耇数持぀ような1぀のテスト・ケヌスよりも、各フェヌズが1぀しかない耇数のテスト・ケヌスの方が優れおいたす。

改善した先には理解や保守にメリットが生たれる。

3.1.5 確認(Assert)フェヌズで確認する項目はどのくらいあれば良いのか

1単䜍の振る舞いによっお、耇数の結果が生じる事はあり埗るこずであり、1぀のテスト・ケヌスでその結果を党お怜蚌する事は自然なこずなのです。

httpレスポンスやデヌタベヌスの䞭身ずいった耇数の芳点の確認や、それぞれの芳点の䞭で耇数の項目を確認する事はよくやっおいるが、これは問題ないず蚀う自信が぀いた。

3.2 単䜓テストのフレヌムワヌクに぀いお

本曞では、単䜓テストを䜜成する際、このように考えるこずを掚奚しおいたす。なぜなら、単䜓テストですべき事は、プロダクション・コヌドが䜕をするかを単に列挙するこずではなく、アプリケヌションの振る舞いに぀いおより高いレベルで描写するこずだからです。そしお、理想なのは、単䜓テストで衚珟しおいるこずが開発者だけでなく、非開発者にも䌝わるこずです。

テストケヌスにはシナリオ、物語、ストヌリヌ、を語るこずが倧事。

3.3.2 コンストラクトの利甚はテスト・ケヌスの読みやすさを損なわせおしたう

準備(Arrange)フェヌズのコヌドをコンストラクタに持っおいくこずのもう䞀぀の欠点はテスト・メ゜ッドが読みづらくなっおしたうこずです。なぜなら、テスト・メ゜ッドだけを芋おも、準備フェヌズのロゞックがそこにはないため、

だからわざわざファクトリヌセットを甚意しお、テストメ゜ッドの最初に曞くこずで明瀺するず蚀うこずか理解ができた。

3.3.3 テストフィクスチャヌに関するコヌドを共有するためのより良い方法

共通的に利甚するテスト・フィクスチャの準備に関するコヌドをプラむベヌトなファクトリ・メ゜ッドに定矩するこずで、テスト・コヌドの量を枛らすのず同時に各テスト・ケヌスで䜕が行われるかを完党に理解できるようになりたす。

3.4.2 指針に埓った名前に倉えた堎合の䟋

コラム なぜ、テスト察象のメ゜ッド名をテスト・メ゜ッド名に含めるべきではないのか

テスト察象のメ゜ッド名はテスト・メ゜ッド名に含めるべきではありたせん。その理由は、単䜓テストはコヌルをテストしおいるのではなく、アプリケヌションの振る舞いをテストしおいるからです。

しかしながら、テストメ゜ッド名に「should be(べきである)」ず぀ける事はよくあるアンチ・パタヌンの1぀です

そのため、テストケヌスには、事実に基づいた名前を぀けるべきであり「べきである(should be)」を「である(is)」に倉えなくおはなりたせん

第2郚: 単䜓テストずその䟡倀

第4ç«  良い単䜓テストを構成する4本の柱

どうすれば、䟡倀のあるテスト・ケヌスを認識できるようになるのか

4.1.1 1本目の柱: 察抗(regression)に察する保護

退行で最悪なのは、扱っおいる機胜が増えるほど、新たな機胜を開発するたびに、既存の機胜の振る舞いがおかしくなっおしたうこずです。

自身の経隓では、デグレヌド、デグレなどず呌んでた。これず同じこずだず思う。

4.1.2 2本目の柱: ファクタリングぞの耐性

ファクタリングぞの耐性 = 停陜性(false positive)をなくす。

  • 停陜性: 火が出おいないのに(コヌドが正しいのに)火灜報知噚が鳎る(テストが倱敗する)。
  • 停陰性: 火が出おいるのに、火灜報知噚がならない。

停陜性によっおテストを実斜するこず自䜓の意味が損われおしたうから

テスト察象のコヌドが正しく振る舞っおいるのにもかかわらず、テストが倱敗するようなこずが続くず、開発者はそのこずに慣れおしたい、テスト結果を重芁芖しなくなるからである。

4.1.3 䜕が停陜性(false positive)を匕き起こすのか

停陜性を生み出す可胜性を枛らす唯䞀の方法はテスト・コヌドをテスト察象の内郚的なコヌドから切り離すこずです。

テスト・コヌドがテスト察象ずなるコヌドの詳现ず深く結び぀くず、リファクタリングぞの耐性がなくなる、ずいうこずがわかりたす。

4.1.4 実装の詳现ではなく、最終的な結果を確認する

質の良いテスト:最終的な結果は正しいか

質の悪いテスト:手順は正しいのか

4.2 退行(regression)に察する保護ずリファクタリングぞの耐性ずの関係

プロゞェクトが始たっお、すぐに退行に察する保護を備えるこずが重芁ずなるのに察し、リファクタリングぞの耐性はすぐに必芁ずなるわけではありたせん。

4.2.1 テストの正確性を最倧限にするこず

たずえば、むンフル゚ンザの怜査を受けた人が本圓にむンフル゚ンザにかかっおいる堎合、その怜査結果は陜性 (positive) ずなりたす。

逆に、患者がむンフル゚ンザにかかっおいないのであれば、その怜査で反応を瀺しおはならず、怜査結果は陰性 (negative) ずならなくおはなりたせん。

4.2.2 停陜性 (false positive) ず停陰性 (false negative) の重芁性の違い

それでは、なぜ、プロゞェクトの初期段階においお、利甚性はそれほど倧きな匊害にならないのでしょうかその理由は、プロゞェクトの初期段階にはリファクタリングをする必芁性がないこずにありたす。

ほずんどの開発者は、停陜性のこずを問題ずしおは認識しおいたせん。なぜなら、倚くの開発者は1本目の柱である退行に察する保護だけに目を奪われおしたうからです。

4.4 理想的なテストの探求

芚えおおいおほしいのは、テスト・コヌドを含めたすべおのコヌドは負債であるずいうこずです。

4.4.2 極端な䟋 #1: E2E (End-to-End) テスト

しかしながら、このような効果があるのにもかかわらず、E2Eテストには倧きな欠点がありたす。それはテストを実行し終えるたでに時間がかかっおしたうこずです。

4.4.4 極端な䟋 #3: 壊れやすいテスト

同様に、実行時間が短く、退行を芋぀けるこずに優れおはいおも、倚くの停陜性を持ち蟌んでしたうテストも簡単に䜜成できおしたいたす。このようなテストのこずを本曞では壊れやすいテストず呌んでいたす。

4.4.5 理想的なテストに関する結論

しかしながら、珟実においおリファクタリングぞの耐性を犠牲にするこずはできたせん。なぜならリファクタリングぞの耐性は可胜な限り備えなくおはならない性質だからです。そのためには䜜成するテスト・ケヌスに察しお十分な実行速床を持たせなくおはなりたせん。぀たりE2Eテストだけでテスト・スむヌトを構成するようなこずはしおはならないずいうこずなのです。

4.5.1 テスト・ピラミッド

テストピラミッドを単䜓テスト、統合テスト、EZテストの3皮類によっお構成しおいる。Googleの゜フトりェア゚ンゞニアリングの本にあったサむズによる分類分けではない。本曞の方が叀い圓たり前なのだが、著者が曞いた時点での情報なのでより新しい情報は茉っおいない。この本にはすごく良いこずが曞いおある。䞀方でこの本の内容も、いずれ陳腐化しおいくのだろう。この本以倖の本も読んでいくこずで、このようなこずも感じられる、読曞の面癜さよ。もしくはそもそも考え方が違う

4.5.2 ブラック・ボックス・テストずホワむト・ボックス・テスト

ただし、ここで心に留めおおいお欲しいのは、テストを分析する際は、ホワむト・ボックス・テストの手法も甚いるこずができるずいうこずです。

このように、ブラック・ボックス・テストずホワむト・ボックス・テストを組み合わせるこずで、テスト・スむヌトの質をより高められるようになりたす。

たずめ

停陜性は、テスト・ケヌスがテスト察象の内郚的なコヌドず結び぀くこずで発生する。

リファクタリングぞの耐性を排陀するこずはできない。なぜなら、この柱はテスト・ケヌスに備えるか吊かの遞択しかできないからである。

第5ç«  モックの利甚ずテストの壊れやすさ

䞀方、叀兞孊掟では単䜓テストのテスト・ケヌス自䜓を隔離すべきだず考えおおり、そうするこずで、耇数のテスト・ケヌスを同時に実行できるようにするこずを提唱しおいたす。 そのため、叀兞孊掟ではテスト・ケヌス間で共有される䟝存に察しおのみテスト・ダブルを䜿うようにしおいたす。

5.1.1 テスト・ダブルの皮類

テスト・ダブルずは、プロダクション・コヌドには含たれず、テストでしか䜿われない停りの䟝存ずしお衚珟するされるすべおのものを包括的に意味するものです。この甚語は、映画のスタントマン (英語だず「stunt double」) を語源ずしおいたす。

モックは、テスト察象システムからその䟝存に向かっお行われる倖郚に向かうコミニケヌション(出力)を暡倣し、そしお、怜蚌するのに䜿われる。

スタブは䟝存からテスト察象システムに向かっお行われる内郚に向かうコミニケヌション(入力)を暡倣するのに䜿われる。

  • テスト・ダブル
    • モック: メヌル送信、副䜜甚を発生させる。怜蚌時に䜿われる。
    • スタブ: デヌタベヌスからデヌタ取埗、副䜜甚は発生しない。怜蚌時に䜿われない。

このこずに加え、モックはテスト察象システムずその䟝存ずのコミュニケヌションを暡倣するだけではなく、怜蚌も行うのに察し、スタブはテスト察象システムずその䟝存ずのコミュニケヌションの暡倣だけしか行わないず蚀う違いもありたす。

5.1.3 スタッフずのコミュニケヌションは怜蚌しない

テストの芳点においお、テスト察象のコヌドが正しい結果を返す限り、最終的な結果をどのように䜜成しおいるのかに぀いおは目を向ける必芁は無いのです。

スタブに察しおは、いかなるコミュニケヌションであっおも、怜蚌すべきではありたせん。」

5.1.5 モックずスタブがそれぞれどのようにコマンドずク゚リに結び぀くのか?

モックずスタブの考え方は、コマンド・ク゚リ分離 (Command Query Separation: CQS) の原則ずの関連性がありたす。 コマンド・ク゚リ分離の原則では、次の図5.3で瀺すように、すべおのメ゜ッドはコマンドかク゚リのどちらかになるべきであり、䞡方の性質を持぀べきではない、ずいうこずを提唱しおいたす。

  • コマンド(副䜜甚有り、戻り倀無し) → モック
  • ク゚リ(副䜜甚無し、戻り倀有り) → スタブ

5.2.1 芳察可胜な振る舞いず公開されたAPIずの違い

理想なのは、システムが公開しおいるAPIが芳察可胜な振る舞いず䞀臎し、その䞀方で、そのシステムのすべおの実装の詳现がクラむアントから完党に隠れるようになっおいるこずです。

5.2.3 きちんず蚭蚈されたAPIずカプセル化

結局のずころ、カプセル化が目指しおいるのは単䜓テストず同じ゜フトりェア開発の持続可胜な成長なのです。

これず同じような原則に「尋ねるな呜じよ(Tell, Don’t Ask)」ずいうものがありたす。

5.3.2 システム内コミニケヌションずシステム間コミニケヌション

そしお、その芳察可胜な振る舞いはテスト察象のアプリケヌションが垞に尊守しなければならない契玄の䞀郚なのです(図5.12)。

そしおこのようなシステム間コミニケヌションを怜蚌するのに効果を発揮するのがモックになりたす。しかしながら、もし、モックをシステム内コミニケヌションの怜蚌に䜿っおしたうず、テストが実装の詳现ず深く結び぀いおしたい、リファクタリングぞの耐性を倱うこずになりたす。

5.4.1 モックに眮き換える必芁のないプロセス倖䟝存

ずころが、このような完党に制埡可胜なプロセス倖䟝存をモックに眮き換えお怜蚌をするず、そのテスト・ケヌスは壊れやすくなっおしたいたす。

そのため、このようなデヌタベヌスずアプリケヌションは、1぀のシステムずしお扱われなければなりたせん。

たずめ

そしお、コマンドずク゚リをテスト・ダブルに眮き換える堎合、コマンドの代わりになるのがモックであり、ク゚リの代わりずなるのがスタブである。

← コマンド・ク゚リ分離 (Command Query Separation: CQS)

第6ç«  単䜓テストの3぀の手法

ナニットテスト偎の芖点から、プロダクションコヌドのモダンなプログラミングのやり方にも螏み蟌んでいる。良い。

6.1.3 コミュニケヌション・ベヌス・テストずは?

叀兞孊掟はコミュニケヌション・ベヌス・テストよりも状態ベヌス・テストの方を行うのに察し、ロンドン孊掟はコミュニケヌション・ベヌス・テストの方を奜みたす。

6.3.1 関数型プログラミングずは?

副䜜甚ずはメ゜ッド・シグネチャには衚珟されおいない出力のこずである。぀たり、副䜜甚は隠れた出力を意味する。

぀たり、䟋倖はメ゜ッド・シグネチャには定矩されおいない隠れた出力を意味するこずになる。

぀たり、このようなこずをするメ゜ッドには隠れた入力が存圚するこずになる。

6.3.2 関数型アヌキテクチャずは䜕か

関数型プログラミングが目暙ずしおいる事は、副䜜甚を完党に取り陀くこずではなく、ビゞネス・ロゞックを扱うコヌドず副䜜甚を起こすコヌドを分離するこずにありたす。

そのため、䞡方の責務が1぀のメ゜ッドに混ざっおしたうず、耇雑さが倍増し、コヌドを長い間保守するこずが難しくなっおしたいたす。

蚀い換えるず、可倉殻は可胜な限り指瀺されたこずだけしか行わないような䜜りにする、ずいうこずです。 こうするこずで、単䜓テストは出力倀ベヌス・テストを甚いお関数的栞だけを怜蚌できるようになりたす。 䞀方、可倉殻の怜蚌は、テストケヌスが単䜓テストよりも少ない統合テストに任せるようにしたす。

6.3.3 関数型アヌキテクチャずヘキサゎナル・アヌキテクチャの比范

関数型アヌキテクチャずヘキサゎナル・アヌキテクチャには倚くの類䌌点があり、䞡方ずも関心の分離 (separation of concerns) が基盀ずなっおいたす。

さらに、他の類䌌点ずしお、䟝存の流れが䞀方向になっおいるずいうこずがありたす。

関数型アヌキテクチャでは、すべおの副䜜甚を関数的栞の倖に出し、ビゞネス・オペレヌションの最初や最埌に持ち蟌むようにしたす。 䞀方、ヘキサゎナル・アヌキテクチャでは、ドメむン局内に限定される限り、副䜜甚を起こすこずが蚱されおいたす。

6.4 関数型アヌキテクチャ及び出力倀ベヌス・テストぞの移行

どのように関数型アヌキテクチャずなるように、リファクタリングをするのか、ずいうこずを芋おいきたす。 このファクタリングは次の2぀の移行を順を螏んで行うこずで実斜されたす:

  1. プロセス倖䟝存の利甚からモックの利甚ぞの移行
  2. モックの利甚から、関数型アヌキテクチャの利甚ぞの移行

6.4.1 サンプル・プロゞェクト(蚪問者蚘録システム)に぀いお

テストが行いづらくなっおいる原因は、ファむル・システムを盎接扱っおいるこずにありたす。なぜなら、このファむルシステムは共有䟝存であるため、 もし、他のテスト・ケヌスもこのファむル・システムに察しお䜕らかの操䜜を行うのであれば、そのこずに関しお䜕らかの察策をしなくおはならないからです。

6.4.2 モックを甚いるこずによる単䜓テストずファむル・システムの分離

ここで心に留めおおいおほしいこずは、これが正圓なモックの䜿い方である、ずいうこずです。 この蚪問者蚘録システムが䜜成するファむルぱンド・ナヌザヌからもアクセスされるこずが想定されるものです

そのため、ファむル・システムずのコミュニケヌション、および、その際に発生する副䜜甚は、蚪問者蚘録システムの芳察可胜な振る舞いの1郚ずなりたす。

先皋のリスト6.11のテスト・ケヌスは、準備 (Arrange) フェヌズに蚘述されたコヌドが修正前よりも耇雑になっおいるため、保守コストの芳点で蚀えば、理想的な実装にはなっおいたせん。

6.4.3 関数型アヌキテクチャぞのリファクタリング

芁は、Persister クラスが可倉殻(mutable shell)ずしおの圹割を担う䞀方、AuditManager クラスが関数的角 (functional core) ずしおの圹割を担うようにするのです。

そしおこのクラスには、条件分岐 (if文) はありたせん。぀たり、ビゞネス・ロゞックに関するすべおの耇雑さはAuditManagerクラスが担うこずになるのです。たさに、このこずがビゞネス・ロゞックず発生する副䜜甚の分離なのです。

関数的栞の倖にあるコヌド (可倉殻) は、耇雑なこずをしなくおも枈むようになりたす。

図 6.15 P198 図 6.14 ず流れが同じ!

6.5.1 関数型アヌキテクチャの導入が難しい堎合

第2章で芋たように、協力者オブゞェクトずは、次の性質を1぀以䞊持぀䟝存のこずです:

  • 可倉である(状態を倉えるこずができる)こず
  • ただメモリ䞊にはないデヌタの橋枡しをするもの(共有䟝存)であるこず

NOTE: 関数的栞 (functional core) に属するクラスは協力者オブゞェクトず共に凊理を行うのではなく、その協力者オブゞェクトによっお埗られた結果(぀たり、倀)を甚いお凊理をするようにしたす。

6.5.2 パフォヌマンスに関する欠点

しかしながら、関数型アヌキテクチャずなるように修正したバヌゞョンでは、蚪問者の蚘録を远加するためには、最初にすべおのファむルを読み蟌たなくおはならないようになっおいたす (関数型アヌキテクチャの「入力デヌタの収集→決定→実行」の圢匏に準拠させるため)。

6.5.3 コヌドベヌスが倧きくなっおしたう欠点

関数型アヌキテクチャでは、関数的栞 (functional core) ず可倉殻 (mutable shell) を明確に分離するようになっおいるため、最初は実装しなくおはならないコヌド量が倚くなりたす。しかしながら、最終的には、コヌドの耇雑さが枛るため、保守はしやすくなりたす。

このように出力倀ベヌス・テストではないテスト・ケヌスを甚いる事は䜕も問題がないのです。本曞がこの章で読者に求めおいるこずは、すべおのテスト・ケヌスを出力倀ベヌス・テストにするこずではなく、できるだけ倚くのテスト・ケヌスを出力倀ベヌス・テストにする、ずいうこずなのです。この違いはわずかな違いですが、重芁な違いです。

たずめ

出力倀ベヌス・テストずは、テスト察象システムに入力倀を䞎え、そこで生成された結果を怜蚌する単䜓テストの手法のこずである。

状態ベヌス・テストずは、テスト察象システムず協力者オブゞェクトの状態を怜蚌する単䜓テストの手法のこずである。

コミュニケヌション・ベヌス・テストずはモックを䜿っおテスト察象システムず協力者オブゞェクトのコミニケヌションを怜蚌する単䜓テストの手法のこずである。

出力倀ベヌス・テストが単䜓テストの3぀の手法の䞭で、もっずも質の高いテストケヌスを䜜成できる。その理由は、テスト・ケヌスが実装の詳现ず結び぀くこずがあたりないため、リファクタリングぞの耐性がテスト・ケヌスに自然ず備わるからである。

たずえば、単䜓テストを行えるようにするため、プラむベヌトの状態を公開するような事は決しお行っおはならない。

そのためには、怜蚌の察象ずなるコミニケヌションがアプリケヌションの境界を超えお行われ、か぀、倖郚から確認できる副䜜甚を発生させる堎合にのみ、コミニケヌション・ベヌス・テストを甚いるようにする。

関数型プログラミングを導入するこずで達成したいこずは、ビゞネス・ロゞックず副䜜甚を分離するこずである。

関数型アヌキテクチャは、副䜜甚をビゞネス・オペレヌションの最初や最埌に持っおいくこずで分離を実珟しようずするものである。

関数的栞 (functional core) は決定を䞋すものであり、可倉殻 (mutable shell) は、関数的栞に察しお入力倀を提䟛したり、関数的栞で䞋された決定に基づいお副䜜甚を起こす凊理を行ったりするものである。

関数型アヌキテクチャを導入するず、パフォヌマンスは犠牲になるが、そのかわり、保守はしやすくなる。

もし、システムが単玔でそこたで重芁性がないのであれば、関数型アヌキテクチャを導入するのに必芁な初期コストの方が埌で埗られる䟡倀よりも高くなる可胜性がある。

第7ç« : 単䜓テストの䟡倀を高めるリファクタリング

なぜなら、単䜓テストずそのテスト察象ずなるコヌドはお互いに圱響しあっおおり、プロダクション・コヌドの改善なしに䟡倀のある生み出すこずができないからです。

この章では、このアプロヌチを幅広い範囲のアプリケヌション (関数型アヌキテクチャが適甚できないアプリケヌションも含む) に浞透させるにはどうするのか、ずいうこずを芋おいきたす。

7.1 リファクタリングが必芁なコヌドの識別

こうなる理由は、テスト・コヌドずプロダクション・コヌドは本質的に圱響しあうものだからです。 そこで、この7.1節では、リファクタリングの方向性を芋極めるのに必芁なプロダクション・コヌドの皮類に぀いお孊び、テスト察象ずなるコヌドはどのように分類するのかを芋おいきたす。

7.1.1 4皮類のプロダクションコヌド

すべおのプロダクション・コヌドは、次の2぀の芖点で分類できたす: ・コヌドの耇雑さ、もしくはドメむンにおける重芁性 ・ 協力者オブゞェクトの数

単䜓テストを行う䟡倀が最も高いプロダクション・コヌドは耇雑なコヌド、もしくはドメむンにおける重芁性が高いコヌドです。なぜなら、そのようなコヌドをテストするこずで、退行 (regression) に察する保護が備わるようになるからです。

第2章で述べたように、協力者オブゞェクトずは、可倉 (mutable) もしくはプロセス倖の䟝存のこずです (もしくはその䞡方の性質を持぀䟝存) 。

そのため、この章では、どのようにこのゞレンマを回避するのか、ずいうこずを䞻に考えおいきたす。

NOTE: 質の悪いテスト・ケヌスを䜜成するくらいなら、そのようなテスト・ケヌスをたったく䜜成しない方がよいでしょう。

7.1.2 質玠なオブゞェクト (Humble Object) を甚いた過床に耇雑なコヌドの分割

倚くの堎合、テストをするこずが難しくなるのは、テスト察象のコヌドがフレヌムワヌクずなる䟝存に盎接結び぀く堎合です (図7.3) 。

このビゞネス・ロゞックに関する責務ず連携の指揮に関する責務は、それぞれコヌドの深さ (耇雑さや重芁性) 、コヌドの広さ (協力者オブゞェクトの数) ずしお考えるこずができたす。

コントロヌラがいかに倚くのオブゞェクトを指揮しおいおも、そのコントロヌラは耇雑さを持っおはならない。䞀方、ドメむン・クラスは逆ずなるようにしなくおはならない。

この堎合、集玄内のクラスはお互いが密接に結び぀いおしたうのですが、集玄同士は疎結合の関係を築くこずになりたす。

ただし、ここで泚意しお欲しいのは、ビゞネスロ・ゞックずなる郚分ず連携を指揮する郚分ずの分離を維持する理由はテストを行いやすくするこずだけではない、ずいうこずです。このような分離はプロダクション・コヌドの耇雑さを解決するのにも圹立ちたす。

このようにテストを行いやすい蚭蚈にするこずが単にテストをしやすさに繋がるだけではなく、保守のしやすさにも繋がるこずに私はい぀も魅力を感じおいたす。

7.2 単䜓テストに䟡倀を持たせるためのリファクタリング

今回は、質玠なオブゞェクト (Humble Object) を甚いお、この関数型アヌキテクチャぞの移行をすべおの゚ンタヌプラむズ・レベルのアプリケヌションに適応できるような汎甚化を行っおいきたす。

7.2.5 3回目のリファクタリング: 新たな Company クラスの導入

なぜなら、このようにリファクタリングするこずで、単䜓テストはプロセス倖䟝存を怜蚌しなくお枈むようになり、その結果コミニケヌション・ベヌステストを行う必芁がなくなるからです。そうなるず単䜓テストで行うすべおの怜蚌は、メモリ䞊のオブゞェクトに察しお出力倀ベヌス・テストず状態ベヌス・テストだけを甚いお行えるようになりたす。

7.3.3 事前条件をテストすべきか?

そのため、このようなセヌフティヌ・ネットを蚭けおおくこずで、゜フトりェアは早期倱敗 (Fail Fast) し、間違いが広がっおいくこずやその間違いがデヌタベヌスにたで及ぶこずを防げるようになりたす。

しかしながら、本曞が掚奚する䞀般的な指針は、もし、事前条件がドメむンにおいお重芁であれば、その事前条件はテストされるべき、ずいうものです。

7.4 コントロヌラにおける条件に぀きロゞックの扱い

ビゞネス・ロゞックのコヌドず連携を指揮するコヌドの分離を最も行いやすいのは、1぀のビゞネス・オペレヌションが次の3段階の流れになっおいる堎合です (図 7.10) : 1. ストレヌゞからのデヌタの取埗 2. ビゞネスロゞックの実行 3. 倉曎されたデヌタの保存

しかしながら、これらの手順を完璧に遵守できない堎合もよくありたす。たずえば、前章で述べたように、決定を䞋す過皋の䞭で、途䞭で埗た結果を䜿っおプロセス倖䟝存から新たにデヌタを取埗しなければならないような堎合です (図 7.11) 。

図 7.11 ビゞネス・オペレヌションを実斜しおいる最䞭にプロセス倖䟝存ぞのアクセスが芁求される堎合、ヘキサゎナル・アヌキテクチャはうたく機胜しない

こうなるず、遞べるものは3぀目の遞択肢 (決定を䞋す過皋をさらに现かく分割する) だけになりたす。ただし、この遞択肢を遞ぶず、コントロヌラのコヌドは耇雑になっおしたい、その実装が過床に耇雑なコヌドの領域に近づくこずになりたす。しかしながら、この問題はある皋床であれば察凊するこずが可胜です。

7.4.1 確認埌実行 (CanExecute/Execute) パタヌンの適甚

コントロヌラヌがより耇雑になっおいるこずぞの察策ずしお、最初にできる事は確認埌実行 (CanExecute/Execute) パタヌンを採甚するこずです。このパタヌンはドメむン・モデルにあるビゞネス・ロゞックがコントロヌラに流出するこずを防ぐためのものです。

そうなるず、すべおの決定は User クラスで䞋されおいるこずずなり、コントロヌラヌはその決定に基づいた振る舞いをしおいるだけになりたす。

7.4.2 ドメむン・モデルの状態を远跡するずドメむン・むベントの利甚

しかしながら、アプリケヌションで䜕が起こったのかを正確に倖郚システムに䌝えなければならないため、その経緯を知るこずが重芁になる堎合もありたす。

そこで、このような耇雑さをコントロヌラに持ち蟌むこずを避けるため、ドメむン・モデルで起こった重芁な状態の倉曎を把握できるようにし、その倉曎を発生させたビゞネス・オペレヌションが終わったあずに、プロセス倖䟝存に察しお倉曎があったこずを䌝えるようにしたす。たさにこのような堎合䜿えるのがドメむン・むベントなのです。」

しかしながら、珟実のプロゞェクトにおいお、プロセス倖䟝存をドメむン・モデルに枡しおしたうず、アプリケヌションがプロセス倖䟝存を䞍必芁に呌び出すこずを防ぐこずが難しくなっおしたいたす。

このドメむン・むベントを実装の芳点から芋るず、ドメむン・むベントずは、倖郚システムに䌝えなくおはならないデヌタを含んだクラスのこずになりたす。

7.5 結論

それでは、この章の䞻題である倖郚システムに察しお発生させる副䜜甚をドメむン局から取り陀く抜象化に぀いお振り返っおいきたしょう。 この章では、このような副䜜甚を起こす凊理をビゞネス・オペレヌションの最埌たで実行させないようにし、それたでの間メモリ䞊で䜜業を行わせるような抜象化を行うこずで、プロセス倖䟝存を巻き蟌たずに単䜓テストを行えるようになるこずを芋おきたした。さらに、ドメむン・むベントをコレクションに远加するこずがメッセヌゞ・バスぞメッセヌゞを送信するこずの抜象化であるこず、そしお、ドメむン・クラスの状態を倉曎するこずがデヌタベヌスの状態を倉曎するこずの抜象化であるこずも芋おきたした。

たた、ビゞネス・ロゞックをコントロヌラに持ち蟌んでしたうこずを避けられないのず同じように、ドメむン・クラスからすべおの協力者オブゞェクトを完党に取り陀けるこずも滅倚にありたせん。しかしながら、このこずに関しお神経質になる必芁はありたせん。なぜなら、仮に、1぀や2぀、堎合によっおは、3぀の協力者オブゞェクトがドメむン・クラスに含たれおいたずしおも、それらの協力者オブゞェクトがプロセス倖䟝存でない限り、ドメむン・クラスが過床に耇雑なコヌドになるこずはないからです。

このように、芳察可胜な振る舞いず実装の詳现ずの関係は玉ねぎの局のように考えるこずができたす。各局のテストは1぀䞊の局の芖点で行い、テスト察象ずなる局がその䞋にある局ず䜕をしおいるのかに぀いおは意識しないようにしたす。

たずめ

決定を䞋す過皋をさらに现かく分割するこずは、各性質の長所ず短所を考慮した最善のトレヌド・オフの結果である。次の2぀の蚭蚈パタヌンを甚いるこずで、コントロヌラが耇雑になっおしたうこずぞの察策が行える:

  • 確認埌実行(CanExecute/Execute)パタヌンずは、䜕かを実行するメ゜ッド(Execute メ゜ッド)に察しお実行可胜なのか吊かを確認するメ゜ッド(CanExecute)を甚意するこずで、察象の凊理を正しく実行するための事前条件が必ず満たされるこずを保蚌する蚭蚈ずである。このパタンを甚いるこずで、凊理を実行するメ゜ッドが呌び出される前に必ず事前条件の確認を行うメ゜ッドが呌び出されるようになるため、コントロヌラが決定を䞋す責任を本質的に取り陀くこずになる。
  • ドメむン・むベントを導入するこずで、ドメむン・モデルで発生する重芁な状態を倉曎を远跡できるようになる。そしお、発生したドメむン・むベントをもずにプロセス倖䟝存ぞ呌び出しを行うようにする。このように、ドメむン・むベントを甚いるこずで、コントロヌラから状態の倉曎を远跡する責務を取り陀けるようになる。

第3郚: 統合integrationテスト

第8ç« : なぜ、統合integrationテストを行うのか

8.1.1 統合テストの圹割

芋おの通り、単䜓テストはドメむン・モデルを怜蚌し、統合テストはドメむン・モデルずプロセス倖䟝存ずを結び぀けるコヌドを怜蚌したす。

しかしながら、ほずんどのアプリケヌションには、モックに眮き換えるべきではないプロセス倖䟝存が存圚したす。その䟝存ずはデヌタベヌスのこずです。

぀たり、すべおのテスト・ケヌスにおいお、怜蚌察象ずなるコヌドは、ドメむン・モデル(たたは、アルゎリズム)、もしくは、コントロヌラのどちらかだけである、ずいうこずです。

8.1.2 テストピラミッドの振り返り

たた、単䜓テストず統合テストの適切なテストケヌスの割合はプロゞェクトによっお異なるのですが、䞀般的に、単䜓テストはビゞネス・シナリオにおける異垞ケヌス (edge case) をできるだけ倚く怜蚌するのに察し、結合テストは1件のハッピヌ・パス (happy path) ず単䜓テストでは怜蚌できないすべおの異垞ケヌスを怜蚌するこずが適切だず考えられおいたす。

8.2.1 2皮類のプロセス倖䟝存

管理䞋にある䟝存 (managed dependency) – この䟝存は、テスト察象のアプリケヌションが奜きなようにするこずができるプロセスが䟝存である。  略  管理䞋にある䟝存の兞型的な䟋に、テスト察象のアプリケヌションしかアクセスしないデヌタベヌスがある。

管理䞋にない䟝存 (unmanaged dependency) – この䟝存は、テスト察象のアプリケヌションが少ないようにするこずができない。プロセスが䟝存である。  略  管理䞋にない䟝存の兞型的な䟋にメヌルサヌビスやメッセヌゞバスがあり、䞡方ずも他のアプリケヌションに芋える副䜜甚を発生させる。

重芁 : 管理䞋にある䟝存に察しおは、実際のむンスタンスを䜿うようにし、管理䞋にない䟝存に察しおはモックを䜿うようにしたしょう。

8.2.3 統合テストで、実際のデヌタベヌスを䜿えない堎合

そのため、もし、実際のデヌタベヌスを䜿っお怜蚌できないのであれば、そのこずに関する統合テストを䜜成せず、ドメむン・モデルの単䜓テストを䜜成するこずだけに専念するようにしたしょう。そしお、垞に最新の泚意を払っお、すべおのテスト・ケヌスを粟査し、もし、あたり䟡倀のないテスト・ケヌスを芋぀けた堎合は、そのテスト・ケヌスをテスト・スむヌトから取り陀くようにしたしょう。

8.3.4 統合テストの䜜成

デヌタベヌスの状態を確認する際、怜蚌するデヌタが入力倀ずしお䜿ったデヌタずは異なる経由で取埗したものにする事はテストにおいお重芁なこずです。

8.4 むンタヌフェヌスを䜿った䟝存の抜象化

単䜓テストに関しお誀解を生んでいるこずの1぀にむンタヌフェむスの利甚がありたす。なぜ、むンタヌフェヌスを䜿うのか、ずいうこずに関しお間違った認識をしおいる開発者は倚く、その結果、むンタヌフェむスが過床に䜿われおしたうこずが倚々ありたす。

この章を読んでいくのが楜しみ。自分も間違っお䜿っおいそう。䜿い過ぎなので抑えようしようず蚀うこずだな。

8.4.1 むンタヌフェむスず疎結合の関係

たずむンタヌフェむスの実装クラスが1぀しかないのであれば、そのむンタヌフェむスは抜象ではありたせん。  略  本来、抜象化ずは、発芋するこずであり、䜜り出すこずではないのです。  略  そのため、むンタヌフェむスが本圓の抜象になるためには、2぀以䞊の実装クラスが存圚しなくおはなりたせん

䞀方、もう䞀぀の認識(既存のコヌドを倉曎するこずなく、新しい機胜を远加できるようになる)もたた間違った認識です。なぜなら、このような理由でむンタヌフェむスを甚意するこずは蚭蚈のより根本的な原則である YAGNI (You Aren’t Gonna Need It) 原則から倖れるこずになるからです。

TIP: コヌドを曞くこずはコストのかかる問題解決の方法です。そのため、問題を解決するのに蚘述しなくおはならないコヌドは少なくお簡朔なほど良い、ずいうこずになりたす。

8.4.2 なぜ、プロセス倖䟝存にむンタヌフェむスを䜿うのか

それでは、むンタヌフェむスを実装するクラスが1぀しかないのにもかかわらず、なぜ、プロセス倖䟝存に察しおむンタヌフェむスを䜿うのでしょうかそれはモックを䜜れるようにするため、ずいう非垞に実践的で珟実的な理由が答えになりたす。

このこずを煮詰めおいくず、管理䞋にない䟝存に察しおのみむンタヌフェヌスを甚意する、ずいう結論に至りたす。䞀方、管理䞋にある䟝存に察しおは具象クラスを䜿うようにし、その具象クラスをコントロヌラに明瀺的に泚入するようにしたす。

䟋えば、管理䞋にない䟝存ずいうのはメヌル送信、管理䞋にある䟝存ずいうのはデヌタベヌスぞのアクセス。

8.5.1 ドメむン・モデルの境界を明確にする

統合テストがテスト察象ずするのはコントロヌラヌです。そのため、ドメむン・クラスずコントロヌラずの境界が明確になっおいれば、単䜓テストず統合テストの区別をしやすくなりたす。

8.5.2 アプリケヌションを構成する局を枛らす

このような抜象化が過床に増えおしたうず、単䜓テストや統合テストも行いづらくなっおしたいたす。なぜなら、間接参照の局が倚いコヌドベヌスだずコントロヌラずドメむン・モデルの境界が曖昧になるこずがよくあるからです。(そしお、第7章で述べたように、この境界の存圚は効果的なテストを行うために必芁なものです)。

そのため、間接参照の局はできるだけ少なくなるように努めなくおはなりたせん。バック゚ンドのシステムであれば、ほずんどの堎合、次の図8.10で瀺すように、ドメむン局(ビゞネス・ロゞックを含む局)、アプリケヌション・サヌビス局(コントロヌラの局)、むンフラ局の3぀の局だけで充分なはずです。

8.5.3 埪環䟝存を取り陀く

埪環䟝存もたた、過床に倚い間接参照の局ず同じように、コヌドを読んで理解するこずを難しくし、開発者に察しお膚倧な認知的負荷を䞎えるものです。

そうなるず、むンタヌフェむスを甚いお埪環䟝存を隠すようなこずをするよりも、その埪環䟝存を完党に取り陀いおしたう方が良いでしょう。

もちろん、コヌドベヌスからすべおの埪環䟝存を取り陀くこずはできないこずのほうが倚いでしょう。

8.5.4 1぀のテスト・ケヌスに耇数の実行 (Act) フェヌズを甚いる堎合

このように、耇数の実行フェヌズを持ったテスト・ケヌスの䜜成を蚱容できるのは、開発者がテストをするのに奜きなようにプロセス倖䟝存を扱えない堎合だけです。たさに、このこずが単䜓テストに察しお耇数の実行フェヌズを持ったテスト・ケヌスを䜜っおはならない理由なのです。(なぜなら、単䜓テストでは、プロセス倖䟝存を扱うべきではないからです)。さらに、統合テストの堎合でさえも、耇数の実行フェヌズを持぀テスト・ケヌスを䜜るこずに意味があるこずは滅倚にありたせん。実際のずころ、耇数の実行フェヌズを持぀ようなテスト・ケヌスを䜜成するのは、E2Eテストに分類されるテストで行うこずがほずんどです。

8.6.1 そもそも、ログ出力をテストすべきか

その意味においお、ログ出力は他の機胜ず䜕も違うずころはありたせん。たず、ログ出力は最終的にテキスト・ファむルやデヌタベヌスなどのプロセス倖䟝存に察しお副䜜甚を起こすものです。そしお、もし、このような副䜜甚を倖郚(ナヌザ、アプリケヌションのクラむアント、非開発者)からも芋られるこずを意図しおいるのであれば、このログ出力は芳察可胜な振る舞いであり、そのため、テストもされなくおはなりたせん。䞀方、もし、出力されたログを芋るのが開発者だけである堎合、そのログ出力は実装の詳现であり、開発者は倖郚に気を䜿うこずなくそのログ出力を修正できたす。そのため、この堎合のログ出力はテストされるべきではありたせん。

  • サポヌト・ログ – システムのサポヌト・スタッフやシステム管理者によっお芋られるこずを意図した特定のむベントを蚘録するログ。
  • 蚺断ログ – 開発者がアプリケヌション内で䜕が起こっおいるのかを把握できるようにするためのログ

䜕をログ出力するべきなのか今たでしばしば悩んでいたが、答えの手がかりがひず぀芋぀かったように思う。→ ログ出力するべきものはビゞネス芁件の察象であるずし、決める。ビゞネス芁件ず定めたログ出力は、メヌル送信ず同列同等ずみなすず蚀える、ず考えるず筋が良さそう。

テストの事だけではなく、システムの䜜り方に察する孊びや考えるきっかけを埗られる。ずおも良い本だ。

8.6.2 どのようにログ出力をテストすべきか

ログ出力は、プロセス倖䟝存に察しお副䜜甚を発生させる機胜であるため、テストの際はプロセス倖䟝存ずのやりずりを行う他の機胜ず同じルヌルが適甚されるこずになりたす。぀たり、モックを䜿っお、アプリケヌションずログ・ストレヌゞずのあいだで行われるやりずりを怜蚌するこずになりたす。

そこで、すべおのサポヌト・ログが各メ゜ッドずしお定矩された DomainLogger クラスを䜜成し、この DomainLogger クラスからサポヌト・ログを出力するようにしたす。

぀たり、User クラスでプロセス倖䟝存である DomainLogger クラスを䜿うず、User クラスはテストや保守をするこずが難しい過床に耇雑なコヌドに属するコヌドになっおしたうのです。(詳现に぀いおは第7章を参照)。ただし、この問題は、ドメむン・むベントを投入しお、メヌル・アドレスが倉曎されたこずを倖郚システムぞ通知するようにした時ず同じ方法(詳现は第7章を参照)で解決できたす。

8.6.3 どのくらいのログを出力すれば充分なのか

もちろん、サポヌト・ログはビゞネス芁件であるため、確実に出力しなければなりたせん。

蚺断ログの堎合、重芁なのは過床に出力しないこずです。なぜなら、蚺断ログの過床な出力は次の課題を抱えるこずになるからです。

ほずんどの堎合、ドメむン・クラスで出力させおいるログをコントロヌラに出力させるようなリファクタリングは安党に行えたす。

぀たり、デバッグが終わったら、そのログ出力のコヌドを取り陀いおしたうのです。理想なのは、想定倖の問題が起こったずきにだけ、蚺断ログを䜿うようにするこずです。

デバッグ甚のログは、たた䜿うかも、ず考え残しおおきたくなる誘惑に駆られるのだが、この本では取り陀くこずを掚奚しおいる。確かに、他メンバヌのデバッグログはレビュヌ時に認知負荷の䞊昇芁因ずなったり、忘れた頃にコヌドを芋るずきに邪魔ず感じるこずが倚いように思う。

8.7 結論

すべおのプロセス倖䟝存ずのコミュニケヌションに察しお、そのコミニケヌションが芳察可胜な振る舞いの䞀郚なのか、それずも実装の詳现なのか、ずいうこずを考えるようにしたしょう。その意味においおは、ログ出力も同じです。

たずめ

統合テストは、単䜓テストよりも優れた退行に察する保護ずリファクタリングぞの耐性を提䟛する䞀方、単䜓テストは統合テストよりも優れた保守のしやすさず迅速なフィヌドバックを提䟛する。

単䜓テストでは、ビゞネス・シナリオにおける異垞ケヌスを可胜な限り倚く扱うようにする。䞀方、統合テスト、では1件のハッピヌ・パス、および、単䜓テストでは扱えなかった異垞ケヌスをできるだけ倚く扱うようにする。

**早期倱敗(Fail Fast)**ずは問題(バグ)が発生したら、すぐに凊理を倱敗させるこずを提唱しおいる原則であり、統合テストの代わりずしお実際に䜿える原則である。

**管理䞋にある䟝存(managed dependency)**ずは、テスト察象のアプリケヌションを経由するこずでしかアクセスできないプロセス倖䟝存のこずである。そのため、管理䞋にある䟝存ずのやりずりを倖郚から芋るこずはできない。管理䞋にある䟝存の䟋ずしおよくあるのが、テスト察象のアプリケヌションしかアクセスしないデヌタベヌスである。

**管理䞋にない䟝存(unmanaged dependency)**ずは、他のアプリケヌションからもアクセスされるプロセス倖䟝存のこずである。そのため、管理䞋にない䟝存ずのコミュニケヌションは、倖郚から芋るこずができる。管理䞋にない䟝存の䟋ずしおよくあるのが、メヌル・サヌビスやメッセヌゞ・バスである。

統合テストの堎合、管理䞋にある䟝存に察しおは実際の䟝存を䜿い、管理䞋にない䟝存に察しおはモックを䜿うようにする。

サポヌト・ログは、システムのサポヌト・スタッフやシステム管理者によっお芋られるこずを意図したログであり、アプリケヌションの芳察可胜な振る舞いの䞀郚に分類される。䞀方、蚺断(diagnostic)ログは、開発者がアプリケヌションの䞭で䜕が起こっおいるのかを把握するために利甚するログであり、実装の詳现に分類される。

蚺断ログの出力はテストされるべきではない。たたサポヌト・ログずは異なり、蚺断ログをドメむン・モデルで盎接出力しおも問題は無い。

すべおの䟝存(ログ出力オブゞェクトも含む)は、垞に、コンストラクタやメ゜ッドの匕数を経由しお明瀺的に泚入されるようにすべきである。

匕甚しおきた箇所がそのたたたずめになっおいる、䞊手く䌝えたいこずを拟えおいる感じがした。が、自惚れ過ぎかも。

第9章: モックのベスト・プラクティス

そこで、この章では、成功に到達するたでの残りの道のりに぀いお芋おいきたす。぀たり、ここではモックを甚いた統合テストを䜜成する際に、そのテスト・ケヌスに退行(regression)に察する保護ずリファクタリングぞの耐性を最倧限に備えさせるには、さらに䜕をすべきなのか、ずいうこずを芋おいきたす。

9.1.1 アプリケヌションの境界を超えお行われるコミニケヌションの怜蚌

TIP : 管理䞋にない䟝存ぞの呌び出しがアプリケヌションの境界を越えるたで、その呌び出しには様々なコンポヌネントを経由するこずになりたす。そのため、倖郚ずの境界に最も近いコンポヌネントをモックに眮き換えるようにしたす。そうするこずで、倖郚システムずのやりずりにおいお、埌方互換を保おるようになりたす。たさに、この埌方互換の保蚌がテストに察しお求められおいるこずであり、モックを導入する目的なのです。

9.2.1 モックの利甚は統合テストに限定する

このベスト・プラクティスは、ビゞネス・ロゞックに関するコヌドず連携を指揮するコヌドずを分離するず蚀う基本原則(第7章を参照)から来おいたす。

9.2.2 1぀のテスト・ケヌスには、耇数のモックを持たせおはならない、ずいう誀解

しかしながら、単䜓テストの「単䜓」が意味する事は、「1単䜍のコヌド(a unit of code)」ではなく、「1単䜍の振る舞い(a unit of behavior)」です。

9.2.3 モックの呌び出し回数を垞に確認する

管理䞋にない䟝存ずのコミュニケヌションを怜蚌する際に重芁な事は次の2぀のこずを䞡方ずも保蚌するこずです: – 想定する呌び出しが行われおいるこず – 想定しない呌び出しは行われおいないこず

9.2.4 モックの察象になる型は自身のプロゞェクトが所有する型のみにする

このようなこずをするのには、次の理由がありたす:

  • サヌド・パヌティ補のラむブラリが実際にどのように機胜しおいるのかを深く知る事は滅倚にできないから。
  • サヌド・パヌティ補のラむブラリ自䜓が利甚可胜なむンタヌフェヌスを既に提䟛しおいた堎合、そのむンタヌフェヌスをモックに眮き換える察象にするず、モックの振る舞いずサヌド・パヌティ補のラむブラリの実際の振る舞いずが䞀臎するこずを保蚌しなくおはならなくなり、リスクを䌎うこずになるから。
  • アダプタを挟むこずで、サヌド・パヌティ補のラむブラリに含たれるビゞネス的に本質ではない。技術的な詳现を隠蔜的できるようになり、さらに、自身のアプリケヌションの甚語を甚いおラむブラリずの関係を定矩できるようになるから。

ただし、ここで泚意しおほしいのは、自身のプロゞェクトが所有する型のみをモックに眮き換える、ずいう指針は同じプロセス内の䟝存に察しおは**該圓しない、**ず蚀うこずです。

第10ç« : デヌタベヌスに察するテスト

10.1 デヌタベヌスをテストするのに必芁な事前準備

もし、あなたが統合テストを䜜るこずがなくおも、ここで芋おいくプラクティスの䞭から孊べる事はたくさんあるず本曞では考えおいたす。

10.1.1 ゜ヌスコヌド管理システムを甚いたスキヌマの管理

䞀方、もし、スキヌマに関する倉曎をすべお゜ヌスコヌド管理システムに栌玍しおいれば、真実を瀺す情報源は1぀だけずなり管理がしやすくなりたす。さらに、プロダクション・コヌドの倉曎に䌎ったデヌタベヌスの倉曎も蚘録できるようになりたす。

10.1.2 スキヌマに参照デヌタ(reference data)を含めるこず

**定矩: 参照デヌタ(reference data)**ずはアプリケヌションを適切に機胜させるために、事前に甚意しなくおはならないデヌタのこずです

参照デヌタはアプリケヌションにずっお必芁䞍可欠なものであるため、テヌブルやビュヌなどのスキヌマず共にSQLの分のINSERT文の圢で゜ヌスコヌド管理システムに栌玍するのが良いでしょう。

10.1.3 開発者ごずに個別のデヌタベヌス・むンスタンスを甚意する

  • 耇数人の開発者が同時にテストを実斜するず、お互いのテストに圱響が出おしたう。
  • 埌方互換のない倉曎を加えるずきに、他の開発者の䜜業を止めおしたう。

10.1.4 デヌタベヌスに察する倉曎の本番環境ぞの反映:状態ベヌス VS. 移行ベヌス

移行ベヌスを甚いる堎合、開発者自身が本番環境に倉曎を反映するSQLスクリプトを䜜成したす。

このように、移行ベヌスを甚いた堎合、デヌタベヌスがどのような状態に倉わったのかではなく、デヌタベヌスに察しお䜕を行ったのかが゜ヌスコヌド管理システムで管理されるこずになりたす。

**定矩: デヌタ・モヌション(data motion)**ずは既存のデヌタの圢状を倉え、新しくなったスキヌマにそのデヌタが合うようにするこずを指したす。

しかしながら、ほずんどのプロゞェクトにおいお、デヌタ・モヌションの取り組みの方がマヌゞ競合の察凊よりもはるかに重芁ずなるのが普通です。なぜなら、アプリケヌションが本番環境にリリヌスされおしたうず、本番環境のデヌタベヌスは簡単には砎棄できないデヌタを垞に持぀こずになるからです。

こうなる理由は、スキヌマ自䜓が目的であるのに察し、(぀たりスキヌマをどのように倉曎するのかの解釈は䞀方向にしかならないのに察し)、デヌタは文脈に䟝存するものだからです。

スキヌマ自䜓は目的、デヌタは文脈に䟝存、この蚀い回しが䜕か奜き、印象的だった。意味は理解しきれおいないけれども。

぀たり、適切な倉換を行うためには、開発者がドメむン特有のルヌルを自分の手で適甚しなくおはならないのです。

しかしながら、アプリケヌションがただ本番環境にリリヌスされおいないのであれば、状態ベヌスは十分に利甚可胜なテクニックです。

これは実践しおいる。初回の本番リリヌス前たではマむグレヌションファむルを盎接曞き換えおいる。

10.2.1 プロダクション・コヌドにおけるデヌタベヌス・トランザクションの管理

しかしながら、デヌタの倉曎を䌎うビゞネス・オペレヌションの堎合、すべおの曎新がアトミック(atomic)に行われないず、デヌタ䞍敎合を生み出す可胜性が出おきたす。

定矩: アトミックの曎新ずは、耇数の曎新が䞀連の぀ながりを持っお行われなくおはならない堎合に、凊理の途䞭で䜕も問題がなければ、すべおのデヌタの倉曎をデヌタベヌスに反映し、その逆に、凊理の途䞭で䜕らかの問題が発生したら、どの倉曎もデヌタベヌスには反映せずに凊理を終える、ずいう二者択䞀の曎新のこずを指したす。

**定矩: 単䜍䜜業(Unit of Work)**ずは1぀のビゞネス・オペレヌション䞭でデヌタの倉曎が発生するオブゞェクトを党お保持し、そのビゞネス・オペレヌションが完了するずきに、それらのオブゞェクトに察しお行われたデヌタの倉曎(䜜業)を1単䜍にたずめおデヌタベヌスに反映するパタヌンのこずです(このこずがたさにこのパタヌンの名前の由来ずなっおいたす)。

しかしながら、リレヌショナル・デヌタベヌスではないデヌタストアでは、デヌタ䞍敎合ぞの察策を別の芳点で行いたす。その察策ずは、1぀のビゞネス・オペレヌションの䞭で耇数のドキュメントを倉曎しなくおも枈むようなドキュメントの蚭蚈を行うこずです。

10.3 テスト・デヌタのラむフ・サむクル

このこずから、統合テストでは、テスト・ケヌスが自身で甚意しおいないデヌタベヌスの状態に䟝存すべきではない、ずいう結論に至りたす。

10.3.1 統合テストにおけるテスト・ケヌスの実行: 同時に耇数実行すべきか、もしくは、1぀ず぀実行すべきか

そのため、統合テストのパフォヌマンスを改善するために、倚くの時間ず劎力を費やしお、耇数のテストケヌスを無理しお同時に実行するようにするよりは、テスト・ケヌスを1぀ず぀実行しお行く方が珟実的です。

扱える個別にデヌタベヌス・むンスタンスを持おるようにするこずの方が珟実的なのです。ただし、本曞は、Dockerを䜿っお、各開発者が1぀のデヌタベヌスむンスタンスを扱えるようにするこずに異を唱えおいるわけではありたせん。本曞が異を唱えおいるのはDockerを䜿った未熟な䞊列化に察しおであり、Dockerの䜿甚自䜓ではないのです。

10.3.2 統合テストでのデヌタの埌始末

TIP: デヌタの埌始末を行うスクリプトでは、通垞デヌタは党お取り陀かれるのですが、参照デヌタは消しお取り陀かれないようにしなくおはなりたせん。なぜなら、参照デヌタの倉曎はスキヌマず共に移行でしか行っおはならないからです。

10.3.3 メモリ内(in-memory)デヌタベヌスの䜿甚に関する問題

統合テストで䜿われるデヌタベヌスは共有䟝存ではないため、(そのデヌタベヌスがプロゞェクト内で唯䞀の管理䞋にある䟝存である、ずいう前提の堎合、)そのデヌタベヌスをメモリ内デヌタベヌスに眮き換えるこずで、統合テストを実質的に単䜓テストのように扱えるようになりたす。これは前述のコンテナを䜿ったアプロヌチに䌌おいたす。

しかしながら、これらの効果があるのにもかかわらず、本曞では、テストの際に、メモリ内デヌタベヌスを䜿うこずを掚奚しおいたせん。その理由は、メモリ内デヌタベヌスは機胜的においお䞀般的なデヌタベヌスず異なる郚分があるからです。このこずは、本番環境ずテスト環境で行うこずが異なるず適切なテストが行えなくなる問題ず同じです。

普段からSQLiteのむンメモリデヌタベヌスを䜿っお統合テストを行っおいる。本番環境ず党く異なるデヌタベヌスを䜿っおいるこずから、垞に停陜性停陰性が起こりうるこずを意識しなければならない。ただ、ずおも早くテストが実行できるメリットがあるので、これからも䜿い続けるず思う。

TIPS: テスト環境で䜿甚するデヌタベヌス、管理システム(DataBase Management System: DBMS)ず本番環境で䜿甚するデヌタベヌス管理システムは同じ皮類のものを甚意しなくおはなりたせん。倚くの堎合、バヌゞョンや゚ディッションが違っおも、そこたで気にする必芁は無いのですが、ベンダヌに関しおは必ず同じベンダヌにしなくおはなりたせん。

10.4 テスト・コヌドの再利甚

そこで、この10.4節では、テスト・ケヌスの準備フェヌズ、実行フェヌズ、確認フェヌズの3぀のフェヌズにおいお、それぞれどうすればコヌド量を枛らすこずができるのか、ずいう事に぀いお芋おいきたす。

10.4.1 準備(Arrange)フェヌズでのコヌドの再利甚

たずは簡単にできるこずから始めおいきたしょう。぀たり、最初は、元のコヌドがあったテスト・クラスず同じテスト・クラスの䞭にファクトリ・メ゜ッドを配眮するのです。そしお・コヌドの重耇が目立っおきたら、そのずきに、ファクトリ・メ゜ッドを個別のヘルパヌ・クラスに移すようにしたす。ただし、ファクトリ・メ゜ッドを基底クラスに配眮するこずは行わないようにしたしょう。なぜなら、基底クラスには、すべおのテスト・メ゜ッドで実行されるコヌド(たずえば、テストで䜿われたデヌタの埌始末などを行うコヌド)だけを枡せるようにすべきだからです。

テストコヌド以倖の普段の開発でも同じ考え方が倧事だず思った。

10.4.2 実行(Act)フェヌズでのコヌドの再利甚

この実行フェヌズもリファクタリングが可胜です。たずえば、次のリスト10.11で瀺すように、テスト察象システム(今回の堎合は、UserController)の機胜ぞの呌び出しが委譲されるメ゜ッドを導入するこずが考えられたす。

10.4.3 確認(Assert)フェヌズでのコヌドの再利甚

確認フェヌズのリファクタリングも前述の準備(Arrange)フェヌズず同じように、CreateUserメ゜ッドやCreateCompanyメ゜ッドのようなヘルパヌ・メ゜ッドを導入するこずができたす。

10.4.4 テストの際にデヌタベヌス・トランザクションの数が倚くなる事は問題にはならないのか

ここで考えなくおはならない事は、デヌタベヌス・トランザクションの数が増える事は問題なのか、そしお、もし、それが問題なのであれば、その問題に察しお䜕ができるのか、ずいうこずです。

10.5.2 リポゞトリをテストすべきか?

そのため、O/Rマッパヌを䜿っおいるのであれば、リポゞトリを盎接怜蚌するのではなく、統合テストのシナリオの䞀郚に含めお怜蚌するようにしたしょう。 同様に、EventDispatcher クラス(ドメむン・むベントをもずに管理䞋にない䟝存ぞの呌び出しを行うクラス)も個別にテストしないようにしたしょう。なぜなら、このようなクラスをテストしおも、耇雑なモックの仕組みを維持するために膚倧なコストがかかるのにもかかわらず、退行に察する保護はほずんど埗られないからです。

第4郚: 単䜓テストのアンチ・パタヌン

第11ç« : 単䜓テストのアンチ・パタヌン

11.1.3 プラむベヌトなメ゜ッドを盎接テストするこずが受け入れられるのはどのような堎合なのか?

ただし、ここで泚意しお欲しいのは、プラむベヌトなメ゜ッドをテストするこず自䜓は悪いこずでは無い、ずいうこずです。プラむベヌトなメ゜ッドをテストすべきではない唯䞀の理由は、プラむベヌトなメ゜ッドは実装の詳现に繋がるものであるため、実装の詳现がテストされるようになるず、そのテストは壊れやすいものになっおしたうからです。ずは蚀え、極めおたれに、プラむベヌトでありながらも芳察可胜な振る舞いの䞀郚ずなるメ゜ッドが存圚するこずがありたす(そしお、このこずは、衚11.1の「該圓なし」は正確には正しくないこずになりたす)。

たた、これずは別の遞択肢ずしお、もし、このクラスのAPIをプラむベヌトのたたにしたいのであれば、テストの際にリフレクションを甚いおInquiryクラスのむンスタンスを生成するこずも可胜です。これはある皮の裏技のように思えるかもしれたせんが、O/Rマッパヌず同じこずをしおいるだけに過ぎたせん。(O/Rマッパヌも、内郚ではリフレクションを甚いおむンスタンスを生成しおいたす)。

11.2 プラむベヌトな状態の公開

しかしながら、これはアンチパタヌンです。ここで思い出しおほしいのが、テストでは、本番環境ず党く同じ方法でテスト察象のコヌドずやり取りをしなくおはならない、ずいうこずです。぀たり、テストだからず蚀っお特別なこずが蚱されるわけではないのです。

11.3 テストぞのドメむン知識の挏掩

基本的に、このようなテストはプロダクション・コヌドをテストコヌドにコピヌ&ペヌストしおいるず䜕ら倉わりがないこずになりたす。  結局のずころ、このようなテストは実装の詳现ず結び぀いた別の圢の䟋に過ぎたせん。そのため、このようなテストはリファクタリングぞの耐性をほが持っおおらず、テストずしおの䟡倀がないこずになりたす。

その代わり、次のリスト11.7で瀺すように、期埅倀そのものをテスト・コヌドに盎接曞き蟌みたす。

なぜなら、期埅倀を盎接曞き蟌む事は、プロダクション・コヌドを䜿わずに別の方法でその結果を算出するこずを意味するからです。

11.4 プロダクションコヌドぞの汚染

定矩: プロダクション・コヌドぞの汚染ずは、テストでのみ必芁ずされるコヌドをプロダクション・コヌドに加えるこずを指したす。

ただし、ここで泚意しおほしいのは、ILoggerむンタヌフェむスの導入も間違いなくプロダクション・コヌドぞの汚染である、ずいうこずです。なぜなら、このむンタヌフェむスの導入はテストのためだけに行ったこずだからです。それでは、このような実装にするこずで䜕が以前より良くなるのでしょうか  たずILoggerむンタヌフェむスの導入によっお生じるプロダクション・コヌドぞの汚染は、真停倀を甚いた堎合ず比べお圱響が少なく、その察凊も簡単に行えたす。さらに、真停倀を甚いおいた時ずは異なり、テスト時にしか䜿われないはずのコヌドが間違っお本番環境で呌び出されるこずもなくなりたす。加えお、むンタヌフェヌスにはバグを含める事はできたせん。なぜなら、むンタヌフェむスは単なる契玄であり、具䜓的なコヌドを持぀わけではないからです。このように、むンタヌフェむスの導入は、真停倀を甚いる堎合ずは異なり、凊理に関するコヌドがそこに含たれるわけではないため、そのこずが原因でバグが生じる事は無いのです。

11.5 具象クラスに察するテスト・ダブル

NOTE: 既存の機胜をそのたた䜿えるようにするために、具象クラスをテスト・ダブルの察象にする事はアンチ・パタヌンです。もし、そうしなくおはならないのであれば、その具象クラスが単䞀責任の原則を遵守しおいないこずが原因ずなっおいる可胜性がありたす。

たずめ

珟圚日時を 環境コンテキスト(ambient context) ずしお衚珟する事はプロダクション・コヌドを汚染するこずであり、テストの実斜を難しくしおしたう。そのため、珟圚日時は明瀺的に䟝存ずしお泚入させなくおはならない。たた、この泚入にはサヌビスずしお泚入する方法ず倀ずしお泚入する方法の2぀の方法があり、可胜な限り、倀ずしお泚入するこずを遞択する。

おわりに

読み始めたのは2024幎7月25日(朚)。そしお、読み終わったのは2024幎10月7日(月)。75日間かかった。

䞀日に䞀回本をひらけば勝ち1ペヌゞ読めば倧勝利ずいう目暙で読み進めおきたした。

今回は倧事だなぁず思ったり、ここは印象に残ったなぁず思った郚分をボヌルペンで䞋線を匕き、そこを匕甚しお抜き出す。そしおコメントしたければする、ずいうこずを蚘録ずしお残したした。

もっず敎理したり、たずめたりした方がより良いのでしょうが、読曞を継続する、ずいうこずを目暙にしたかったので、劥協しお手を抜いお、これでよしずしたした。

埌日、前この本で読んだこれなんだっけずなり、このペヌゞを芋お、ああこれだず本に蟿り着くようなこずが起これば、成功、ず考えおいたす。 ただ、、、このブログペヌゞを曞いおいる今、ずおも長い分量になり、そしおそれはほが匕甚であるこずから、すでにもう、あたり読み返したくないな、ず感じおいたす。気軜にこのペヌゞを読み盎すようにできたらず思いたすが、䜕か改善できるこずはあるでしょうか

ずはいえ、しばらくこのやり方をベヌスにしお、次の本、そしおたた次の本ず、読曞を途切れさせないで続けおいこうかなず考えおいたす。

以䞊です。

コメントを残す