PHPカンファレンス福岡2019 ( #phpconfuk )で発表するユニットテストの現場の問題についての整理を行っていくトークです。
© - BASE, Inc. Xユニットテストの現場の問題を原則に⽴ち返って考えるPHP Conference Fukuoka #phpconfuk. . - @hgsgtk
View Slide
© - BASE, Inc.. 問題を考えるための基礎となるユニットテストの⽬的. 現場の問題の整理‧問題に対する戦略の選択肢. テスト戦略に対するメリット‧デメリットの評価このトークが提供すること
© - BASE, Inc.判断の「材料」このトークが提供すること
© - BASE, Inc.このトークが提供しないこと絶対的な「正解」
© - BASE, Inc.お品書き:このトークで取り上げる問題• 意図が汲み取りにくいユニットテストがあります• 不安定なユニットテストがあります• 変更頻度が⾼いユニットテストがあります
© - BASE, Inc.このトークの流れ. 取り上げる現場の問題. ユニットテストの⽬的. 現場の問題の整理と評価. まとめ. 参考‧引⽤資料
© - BASE, Inc.: @hgsgtk⾃⼰紹介東⼝ 和暉 ( Higashiguchi Kazuki )Backend Engineer(Go, PHP, Python etc)BASE BANK, Inc / Dev Division
https://speakerdeck.com/hgsgtk
© - BASE, Inc.このトークの流れ.取り上げる現場の問題. ユニットテストの⽬的. 現場の問題の整理と評価. まとめ. 参考‧引⽤資料
© - BASE, Inc.このトークでの問題整理フォーマット現場でのユニットテストの問題問題障害戦略その課題の難しさ‧取り組みを妨げる障害障害に対してどのような戦略‧戦術を⽤いるか判断評価. 戦略のメリット. を評価する. 戦略のデメリット. を評価する
© - BASE, Inc.問題に対する複数の障害を考えていきます現場でのユニットテストの問題問題障害戦略判断評価
© - BASE, Inc.このトークの流れ. 取り上げる現場の問題.ユニットテストの⽬的. 現場の問題の整理と評価. まとめ. 参考‧引⽤資料
© - BASE, Inc.我々を取り巻く様々なテスト• 受け⼊れテスト• 機能テスト• 統合テスト• ユニットテスト• 負荷テスト• コード品質テスト• etc
© - BASE, Inc.ユニットテストとは• プログラムを構成する⽐較的⼩さな単位(ユニット)が個々の機能を正しく果たしているかどうかを検証する• 実⾏⼿段として、⼿動によるテスト‧⾃動化テストが存在する• このトークでは、⾃動化テストを取り上げる
© - BASE, Inc.なぜユニットテストを書くのか• ⽋陥混⼊の阻⽌、バグを防ぎたい?• テストによるドキュメンテーション?• 設計改善の指標?
© - BASE, Inc.このトークで定義する⽬的コスト削減
© - BASE, Inc.コスト削減の指標ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜユニットテストの両⾯のコストϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ
© - BASE, Inc.コスト削減の指標ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετ>
© - BASE, Inc.詳しくはこちらの資料をhttps://speakerdeck.com/hgsgtk/practices-to-write-better-unit-test
© - BASE, Inc.まとめ• ユニットテストの⽬的をコスト削減とおく• ユニットテストによるコスト削減が、ユニットテストによる発⽣コストを上回る状態を維持する
© - BASE, Inc.(CM)BASEとはネットショップ作成サービス「BASE」ショッピングアプリ「BASE」価値の交換をよりシンプルにし、世界中の⼈々が最適な経済活動を⾏えるようにする。MISSION
© - BASE, Inc.
© - BASE, Inc.このトークの流れ. 取り上げる現場の問題. ユニットテストの⽬的.現場の問題の整理と評価. まとめ. 参考‧引⽤資料
© - BASE, Inc.意図が汲み取りにくいユニットテストがあります意図が汲み取りにくいユニットテストがあります問題障害• 意図が不明瞭なテストコード
© - BASE, Inc.意図が不明瞭なテストコード
© - BASE, Inc.意図が不明瞭なテストコード「1がtrueで2がfalseね、ワカラナイ」
© - BASE, Inc.意図が汲み取りにくいユニットテストがあります意図が汲み取りにくいユニットテストがあります問題障害戦略意図が不明瞭なテストコード“Communicate Intent”
© - BASE, Inc.“Communicate Intent”(意図を伝える)• テストをメンテナンスする開発者が、理解しやすくメンテナンスしやすいテストにすること• xUTP(※ )の⾃動テストの原則の⼀つ• 理解が難しいコードは、“Obscure Test”と表現される※1 ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ / Chapter 5. Principles ofTest Automation / ࣗಈςετͷతɾݪଇΛମܥతʹཧͨ͠ॻ੶
© - BASE, Inc.“Obscure Test”(曖昧なテスト)• ひと⽬で理解することが難しいテスト• ドキュメントとしての効果が減り、メンテナンスコストの⾼いテストにつながるRefs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ / Chapter 15. Code Smells
© - BASE, Inc.戦略を使って改善する例
© - BASE, Inc.戦略を使って改善する例「2は禁⽌ユーザーだからfalseなのね」
© - BASE, Inc.意図が汲み取りにくいユニットテストがあります意図が汲み取りにくいユニットテストがあります問題障害戦略意図が不明瞭なテストコード“Communicate Intent”判断評価. 戦略のメリット. を評価する. 戦略のデメリット. を評価する
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Communicate Intent”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ•υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• 作成者の「意図」が伝わることで、テスト対象コードのドキュメンテーションとしての価値が⾼まる
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ•طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Communicate Intent”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ•υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• テストの可読性が向上することで、メンテナンス性の良いコードになる。
© - BASE, Inc.“Communicate Intent”の判断ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετ>• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ•υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• ৽نςετ࡞ίετൃੜ•طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ
© - BASE, Inc.+α 失敗の原因を明瞭に伝える適切なエラーレポート• テストコード⾃体が “Communicate Intent”であることが重要• 発想の⼀つとして、Goのユニットテストについてのアイデアを抑える• テスト結果だけで「なぜ失敗したのか」がわかる“適切なエラーレポート”
© - BASE, Inc.+α Go FAQ: Why does Go not have assertions?Go⾔語では、アサートを提供していません。アサートが便利である点は疑う余地はありませんが、我々の経験的には、プログラマはアサートを、適切なエラーハンドリングとエラーレポートを考慮せずに済ますためのツールとして使っています。(※ 原⽂私訳)Refs: https://golang.org/doc/faq#assertions
© - BASE, Inc.+α Proper error reportingの例 (Go)
© - BASE, Inc.+α Proper error reportingの例 (Go)=== RUN TestInStatusList--- FAIL: TestInStatusList ( . s) sample_test.go: :InStatusList(deleted) = false, want trueFAIL直接的かつ適切な内容をレポートに込めるex. “f(x) = y, want z”という⼀つの形式Refs ॻ੶ʰϓϩάϥϛϯάݴޠGoʱ / ୈ11ষ ςετ
© - BASE, Inc.+α 失敗の原因を明瞭に伝える適切なエラーレポート• ⾔語は違えどエラーレポーティングの考え⽅は頭の⽚隅に意識しておくとよい• PHPUnit\Framework\Assert::assertXxxはメッセージを渡すことができる• テスト失敗原因が分かりづらいかも知れないと思ったときに意識すると、メンテナンスコストの低い費⽤対効果の良いテストコードになりうる
© - BASE, Inc.まとめ:意図が汲み取りにくいユニットテストがあります意図が汲み取りにくいユニットテストがあります問題障害戦略意図が不明瞭なテストコード“Communicate Intent”判断評価 . ドキュメンテーションコストの削減. 既存テスト維持コストの削減. 該当なし>
© - BASE, Inc.(CM) BASE BANK銀⾏をかんたんにし、全ての⼈が挑戦できる世の中にMISSIONhttps://thebase.in/yellbank• BASE, Inc の100%⼦会社• 即座に資⾦調達ができる⾦融サービス「YELL BANK(エールバンク)」を運営
© - BASE, Inc.不安定なユニットテストがあります不安定なユニットテストがあります問題障害• 実⾏順序で結果が変わるユニットテスト• 相互依存するデータベースフィクスチャ
© - BASE, Inc.実⾏順序で結果が変わるテスト
© - BASE, Inc.不安定なユニットテストがあります不安定なユニットテストがあります問題障害戦略実⾏順序で結果が変わるユニットテスト“Keep Tests Independent”
© - BASE, Inc.“Keep Tests Independent”(テストを独⽴させる)• テストが相互依存‧順序依存する場合、”独⽴性”がない状態• 独⽴していないテストは、 “Erratic Test”(不安定なテスト)と呼ばれるRefs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ/ Chapter 5. Principles of Test Automation
© - BASE, Inc.“Erratic Test”(不安定なテスト)• “sometimes they pass and sometimes theyfail”• 不安定な状態を続けると、他の原因での失敗を⾒逃してしまうリスクが有る• この問題のトラブルシューティングは難しいRefs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ/ Chapter 15. Code Smells
© - BASE, Inc.逸脱の常態化(normalization of deviance)“テストスイートへ信頼を失い、「常にこのように失敗する」と思うようになります。”“間違っているものに慣れすぎて、それを正常で問題ないと徐々に受け⼊れ始めることがあるという考え⽅です。”Refs ॻ੶ʰϚΠΫϩαʔϏεΞʔΩςΫνϟʱ/ 7ষ ςετ - 7.6 ৴པͰ͖ͳ͍੬ऑͳςετ
© - BASE, Inc.経験上の主な原因:実⾏順序で結果が変わる• テストセットアップ‧実⾏時に⾏ったグローバルな状態変更を後⽚付けできていない• グローバルな状態変更• グローバル変数の更新• トランザクション状態• テスト実⾏時間• etc
© - BASE, Inc.戦略を使った改善例:tearDown()での後⽚付けhttps://github.com/cakephp/cakephp/blob/14b1fece73b4786f1f3f23f77c828aa477f5e002/src/TestSuite/TestCase.php#L156
© - BASE, Inc.戦略を使った改善例:tearDown()での後⽚付けhttps://github.com/cakephp/cakephp/blob/14b1fece73b4786f1f3f23f77c828aa477f5e002/src/TestSuite/TestCase.php#L156この例は、CakePHP .x⾃⾝がやっているtearDown()プロジェクトの経験上、忘れがち‧問題になりがちな後⽚付け忘れがあれば、super classで毎回tearDownしておく選択肢も
© - BASE, Inc.不安定なユニットテストがあります不安定なユニットテストがあります問題障害戦略実⾏順序で結果が変わるユニットテスト“Keep Tests Independent”判断評価. 戦略のメリット. を評価する. 戦略のデメリット. を評価する
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Keep Tests Independent”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ•ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• “Erratic Test”(不安定なテスト)回避効果• 別の⽋陥‧問題を⾒逃してしまうリストの低下
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ•طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• ϢχοτςετͷֶशίετൃੜϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ•ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• “Erratic Test”(不安定なテスト)回避効果• トラブルシューティングの難しいコストの掛かる現象の回避“Keep Tests Independent”メリット
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ•طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ•Ϣχοτςετͷֶशίετൃੜ“Keep Tests Independent”のデメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ•ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• Super classで共通のtearDownをしていても、個別で必要だった⽚付けの⾒逃しや⽚付け忘れはありうる• その際のErratic Testの原因特定のために、「何を⽚付けてくれているか」知る必要がある• クラスの層ができるので、ある種の「プロジェクトの仕様」を学習する機会が発⽣する
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ•طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ•Ϣχοτςετͷֶशίετൃੜ“Keep Tests Independent”のコスト評価ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ•ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ
© - BASE, Inc.+α テストの実⾏順序を指定する必要がある場合• TestMethodA -> Bと順序を指定したいケースもありうる• PHPUnitでは、@depends アノテーション等でテストの依存関係を明⽰することも可能Refs https://phpunit.de/manual/6.5/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.test-dependencies
© - BASE, Inc.まとめ:不安定なユニットテストがあります> 実⾏順序で結果が変わるユニットテスト不安定なユニットテストがあります問題障害戦略実⾏順序で結果が変わるユニットテスト“Keep Tests Independent”判断評価. ⽋陥の早期発⾒による修コスト削減. 既存テスト維持コスト削減. ユニットテストの学習コスト発⽣><
© - BASE, Inc.相互依存するデータベースフィクスチャ
© - BASE, Inc.相互依存するデータベースフィクスチャ「テストを追加するゾ」
© - BASE, Inc.相互依存するデータベースフィクスチャ 「テストを追加するゾ」
© - BASE, Inc.不安定なユニットテストがあります不安定なユニットテストがあります問題障害戦略相互依存するデータベースフィクスチャMinimal Fixture / Standard Fixture
© - BASE, Inc.“Standard Fixture”• “We reuse the design of the test fixtureacross the many tests.• 複数のテストケースでフィクスチャレコード定義を共有する戦略Refs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ/ Chapter 18. Test Strategy Patterns
© - BASE, Inc.“Shared Fixture”“We reuse the design of the test fixture acrossthe many tests.
© - BASE, Inc.“Minimal Fixture”• “Use the smallest and simplest fixturepossible for each test.”• テストケースごとにフィクスチャレコード定義を⽤意する戦略Refs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ/ Chapter 18. Test Strategy Patterns
© - BASE, Inc.“Minimal Fixture”“Use the smallest and simplest fixturepossible for each test.”ex. Larval: Factory, Ruby on Rails: FactoryBot
© - BASE, Inc.Standard & Minimal Fixture• 2つの戦略を組み合わせて使う
© - BASE, Inc.不安定なユニットテストがあります不安定なユニットテストがあります問題障害戦略相互依存するデータベースフィクスチャMinimal Fixture / Standard Fixture判断評価. 戦略のメリット. を評価する. 戦略のデメリット. を評価する
© - BASE, Inc.不安定なユニットテストがあります不安定なユニットテストがあります問題障害戦略相互依存するデータベースフィクスチャMinimal Fixture / Standard Fixture判断評価. 戦略のメリット. を評価する. 戦略のデメリット. を評価するMinimal Fixture‧Standard Fixture2つの戦略の⽐較から考える
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Standard Fixture”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• ⼀つの定義で済むのでフィクスチャの作成コストが削減• ex. ユーザー情報のレコード準備
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Standard Fixture”のデメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• 複数テストケースで共有する分、相互依存する• テスト間の独⽴性が低下• 影響範囲を考慮に⼊れたテスト作成が必要になる
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Standard Fixture”のデメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• フィクスチャレコードが⼤きくなりがちなので、テスト準備に時間がかかる• テストの実⾏速度が低下する
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Standard Fixture”の評価ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Minimal Fixture”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• テストケースごとの影響範囲なので、最⼩限のフィクスチャレコードを⽤意すればいい• エッジケースのテストパターンを作りやすい• ex. 「利⽤禁⽌ユーザー」
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Minimal Fixture”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• 既存テストコードが依存するフィクスチャの影響範囲が明確• 影響範囲が理解しやすい分、メンテナンスもしやすいテストフィクスチャになる
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Minimal Fixture”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• テストレコードが最⼩限なので、テスト実⾏速度が(少し)早い
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Minimal Fixture”のデメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• “Standard Fixture”のメリットでもあるテストレコードの再利⽤
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Minimal Fixture”の評価ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ
© - BASE, Inc.まとめ:不安定なユニットテストがあります不安定なユニットテストがあります問題障害戦略相互依存するデータベースフィクスチャMinimal Fixture / Standard Fixture判断評価. Minimal Fixtureのメリット/デメリット. Standard Fixtureのメリット/デメリット><
© - BASE, Inc.変更頻度が⾼いユニットテストがあります変更頻度が⾼いユニットテストがあります問題障害• 内部実装に依存したユニットテスト• Mockが妨げるユニットテスト
© - BASE, Inc.内部実装に依存したユニットテスト
© - BASE, Inc.内部実装に依存したユニットテストPrivate methodに対して、ちょっとしたリファクタリングを⾏う(外部の振る舞いは変わらない)
© - BASE, Inc.ERRORS!Tests: , Assertions: , Errors: .ArgumentCountError: Too few argumentsto function Hoge::getUserPoint(), passedand exactly expected
© - BASE, Inc.内部実装に依存したユニットテストPrivate Method “getUserPoint”に対するユニットテストが失敗していた"ArgumentCountError: Too few arguments to functionHoge::getUserPoint(), passed and exactly expected”
© - BASE, Inc.内部実装に依存したテスト• 内部実装: 公開していない実装情報• private / protected• 内部実装に依存しすぎるテストは、 “FragileTest” (壊れやすいテスト)と呼ばれる• 振る舞いを変えない改善に対してもテストが反応してしまう
© - BASE, Inc.“Fragile Test” (壊れやすいテスト)• 修正した箇所と無関係なはずのテストが失敗しはじめる。• いくつかの原因のうち、 “OverspecifiedSoftware” という状況となっている• テストコードで、テスト対象の振る舞いを過剰に検証している。Refs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ/ Chapter 16. Behavior Smells
© - BASE, Inc.変更頻度が⾼いユニットテストがあります変更頻度が⾼いユニットテストがあります問題障害戦略内部実装に依存したテスト“Use the Front Door First”
© - BASE, Inc.“Use the Front Door First”(最初に正⾯⽞関を使う)• 外部から利⽤することを期待する public インタフェース と、内部のみからが期待される privateインターフェース• テスト対象とは可能な限り、Front Door(public interface)を介した対話をするRefs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ/ Chapter 5. Principles of Test Automation
© - BASE, Inc.“Front Door”を介するための選択肢. 既存の public インターフェースからテストする. private インタフェースを public に引き上げる
© - BASE, Inc.“publicメソッドにすべきかどうかで悩んでしまう場合、たいていはそのクラスが多くのことを⾏いすぎており、修正すべきであることを意味しています。”Refs ॻ੶ʰϨΨγʔίʔυվળΨΠυʱ/ 10.1 ӅΕͨϝιου
© - BASE, Inc.戦略を使って改善する例「ポイント合計算出」を別クラスに移乗してpublicにする
© - BASE, Inc.変更頻度が⾼いユニットテストがあります変更頻度が⾼いユニットテストがあります問題障害戦略内部実装に依存したテスト“Use the Front Door First”判断評価. 戦略のメリット. を評価する. 戦略のデメリット. を評価する
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Use the Front Door First”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• クラスの責務の⾒直しによる設計改善• メンテナンス性の⾼い動作コードへ
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Use the Front Door First”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• 動作コードの変更に対する変更頻度の減少
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Use the Front Door First”のデメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• テスト対象の内部処理は “ブラックボックス” として扱いテストしすぎない• (外側から⾒た振る舞いを検証する)• 内部の private に複雑なロジックが詰め込まれている場合、振る舞い保証が不⼗分な可能性も。
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“Use the Front Door First”の評価ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ
© - BASE, Inc.まとめ:変更頻度が⾼いユニットテストがあります変更頻度が⾼いユニットテストがあります問題障害戦略内部実装に依存したテスト“Use the Front Door First”判断評価. 設計改善によるメンテナンスコスト削減. 既存テスト維持コスト. ⽋陥の早期発⾒による修正コスト増加(の可能性)><
© - BASE, Inc.Mockが妨げるユニットテスト
© - BASE, Inc.Mockが妨げるユニットテスト内部の処理を⼀部リファクタリングする(外部の振る舞いは変わらない)
© - BASE, Inc.ERRORS!Tests: , Assertions: , Errors: .Expectation failed for method name is equal to when invoked time(s).Method was expected to be called times,actually called times.
© - BASE, Inc.Mockが妨げるユニットテスト• 「顧客がbanされている」というケースではTripRepository::getByCustomerId()は呼ぶ必要がなくなった。• しかし、ユニットテストは呼び出されることを期待しているため、テストは失敗する
© - BASE, Inc.“Mock” を定義するためのxUTPによる語彙整理Test Double: テスト固有の同等物Mock: テスト対象に適切に使⽤されているか検証するRefs ॻ੶ʰxUnit Test Patterns: Refactoring Test Codeʱ/ Chapter 23. Test Double Patterns
© - BASE, Inc.このトーク内での “Mock” は「広義のMock Object」Refs ॻ੶ʰςετۦಈ։ൃʱ/ C ϢχοτςετपลͷࣝͷཧͱTDD֦ுͷࢼΈ• xUTPの語彙整理により、「広義のMockObject」‧「狭義のMock Object」を分けて議論できるようになった• このトークでの “Mock” もTest Doubleの範囲をざっくりと含める「広義のMock Object」とする
© - BASE, Inc.変更頻度が⾼いユニットテストがあります変更頻度が⾼いユニットテストがあります問題障害戦略Mockが妨げるユニットテスト“的確なエクスペクテーション”
© - BASE, Inc.的確なエクスペクテーションRefs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷΛௌ͘“意図を明確にするためには、スタブとエクスペクテーション‧アサーションをきっちり区別するとよい。”
© - BASE, Inc.区別するための指標:コマンド/クエリRefs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷΛௌ͘ΫΤϦʹΞϩʔΞϯεɺίϚϯυʹΤΫεϖΫςʔγϣϯ• コマンドとは、 副作⽤を伴うことのある呼び出し• オブジェクトの外の世界を変化させる• ex. DBのレコード保存‧状態変化• 複数回実⾏した場合、システム状態は違ったものになる => エクスペクテーション
© - BASE, Inc.区別するための指標:コマンド/クエリ• クエリは、外の世界を変化させない• 何回呼んでも呼ばなくても構わない• ⇒ アローアンスRefs ॻ੶ʰ࣮ફςετۦಈ։ൃʱ / ୈ20ষ ςετͷΛௌ͘ΫΤϦʹΞϩʔΞϯεɺίϚϯυʹΤΫεϖΫςʔγϣϯ
© - BASE, Inc.戦略を使って改善する
© - BASE, Inc.変更頻度が⾼いユニットテストがあります > Mockが妨げるユニットテスト変更頻度が⾼いユニットテストがあります問題障害戦略Mockが妨げるユニットテスト“的確なエクスペクテーション”判断評価. 戦略のメリット. を評価する. 戦略のデメリット. を評価する
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“的確なエクスペクテーション”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• テストコードのモックオブジェクトが適切にコントロールされる• 対象オブジェクトが他オブジェクトとどう協調するかがテストによって⽰される
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“的確なエクスペクテーション”のメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• 動作コードの変更に対する変更頻度の減少
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“的確なエクスペクテーション”のデメリットϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ• すべてエクスペクテーションにすると、過剰ではあるがテスト対象オブジェクトの内部処理が”より”テストされる。
© - BASE, Inc.• ৽نςετ࡞ίετൃੜ• طଘςετҡ࣋ίετൃੜ• ςετ࣮ߦ࣌ؒͷͪίετൃੜ• ࣗಈςετͷͨΊͷCIҡ࣋ίετൃੜ• Ϣχοτςετͷֶशίετൃੜ“的確なエクスペクテーション”の評価ϢχοτςετʹΑΔίετݮϢχοτςετͷ࡞ɾҡ࣋ίετVS• खಈϢχοτςετͷίετݮ• ܽؕͷૣظൃݟʹΑΔमਖ਼ίετݮ• υΩϡϝϯςʔγϣϯίετݮ• σόοάίετݮ• ઃܭվળʹΑΔϝϯςφϯείετݮ
© - BASE, Inc.まとめ:変更頻度が⾼いユニットテストがあります > Mockが妨げるユニットテスト変更頻度が⾼いユニットテストがあります問題障害戦略Mockが妨げるユニットテスト“的確なエクスペクテーション”判断評価. ドキュメンテーションコスト削減. 既存テスト維持コスト削減. ⽋陥の早期発⾒による修正コスト削減><
© - BASE, Inc.このトークの流れ. 取り上げる現場の問題. ユニットテストの⽬的. 現場の問題の整理と評価.まとめ. 参考‧引⽤資料
© - BASE, Inc.まとめ• 課題の分析‧それに対する原則‧戦略は多くの⽂献で⽰されている• しかし、それらにはメリット‧デメリットがそれぞれあり、絶対的な正解はない• 状況に応じてどの選択肢が適切か判断する必要がある
© - BASE, Inc.“やり⽅”を覚える ⇒ “考え⽅”を⾝につける
© - BASE, Inc.このトークの流れ. 取り上げる現場の問題. ユニットテストの⽬的. 現場の問題の整理と評価. まとめ.参考‧引⽤資料
© - BASE, Inc.今回取り上げなかった「テスト駆動開発」についてhttps://speakerdeck.com/hgsgtk/tesutokaxin-iwojie-jue-surutesutoqu-dong-kai-fa-falseahuroti-at-phpkanhuarensuxian-tai-
© - BASE, Inc.今回取り上げなかった「レガシーコード改善のテスト」についてhttps://speakerdeck.com/hgsgtk/phpbaziyonatuputojue-ji-ripureisuwozhi-etayunitutotesuto-number-phpcon
© - BASE, Inc.今回取り上げなかった「個⼈としてのテストの始め⽅」についてhttps://speakerdeck.com/hgsgtk/tesutowoshu-itakotokanaiensiniakatesutowoshu-keruyouninarumateyatutakoto-at-phpkanhuarensuguan-xi-
© - BASE, Inc.参考‧引⽤資料 - トーク内で引⽤した書籍• 『xUnit Test Patterns: Refactoring Test Code』• 『テスト駆動開発』• 『実践テスト駆動開発』• 『マイクロサービスアーキテクチャ』• 『レガシーコード改善ガイド』• 『プログラミング⾔語Go』
© - BASE, Inc.参考‧引⽤資料 - トーク構築に寄与した書籍• 『オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て⽅』• 『ソフトウェア‧テストの技法 第2版』• 『プログラマが知るべき97のこと』• 『リファクタリング 既存のコードを安全に改善する』• 『Clean Code アジャイルソフトウェア達⼈の技』• 『初めての⾃動テスト』• 『Succeeding with Agile: Software Development Using Scrum』• 『エキスパートPythonプログラミング 改訂2版』
© - BASE, Inc.?>