エクスペクションの宣言
Note: エクスペクション(期待)を動作させるためには、
Mockery::close()
をtearDown
や_before
のようなコールバックメソッドの中で実行する必要があります。(Mockeryが他のフレームワークと統合されているかどうかによります。)この静的呼び出しで、現在のテストで使用したMockeryのコンテナをクリーンアップし、エクスペクションのために必要な検査タスクが実行されます。
モックオブジェクトを生成したら、それがどのように振る舞うべきか(さらに、どのように呼び出されるか)を正確に宣言し始めることになります。これはMockeryのエクスペクション宣言の役割です。
メソッド呼び出しのエクスペクション宣言
名前を指定し、そのメソッドが呼び出されるのを期待していることをテストダブルへ伝えるには、shouldReceive
メソッドを使用します。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method');
このエクスペクションが、さらに付け加える他のエクスペクションと制約の開始点となります。
期待しているメソッド呼び出しを2つ以上指定できます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method_1', 'name_of_method_2');
これら全て、エクスペクションや制約をチェーンとして受け付けます。
メソッド呼び出しのエクスペクションと、返される値を宣言することもできます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive([
'name_of_method_1' => 'return value 1',
'name_of_method_2' => 'return value 2',
]);
メソッド呼び出しのエクスペクションと戻り値を更に短く指定する方法です。
$mock = \Mockery::mock('MyClass', ['name_of_method_1' => 'return value 1', 'name_of_method_2' => 'return value 2']);
これら全て、エクスペクションや制約をチェーンとして受け付けます。
テストダブルで名前を指定し、呼び出されないことを期待する宣言もできます。
$mock = \Mockery::mock('MyClass');
$mock->shouldNotReceive('name_of_method');
これは、shouldReceive()->never()
呼び出しの短縮形です。
メソッド引数のエクスペクション宣言
エクスペクションを宣言した全てのメソッドに対し、期待する引数のリストと一致するメソッド呼び出しのみに適用するように制約を付け加えることができます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->with($arg1, $arg2, ...);
// もしくは
$mock->shouldReceive('name_of_method')
->withArgs([$arg1, $arg2, ...]);
組み込み済みのマッチャークラス(後述)を使用し、より柔軟に追加できます。たとえば、with()
の引数リストの中の\Mockery::any()
マッチャーを指定すると、その位置ではどんな引数でも一致します。MockeryではHamcrestライブラリーのマッチャーも使用できます。たとえば、Hamcrest関数のanything()
は、\Mockery::any()
と同じ働きをします。
つまり、重要な注意点は、指定された全エクスペクションは、指定したメソッドが指定した引数で呼び出された場合のみ、適用されるということです。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')->with('Hello');
$mock->foo('Goodbye'); // NoMatchingExpectationExceptionが投げられる
これにより、期待した呼び出しに渡される引数に基づいて、別々のエクスペクションを指定できるのです。
クロージャを使用した引数のマッチング
組み込み済みのマッチャーを各引数に指定する代わりに、渡された引数全部を一度にマッチングする、クロージャを渡すことができます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->withArgs(closure);
メソッド呼び出し時に渡された引数を指定したグロージャーは受け取ります。このエクスペクションはメソッドがコールされ、渡した引数がクロージャでtureと評価される場合に、適用されます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')->withArgs(function ($arg) {
if ($arg % 2 == 0) {
return true;
}
return false;
});
$mock->foo(4); // エクスペクションはマッチ
$mock->foo(3); // NoMatchingExpectationExceptionが投げられる
引数を不問、引数なし
どんな引数がメソッド呼び出しで渡されてもマッチする、エクスペクションを宣言することができます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->withAnyArgs();
他のものが指定されない限り、これはデフォルトで指定されます。
引数がないメソッドコールに一致するエクスペクションを宣言することができます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->withNoArgs();
戻り値のエクスペクション宣言
モックオブジェクトに対し、期待しているメソッド呼び出しでどのような戻り値が返されるのかをMockeryへ指示できます。
そのためには、andReturn()
メソッドを使用します。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturn($value);
これは、期待しているメソッド呼び出しから返される値を指定しています。
エクスペクションへ複数の返り値を指定することも可能です。返り値を続けて記述することで、メソッドの呼び出しごとにどんな値が返されるかをMockeryへ指示します。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturn($value1, $value2, ...)
最初の呼び出しでは$value1
が返され、2つ目の呼び出しでは$value2
が返されます。
宣言した戻り値より、多くの回数メソッドが呼び出された場合、Mockeryはその後のメソッドコールでは最後の値を返します。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('foo')->andReturn(1, 2, 3);
$mock->foo(); // int(1)
$mock->foo(); // int(2)
$mock->foo(); // int(3)
$mock->foo(); // int(3)
別の記述法で、同じ指定が行えます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnValues([$value1, $value2, ...])
引数の代わりに、シンプルに配列を引き受けます。返される順番は指定した配列のインデックスの数値により決まり、指定した値が切れた場合は最後の配列のメンバーが、それ以降の全ての呼び出しで返されます。
以下の2つの書き方も、テストを読む人とのコミュニケーションに役立つでしょう。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnNull();
// もしくは
$mock->shouldReceive('name_of_method')
->andReturn([null]);
メソッドコールはnull
を返す、もしくは何も返さないことをモックオブジェクトへ知らせます。
ときどき、メソッドへ渡された引数に基づいて、返り値を計算したい場合があります。1つ以上のクロージャを受け取る、andReturnUsing()
メソッドを使用して行えます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnUsing(closure, ...);
クロージャは、andReturn()
への追加引数として渡すことで、キューすることもできます。
Note: 現在、
andReturnUsing()
とandReturn()
を混ぜて使用できません。
fluid interfacesをモックしている場合、以下のメソッドが役立ちます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andReturnSelf();
モックしているクラス名を返り値として指定します。
例外発生のエクスペクション
モックオブジェクトのメソッドで例外を投げるように指示することも可能です。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andThrow(Exception);
呼び出し時に、指定したException
オブジェクトを投げます。
オブジェクトではなく、Exception
クラスとメッセージを引数で渡し、モックしたメソッドからException
を投げることもできます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andThrow(exception_name, message);
publicプロパティの設定
メソッド呼び出しに一致するエクスペクションを使用し、andSet()
かset()
により、モックオブジェクトのpublicプロパティへ特定の値をセットできます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->andSet($property, $value);
// もしくは
$mock->shouldReceive('name_of_method')
->set($property, $value);
モックしているクラスの本当のメソッドを呼び出し、その結果を返したい場合は、passthru()
メソッドで戻り値のキューをバイパスするようにエクセプションへ指示します。
passthru()
本当のメソッドに対しマッチングと呼び出し回数のバリデーションが利用でき、その場合も本当のクラスメソッドを期待する引数で呼び出します。
呼び出し回数のエクスペクション宣言
メソッド呼び出しの引数へエクスペクションを指定し、同じメソッド呼び出しに戻り値を指定するのに加え、メソッドが呼び出される回数のエクスペクションを指定することができます。
呼び出し回数のエクスペクションが一致しなかった場合、\Mockery\Expectation\InvalidCountException
が投げられます。
Note: たとえば、PHPUnitの
tearDown()
メソッドなどで、テストの最後に\Mockery::close()
を絶対に呼び出す必要があります。呼び出さないとMockeryはモックオブジェクトに対して実行された呼び出しを検査しません。
メソッドの呼び出し回数を問わない宣言を行うことができます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->zeroOrMoreTimes();
他のものが指定されない場合、これが全メソッドのデフォルトとなります。
特定の回数メソッドが呼ばれることを期待する場合は、以下の要領でMockeryに指示します。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->times($n);
$n
はメソッドが呼び出されるべき回数です。
よく使用される2つの場合は、短縮メソッドが用意されています。
期待しているメソッドが一回のみ呼び出されることを宣言するには:
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->once();
期待しているメソッドが2回呼び出されることを宣言するには:
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->twice();
期待しているメソッドが、呼び出されないことを宣言するには:
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->never();
呼び出し回数モディファイヤー
呼び出し回数エクスペクションには、モデファイヤーが使えます。
もし、Mockeryにメソッドの最低実行回数を指定したい場合は、atLeast()
を使います。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->atLeast()
->times(3);
atLeast()->times(3)
は、(指定したメソッド引数とマッチした)呼び出しが最低でも3回実行されることを意味しています。
同様に、実行すべき最大実行回数をMockeryへ指示できます。atMost()
を使用してください。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->atMost()
->times(3);
atMost()->times(3)
は、4回以上呼び出されないことを意味しています。メソッドが一回も実行されない場合、このエクスペクションには一致します。
呼び出し回数の範囲をbetween()
で指定することもできます。
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method')
->between($min, $max);
実際にこれは、atLeast()->times($min)->atMost()->times($max)
と同じですが、短縮されています。 引数のないtimes()
を続けて、APIの自然言語的読みやすさを保つことができます。
エクスペクション宣言ユーティリティー
似たような指定のメソッドと関連して、特定の順番でメソッドが呼び出されるのを期待する宣言ができます。
ordered()
順番はモックを準備するときに、このモディファイヤーが実際に使用された順番で決定されます。
(名前や番号で指定される)オーダーグループに従い、メソッドを宣言できます。グループ内のメソッドは、どんな順番でも呼び出されますが、グループ外の順番は、他のグループとの順番通りになる必要があります。
ordered(group)
メソッド2の前に実行されるグループ1,そのグループ1より前に実行されるメソッド1という指定が可能なわけです。
ordered()
やordered(group)
を呼び出す前に、この順番は現在のモックオブジェクトのみに適用されるわけでなく、全モックオブジェクト間の順番であると宣言できます。
globally()
これは、複数のモック間での実行順エクスペクションを命令するものです。
byDefault()
により、デフォルトのエクスペクションを指定できます。デフォルトでないエクスペクションが作成されない限り、このデフォルトエクスペクションが適用されます。
byDefault()
エクスペクションは即時に以前のデフォルトエクスペクションに置き換えられます。デフォルトモックをユニットテストsetup()
で用意し、特定のテストで必要になれば、後ほど調整できるため便利です。
エクスペクションのチェーンで、現在のモックオブジェクトを返す場合は:
getMock()
一行でモックの準備を行いたい場合に便利です。たとえば:
$mock = \Mockery::mock('foo')->shouldReceive('foo')->andReturn(1)->getMock();