PHPのMockeryでメソッドが特定の引数を受け取った時のみ動作を変えるモック作成方法

by @dekokun on 2014/03/08 11:55

Tagged as: PHP, Mockery.

テスト、モック使用が重要ですね。

DIとモックで快適なテストライフを送りましょう。

さて、現在、モックライブラリとしてMockeryを使っております。

先日、ある引数が渡ってきた時だけ動作を変更するようなモックを作りたかったのですが軽く調べただけでは出てこないような情報だったためこちらに記載しておきます。

具体的には、withで引数指定してshouldReturnで返り値を決定し、その後passthruを指定することにより、それ以外の引数の際は元のメソッドの動作を行わせることができます。

ただし、以下でテストしているようにpassthruの後にwithで引数指定を行った場合は指定が効きませんのでご注意。

ただ、一点、上記のようなテストはテストが実装の内部に依存しすぎた脆いテストになりがちであり、そもそも上記のようなモックが必要だと思った時点で設計に敗北している可能性が高いのでそこだけ注意。

<?php

use \Mockery as m;

class MockeryTest extends PHPUnit_Framework_TestCase
{
    private $mock;

    public function setUp()
    {
        $mock = m::mock('\HOGE', ['a', 'b']);
        // 引数がhogeだったら"mock : hoge"を返す
        $mock->shouldReceive('get')->with('hoge')->andReturn('mock : hoge');
        // 引数がfugaだったら"mock : fuga"を返す
        $mock->shouldReceive('get')->with('fuga')->andReturn('mock : fuga');
        // それ以外の引数の場合は元のクラスの動作をさせる
        $mock->shouldReceive('get')->passthru();
        // 引数がfooだったら"mock : foo"を返す ?
        $mock->shouldReceive('get')->with('foo')->andReturn('mock : foo');
        $this->mock = $mock;
    }

    /**
    * @test
    */
    public function Mockeryが特定の引数の時だけちゃんと動く()
    {
        $this->assertEquals('mock : hoge', $this->mock->get('hoge'));
    }

    /**
    * @test
    */
    public function withでの指定が2個目でも成功する()
    {
        $this->assertEquals('mock : fuga', $this->mock->get('fuga'));
    }

    /**
    * @test
    */
    public function withで指定されていなかった場合は元のメソッドと同じ動きをする()
    {
        $this->assertEquals('original : piyoab', $this->mock->get('piyo'));
    }

    /**
    * @test
    */
    public function passthruの後の指定は無効()
    {
        // with('foo')->andReturn('mock : foo') としたはずだが…
        $this->assertEquals('original : fooab', $this->mock->get('foo'));
    }
}

class HOGE
{

    protected $a;
    protected $b;

    public function __construct($a, $b)
    {
        $this->a = $a;
        $this->b = $b;
    }
    public function get($str)
    {
        return 'original : ' . $str . $this->a . $this->b;
    }
}

上記を実行すると、テストは全部通ります。めでたしめでたし。

$ vendor/bin/phpunit test.php
PHPUnit 4.0.3 by Sebastian Bergmann.

....

Time: 36 ms, Memory: 3.25Mb

OK (4 tests, 4 assertions)

まとめ

  • mockeryを使った際に、メソッドがある引数を受け取った時だけモックの動きを変え、それ以外の引数の時は元のメソッドの動きをさせる場合はwithとpassthruを組み合わせて使いましょう
comments powered by Disqus