--> -->

skimemo


Laravel-20181220 の変更点


#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 が出てしまって解決策が見つかっていません。};