如何模拟对象工厂 [英] How to mock an Object Factory

查看:62
本文介绍了如何模拟对象工厂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用工厂(请参阅 http://www.php.net/manual/zh-CN/language.oop5.patterns.php (针对该模式))可以极大地提高我们代码的可测试性.一个简单的工厂可能看起来像这样:

I use Factories (see http://www.php.net/manual/en/language.oop5.patterns.php for the pattern) a lot to increase the testability of our code. A simple factory could look like this:

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

这是使用该工厂的示例类:

Here is a sample class using that factory:

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 */
        /* ... */
    }
}

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

Now for proper unit testing I need to mock the object that will return stubs for the classes, and that is where I got stuck. I thought it would be possible to do it like this:

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));
    }
}

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

But when I run the test, this is what I get:

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.

因此,很显然,无法以这种方式根据传递的方法参数让模拟对象返回不同的值.

So obviously it is not possible to let a mock object return different values depending on the passed method arguments this way.

这怎么办?

推荐答案

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

The problem is that the PHPUnit Mocking doesn't allow you to do this:

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

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

每个-> method(); 只能有一个期望.尚不了解-> with()的参数是否不同!

You can only have one expects per ->method();. It is not aware of the fact that the parameters to ->with() differ!

因此,您只需将第二个-> expects()覆盖即可.这就是这些断言的实现方式,而不是人们所期望的.但是有解决方法.

So you just overwrite the first ->expects() with the second one. It's how those assertions are implemented and it's not what one would expect. But there are workarounds.

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

You need to define one expects with both behaviors / return values!

请参阅: PHPUnit中的模拟-具有不同参数的同一方法的多个配置

在使示例适应您的问题时,它看起来可能像这样:

When adapting the example to your problem it could look like this:

$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;
            }
       ));

这篇关于如何模拟对象工厂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