PHPUnitのモックの使い方について、毎回同じような検索をかけて同じ記事にたとっていたのですが、その記事がある日突然消えたので
「いい加減自分の中に落とし込まないとやばい!」
と思い、PHPUnitのソースコードから理解することにしました。
前提
諸事情により、この記事は PHPUnit 9.6 を前提にしています。
参照ソース:
https://github.com/sebastianbergmann/phpunit/blob/9.6/src/Framework/TestCase.php
createMock
protected function createMock(string $originalClassName): MockObject
{
return $this->createMockObject($originalClassName);
}
private function createMockObject(string $originalClassName): MockObject
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->getMock();
}
createMock() は getMockBuilder() に定型の設定をしただけのラッパー。
createMock のデフォルト動作
- コンストラクタは実行されない
- 未設定のメソッドは適当なデフォルト値を返す
return type に従い、null だけでなく以下のような値が返る- bool → false
- int → 0
- string → ''
- array → []
- final / private / static メソッドはモック化されない
例:updateメソッドが、引数'something' で一度だけコールされることを確かめる
$service= $this->createMock(XXXService::class);
$service->expects($this->once())
->method('update')
->with($this->equalTo('something'));
※ with() はデフォルトで equalTo() と同等の比較が行われる
getMockBuilder
PHPUnitのソースコード
public function getMockBuilder(string $className): MockBuilder
{
$this->recordDoubledType($className);
return new MockBuilder($this, $className);
}
こちらは モックの構成を細かくカスタマイズ可能。
- コンストラクタ実行の有無(デフォルトで実行)
- 特定のメソッドだけモック化
などなどの設定ができます。
例:コンストラクタ実行する・updateメソッドだけモック化する場合
$mock = $this->getMockBuilder(XXXService::class)
->onlyMethods(['update'])
->getMock();
createMockとgetMockBuilderの違いまとめ
- createMock:いい感じのモック構成を自動で作ってくれる
- getMockBuilder:モック構成をカスタマイズできる
The createStub($type) and createMock($type) methods immediately return a test double object for the specified type (interface or class). The creation of this test double is performed using best practice defaults. The __construct() and __clone() methods of the original class are not executed and the arguments passed to a method of the test double will not be cloned. If these defaults are not what you need then you can use the getMockBuilder($type) method to customize the test double generation using a fluent interface.
Googleに翻訳してもらうと、
メソッドcreateStub($type)とcreateMock($type)メソッドは、指定された型(インターフェースまたはクラス)のテストダブルオブジェクトを即座に返します。このテストダブルの作成は、ベストプラクティスのデフォルトに基づいて実行されます。元のクラスのメソッド __construct()と__clone()メソッドは実行されず、テストダブルのメソッドに渡された引数は複製されません。これらのデフォルト設定が適切でない場合は、getMockBuilder($type)メソッドを使用して、Fluentインターフェースを用いてテストダブル生成をカスタマイズできます。
| 機能 | createMock() | getMockBuilder() |
|---|---|---|
| 初期設定 | ベストプラクティス設定済 | 自分で設定 |
| コンストラクタ | 実行されない | デフォルトは実行(変更可) |
| 未設定メソッドの戻り値 | 型に応じたデフォルト値 | 設定内容に依存 |
| 特定メソッドのみモック化 | 不可 | 可 |
| 柔軟性 | 低 | 高 |
原則createMock推奨、特殊なケースではgetMockBuilder
基本的にはベストプラクティスの設定がされているcreateMockを使うのが良さそう。
ただし以下のような場合は、getMockBuilderを使う必要がある。
- コンストラクタを実行させたい時
- 一部だけモック化して、他はプロダクトコードの処理を使いたい時
- クラス内で new されるオブジェクトの差し替え
さいごに
PHPUnitでモックを扱う際にいつも見ていたこの記事が見られなくなってしまったので、自分でまとめておくことの重要性を感じました。
https://taisablog.com/archives/55