--> -->
#title(Mockeyで部分的にmethodを差し替えてテストする) * Mockeyで部分的にmethodを差し替えてテストする [#ebf22b8f] 結論だけ書くと超単純で、マニュアルのままなのですが、何故かはまったのでメモです。~ ~ 以下のようなメソッドをテストしたいとします。~ &size(11){(20181223追記:SubClassを使ったパターンでした)};~ #code(php){{ namespace App\Http\Manager; class MyClass { function doSomething(){ $subclass = new SubClass(); if($subclass->add(1,2)==3){ if($this->add(1,2)==3){ return true; } else { return false; } } } class SubClass { public function add($a,$b) { protected function add($a,$b) { return $a+$b; } } }} ~ &inlinecode{doSomething()};のif文が成功したパターンと失敗したパターンを試験したいのですが、失敗したパターンを発生させることができません。(あくまで例です(^^;)、本当にあり得ないのであればコードを削除しましょう)~ ~ この場合、Laravelに標準でバンドルされているMockeryを使って以下のように書けます。~ &size(11){(当初phpUnitで色々検索して迷宮に填まってしまいました。phpUnitのMockやStubでは実現できません)};~ この場合、phpUnitのMockでは以下のように書けます。~ #code(php){{ namespace tests\Unit; use App\Http\Manager\MyClass; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use Tests\TestCase; class MyTest extends TestCase { use MockeryPHPUnitIntegration; public function testMyClass() { // 成功 $myclass = new MyClass(); $result = $myclass->doSomething(); $this->assertTrue($result); // 失敗 $mock = \Mockery::mock('App\Http\Manager\MyClass')->makePartial(); $mock->shouldReceive('add')->andReturn(5); $mock = $this->createPartialMock(MyClass::class, ['add']); $mock->expects($this->once())->method('add')->willReturn(0); /** @noinspection PhpUndefinedMethodInspection */ $result = $mock->doSomething(); $this->assertFalse($result); } } }} 19行目でテスト対象クラスをフルパス(?)で指定し、&inlinecode{makePartial()};を使用で一部のメソッドのみ差し替えを定義します。 20行目で差し替えるメソッドと返り値を定義します。これ以外のメソッドが&inlinecode{MyClass};に存在しても、それはそのまま実行できます。~ また、Laravelに標準でバンドルされているMockeryでは以下のように書けます。~ #code(php){{ // 失敗 $mock = \Mockery::mock('App\Http\Manager\MyClass')->makePartial()->shouldAllowMockingProtectedMethods(); $mock->shouldReceive('add')->andReturn(0); /** @noinspection PhpUndefinedMethodInspection */ $result = $mock->doSomething(); $this->assertFalse($result); }} Mockeryの方はテスト対象クラスをフルパスで指定しています。 いずれもPartial(部分的)なmockですので、&inlinecode{shouldReceive};で定義された差し替えるメソッド以外は&inlinecode{MyClass};のオリジナルのものがそのまま実行されます。~ &inlinecode{$mock};には、クラス&inlinecode{MyClass};のメソッドがそっくり継承されているので、22行目で&inlinecode{$mock};を使って&inlinecode{doSomething()};を呼び出します。~ ~ 簡単、明快。~ (でもなんかいまいち挙動が・・・再度書き直すかも) ただ、Mockeryだからこそできることというのがいまいち良く分かってない・・・。~ ~ &size(12){実は外部メソッドを呼んでいる処理のテストでoverloadを使おうとしたのですが、LaravelのFWによってautoloadされてしまうためか class already exists が出て使用できませんでした。アノテーションを入れても今度は $name must not be null が出てしまって解決策が見つかっていません。};