skimemo


Laravel-20181220

_ Mockeyで部分的にmethodを差し替えてテストする

結論だけ書くと超単純で、マニュアルのままなのですが、何故かはまったのでメモです。

以下のようなメソッドをテストしたいとします。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
namespace App\Http\Manager;class MyClass {    function doSomething(){        if($this->add(1,2)==3){            return true;        } else {            return false;        }    }    protected function add($a,$b) {        return $a+$b;    }} 


doSomething()のif文が成功したパターンと失敗したパターンを試験したいのですが、失敗したパターンを発生させることができません。(あくまで例です(^^;)、本当にあり得ないのであればコードを削除しましょう)

この場合、phpUnitのMockでは以下のように書けます。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
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 = $this->createPartialMock(MyClass::class, ['add']);        $mock->expects($this->once())->method('add')->willReturn(0);        /** @noinspection PhpUndefinedMethodInspection */        $result = $mock->doSomething();        $this->assertFalse($result);     }} 

また、Laravelに標準でバンドルされているMockeryでは以下のように書けます。

  1
  2
  3
  4
  5
  6
        // 失敗        $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ですので、shouldReceiveで定義された差し替えるメソッド以外はMyClassのオリジナルのものがそのまま実行されます。
$mockには、クラスMyClassのメソッドがそっくり継承されているので、22行目で$mockを使ってdoSomething()を呼び出します。

簡単、明快。
ただ、Mockeryだからこそできることというのがいまいち良く分かってない・・・。

実は外部メソッドを呼んでいる処理のテストでoverloadを使おうとしたのですが、LaravelのFWによってautoloadされてしまうためか class already exists が出て使用できませんでした。アノテーションを入れても今度は $name must not be null が出てしまって解決策が見つかっていません。


Last-modified: 2018-12-23 (日) 11:37:24