Slide 1

Slide 1 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. #xUTP #xUnit #PHPUnit PHPUnit 9 時代の Test Doubleの作り方 1 2021.03.26 ~ 03.28 PHPerKaigi2021 @hgsgtk Kazuki Higashiguchi

Slide 2

Slide 2 text

© 2012-2021 BASE, Inc. 2 PHPUnitの使い方でちょこっと ( ・´ー・`)どやれる(かもしれない) 知識

Slide 3

Slide 3 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. 当発表で得られるもの 1 2 3 MockBuilder::setMethodsがdeprecatedになった背 景を理解する PHPUnit 9 で変わったTest Doubleの作り方をユー ザー視点で理解する モックオブジェクト作成についてPHPUnit 10 の見 通しを知る 3

Slide 4

Slide 4 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. @hgsgtk Kazuki Higashiguchi 東京に住まう Software Developer / BASE BANK, Inc. Manager, Tech Lead 自己紹介 4 (2020.12) Dockerアプリケーショ ン開発実践ガイド > コンテナアプリケー ションの設計セオリー (2019.12) PHPにおけるユニット テストとCI/CD > ユニットテストの育 て方 PHPerKaigi Archives 2021.3.27: 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 2020: 自作して理解するxUnit 2019:「質」のいいユニットテストを書くためのプラクティス 2018: レビューをもらいやすい細かいプルリクの切り分け方

Slide 5

Slide 5 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. PHPUnit 9 ● PHPerのためのテスティングフレームワーク、xUnitアーキテクチャ・ファミリの一つ ○ https://github.com/sebastianbergmann/phpunit ● 2020年2月7日に PHPUnit 9 がリリース ○ https://phpunit.de/announcements/phpunit-9.html ● PHPUnit 9の特徴 ○ PHP7.3 >= ○ いくつかの破壊的変更 (ex. assertEquals 等のパラメータ) ○ いくつかのAPIの非推奨化(ex. expectException) 5

Slide 6

Slide 6 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. 余談: PHPUnit Versions 6 https://phpunit.de/supported-versions.html

Slide 7

Slide 7 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. PHPUnit 9のTest Doubleの作り方に影響する話 MockBuilder::setMethods() の deprecated 7

Slide 8

Slide 8 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. 補足: “Test Double” ● 『xUnit Test Patterns: Refactoring Test Code』(通称: xUTP)にて定義され た語彙整理、テスト固有の同等物を指す ○ http://xunitpatterns.com/Test%20Double.html ● 一般的によく使われるMockはTest Doubleのことを言っていることが多い(”広義 のMock”) 8

Slide 9

Slide 9 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. Pull Request #3687 [Summary] ● onlyMethodsとaddMethodsが追加 されました(リリース: PHPUnit 8.5.0) ● setMethodsが(soft) deprecatedと なりました(リリース: PHPUnit 9) 9 ● MockBuilder::setMethods() を deprecated にするPR (2019/03/10) ○ https://github.com/sebastianbergmann/phpunit/pull/3687

Slide 10

Slide 10 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. PHPUnit 9以前のテストダブルの作り方例 10 MockBuilder::setMethods()でモックオブジェクトにメソッドを設定していた

Slide 11

Slide 11 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. PHPUnit 9時代のテストダブルの作り方:未定義 11 未定義メソッドはMockBuilder::addMethods()で

Slide 12

Slide 12 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. PHPUnit 9時代のテストダブルの作り方:定義済み 12 定義済みメソッドはMockBuilder::onlyMethods()で

Slide 13

Slide 13 text

© 2012-2021 BASE, Inc. 13 ( ・´ー・`)どや

Slide 14

Slide 14 text

© 2012-2021 BASE, Inc. 14 ( ' ^'c彡☆))Д´) パーン

Slide 15

Slide 15 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. addMethods/onlyMethods の背景 15 @DFoxinator によるPR#3687の意図説明(私訳、サマリー) ● setMethods()の場合”クラス内のメソッド存在”が保護されていない ● メソッドを削除してもsetMethods()で作ったテストは通ってしまう ● createMock()は、厳密に「存在しないメソッドをモックしようとしたこと」を教 えてくれることも相まって、動作に混乱が生まれている ● より厳密な機能がほしいユーザー向けに後方互換性を保つ形で機能を提供した ● => setRealMethods ってどうですかという提案 最初 は”setRealMethods”って いう名前で 提案されていたんだね

Slide 16

