これは何
- laravel で Mockery を使ったパーシャルモックの作り方について試してみたものです。
まとめ
- 最初にまとめを書いておきます。
// makePartialで、パーシャルモックになる。
$m = \Mockery::mock('App\Libs\MyHello')->makePartial();
// makePartialすると **全て** のメソッドが元クラスのメソッドに中継されるので
// 明示的に、中継メソッドを指定したい場合は、passthru を使う。
$m->shouldReceive('dog')->passthru();
// クラス名を間違えて(うっかりエルが多すぎた)もエラーにならず、メソッドなし空モックができてしまうので、
$m = \Mockery::mock('App\Libs\MyHellllllo')->makePartial();
// クラス指定は ::class(完全修飾したクラス名を得ることができる)を使ったほうが間違いが無い。
// PHP: クラスの基礎 - Manual
// http://www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.class.class
$m = \Mockery::mock(MyHello::class)->makePartial();
// コンストラクタを呼びたい場合、第2パラメータで指定すればよい。
$m = \Mockery::mock('App\Libs\MyHello', ['aa','コンストラクタの引数'])->makePartial();
試してみた
- 詳しくはこんな感じでした。
ソース
app/Libs/MyHello.php
<?php
namespace App\Libs;
class MyHello
{
function __construct($msg)
{
echo "Hello->__construct({$msg})\n";
}
public function dog($msg)
{
echo "Hello->dog({$msg})\n";
}
public function cat($msg)
{
echo "Hello->cat({$msg})\n";
}
}
tests/Unit/MyHelloTest.php
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Libs\MyHello;
class MyHelloTest extends TestCase
{
/**
* 普通にパーシャルモックを作る
*/
public function test10()
{
echo "===".__METHOD__."\n";
$m = \Mockery::mock('App\Libs\MyHello', ["CONSTRACT"]);
// モックメソッドがない場合、オリジナルのメソッドに中継
$m->makePartial();
// cat() のモックメソッド定義
$m->shouldReceive('cat')->with(\Mockery::on(function ($msg) {
echo "Hello->cat({$msg}) MOCK\n";
return true;
}));
echo get_class($m) . "\n";
$m->dog('DOG'); // オリジナルのdog()をcall
$m->cat('CAT'); // 上記のモックのcat()をcall
$this->assertTrue(true);
}
/**
* makePartial() を使わなくても
* passthru() でパーシャルモックを作れる。
*/
public function test11()
{
echo "===".__METHOD__."\n";
$m = \Mockery::mock('App\Libs\MyHello', ["CONSTRACT"]);
// dog() をオリジナルのdog()に中継
$m->shouldReceive('dog')->passthru();
// cat() のモックメソッド定義
$m->shouldReceive('cat')->with(\Mockery::on(function ($msg) {
echo "Hello->cat({$msg}) MOCK\n";
return true;
}));
echo get_class($m) . "\n";
$m->dog('DOG'); // オリジナルのdog()をcall
$m->cat('CAT'); // 上記のモックのcat()をcall
$this->assertTrue(true);
}
/**
* オリジナルのクラスの指定は、文字列('App\Libs\MyHello')以外にも
* MyHello::class でも指定できる。
*/
public function test12()
{
echo "===".__METHOD__."\n";
$m = \Mockery::mock(MyHello::class, ["CONSTRACT"])
->makePartial();
$m->shouldReceive('cat')->with(\Mockery::on(function ($msg) {
echo "Hello->cat({$msg}) MOCK\n";
return true;
}));
echo get_class($m) . "\n";
$m->dog('DOG');
$m->cat('CAT');
$this->assertTrue(true);
}
/**
* オブジェクト(new クラス名)でも指定できる。
*/
public function test20()
{
echo "===".__METHOD__."\n";
$m = \Mockery::mock(new MyHello("CONSTRACT"));
$m->shouldReceive('cat')->with(\Mockery::on(function ($msg) {
echo "Hello->cat({$msg}) MOCK\n";
return true;
}));
echo get_class($m) . "\n";
$m->dog('DOG');
$m->cat('CAT');
$this->assertTrue(true);
}
/**
* クラス名はnamespace含めてフルで指定しないと、別物になるので注意
*/
public function test30()
{
echo "===".__METHOD__."\n";
$m = \Mockery::mock('MyHello'); // use App\Libs\MyHello; があっても、これは別物になる
$m->shouldReceive('cat')->with(\Mockery::on(function ($msg) {
echo "Hello->cat({$msg}) MOCK\n";
return true;
}));
echo get_class($m) . "\n";
//$m->dog('DOG'); // このメソッドは存在しないのでエラーになる
$m->cat('CAT');
$this->assertTrue(true);
}
}
phpunit実行結果
$ vendor/bin/phpunit
PHPUnit 7.5.6 by Sebastian Bergmann and contributors.
..===Tests\Unit\MyHelloTest::test10
Hello->__construct(CONSTRACT)
Mockery_0_App_Libs_MyHello
Hello->dog(DOG)
Hello->cat(CAT) MOCK
.===Tests\Unit\MyHelloTest::test11
Hello->__construct(CONSTRACT)
Mockery_0_App_Libs_MyHello
Hello->dog(DOG)
Hello->cat(CAT) MOCK
.===Tests\Unit\MyHelloTest::test12
Hello->__construct(CONSTRACT)
Mockery_0_App_Libs_MyHello
Hello->dog(DOG)
Hello->cat(CAT) MOCK
.===Tests\Unit\MyHelloTest::test20
Hello->__construct(CONSTRACT)
Mockery_1_App_Libs_MyHello_App_Libs_MyHello
Hello->dog(DOG)
Hello->cat(CAT) MOCK
.===Tests\Unit\MyHelloTest::test30
Mockery_2__MyHello
Hello->cat(CAT) MOCK
. 7 / 7 (100%)
Time: 260 ms, Memory: 14.00 MB
OK (7 tests, 13 assertions)