如何模拟对象工厂

Seb*_*uer 6 php phpunit factory-pattern

我使用Factories(请参阅http://www.php.net/manual/en/language.oop5.patterns.php获取模式)来增加代码的可测试性.一个简单的工厂看起来像这样:

class Factory
{
    public function getInstanceFor($type)
    {
        switch ($type) {
            case 'foo':
                return new Foo();
            case 'bar':
                return new Bar();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是使用该工厂的示例类:

class Sample
{
    protected $_factory;

    public function __construct(Factory $factory)
    {
        $this->_factory = $factory;
    }

    public function doSomething()
    {
        $foo = $this->_factory->getInstanceFor('foo');
        $bar = $this->_factory->getInstanceFor('bar');
        /* more stuff done here */
        /* ... */
    }
}
Run Code Online (Sandbox Code Playgroud)

现在进行适当的单元测试,我需要模拟将返回类的存根的对象,这就是我遇到的问题.我认为有可能这样做:

class SampleTest extends PHPUnit_Framework_TestCase
{
    public function testAClassUsingObjectFactory()
    {
        $fooStub = $this->getMock('Foo');
        $barStub = $this->getMock('Bar');

        $factoryMock = $this->getMock('Factory');

        $factoryMock->expects($this->any())
            ->method('getInstanceFor')
            ->with('foo')
            ->will($this->returnValue($fooStub));

        $factoryMock->expects($this->any())
            ->method('getInstanceFor')
            ->with('bar')
            ->will($this->returnValue($barStub));
    }
}
Run Code Online (Sandbox Code Playgroud)

但是当我运行测试时,这就是我得到的:

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) SampleTest::testDoSomething
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-bar
+foo

FAILURES!
Tests: 1, Assertions: 0, Failures: 1.
Run Code Online (Sandbox Code Playgroud)

所以显然不可能让mock对象根据传递的方法参数返回不同的值.

如何才能做到这一点?

edo*_*ian 6

问题是PHPUnit Mocking不允许你这样做:

$factoryMock->expects($this->any())
        ->method('getInstanceFor')
        ->with('foo')
        ->will($this->returnValue($fooStub));

$factoryMock->expects($this->any())
        ->method('getInstanceFor')
        ->with('bar')
        ->will($this->returnValue($barStub));
Run Code Online (Sandbox Code Playgroud)

你只能有一个expects每个->method();.它不知道参数->with()不同的事实!

所以你只需->expects()用第二个覆盖第一个.这些断言是如何实现的,并不是人们所期望的.但有一些解决方法.


您需要使用行为/返回值定义一个期望值!

看到: Mock in PHPUnit - multiple configuration of the same method with different arguments

在将示例调整为您的问题时,它可能如下所示:

$fooStub = $this->getMock('Foo');
$barStub = $this->getMock('Bar');

$factoryMock->expects($this->exactly(2))
       ->method('getInstanceFor')
       ->with($this->logicalOr(
                 $this->equalTo('foo'), 
                 $this->equalTo('bar')
        ))
       ->will($this->returnCallback(
            function($param) use ($fooStub, $barStub) {
                if($param == 'foo') return $fooStub;
                return $barStub;
            }
       ));
Run Code Online (Sandbox Code Playgroud)