Slide 16 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. createMockの厳密な振る舞い 16 ● createMockでは、ここのデフォルトの振る舞いが 厳密になっていてエラーになります ● setMethodsで、消えたメソッドに期待値設定して いると、実際のクラスから消えた後も気が付かずに テストが通る『夢の世界に生きるテスト』(※1) になる 「PHPUnitはメソッド消えた時はエラーで落ち てくれるよ」という話は厳密には偽です 「createMock使っていれば」が正確です ( ' ^'c彡☆))Д´) パーン ※1 『オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつ づける柔軟アプリケーションの育て方』 9.2 受信メッセージをテストする

Slide 17

Slide 17 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. 具体的なユースケース想定 17 ● 「みんなcreateMock使ってるのでは?」というsebastianbergmannのリアク ションもあった(みなさんどうですか?) ● 今回のPRにおける具体的な使用例としては、クラスの一部のメソッドをモックす るシーン ○ その場合はcreateMockの対応しないユースケースである

Slide 18

Slide 18 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. createPartialMockによるモックはどうか 18 ● createPartialMockがpartial mockingの用途で使えるが、これも setMethods を 内部で使用している(PHPUnit 9現在) ○ ゆえに setMethods に対して指摘された問題は回避できない ● PHPUnit 10に向けたmasterブランチはonlyMethodsを使うようになっている ■ https://github.com/sebastianbergmann/phpunit/issues/3769 ■ https://github.com/sebastianbergmann/phpunit/commit/baa0ccee4c1eb3f697fec256059119c8874b949d

Slide 19

Slide 19 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. addMethods/onlyMethods となる 19 setMethodsに対する後方互換性の維持 と移行戦略 内部実装も徐々に置き換えていく構想 from 8 -> 10

Slide 20

Slide 20 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. PHPUnit 10にてsetMethodsは削除される 20 https://github.com/sebastianbergmann/phpunit/blob/dc2d6c4e2fdd9d53606d2a578de0ba65a162ed5b/ChangeLog-10.0.md Changes in PHPUnit 10.0ではすでに削除が明示されています

Slide 21

Slide 21 text

© 2012-2021 BASE, Inc. 21 Dive deep into PHPUnit

Slide 22

Slide 22 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. Impl: @deprecated setMethods() https://github.com/sebastianbergmann/phpunit/pull/3687 22 src/Framework/MockObject/MockBuilder.php にマーキングが追加される

Slide 23

Slide 23 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. 後日談: mock設定有無判別のリファクタリング src/Framework/MockObject/MockBuilder.php https://github.com/sebastianbergmann/phpunit/pull/3687 23 変更の理由が同じpropertyならば、boolなflag propetryよりnullable propertyにする選択肢 をとったほうがシンプルになる

Slide 24

Slide 24 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. MockBuilder::addMethods() の責務 https://github.com/sebastianbergmann/phpunit/blob/943003a5b2bd1292eb5f202c41be34770284d214/src/Framework/MockObject/MockBuilder.php#L27 24 1. ReflectionClassからメソッドが存在する かをチェックする ○ 存在する場合はaddMethodsの利用シー ンではないため例外 (CannotUseAddMethodsException) ○ onlyMethods()の実装はここが逆の判定 2. mock objectのメソッドに追加する

Slide 25

Slide 25 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. 余談: テストコードから読む例外ハンドリング https://github.com/sebastianbergmann/phpunit/blob/c8be2686e0f97fa648806a516f10f889e5a0be45/tests/unit/Framework/MockObject/MockBuilderTest.php#L75 25 ● 存在するメソッドの場合・存在しない場合両 方でテストを用意 微妙なユースケースはテストコード を読むと挙動が明らかになるよ

Slide 26

Slide 26 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. 参考文献 26 https://www.amazon.co.jp/gp/product/B004 X1D36K?ref=dbs_p2d_P_R_kindle_available_ T1 https://www.amazon.co.jp/dp/4274217884 https://www.amazon.co.jp/dp/B01L8SEVYI

Slide 27

Slide 27 text

© 2012-2019 BASE, Inc. © 2012-2021 BASE, Inc. まとめ ● PHPUnit 9 でsetMethods 使っていた人は onlyMethods/addMethods に変えま しょう ○ PHPUnit 10 で setMethods は削除されます ● createMock を使うと脳死で onlyMethods と同等の振る舞いが得られる ● createParitialMock を使うと PHPUnit 9 までは setMethods と同じ振る舞い ○ PHPUnit 10 になるとこちらを活用すると良いでしょう ● 最初は setRealMethods って名前で提案されてたんだよっていう豆知識を” ( ・´ー・`) どや”って “( ' ^'c彡☆))Д´) パーン” されましょう

Slide 28

Slide 28 text

© 2012-2021 BASE, Inc. Thanks at #phperkaigi