【问题标题】:PHPUnit turning an instance of a class into a mock after instantiationPHPUnit在实例化后将一个类的实例变成一个模拟
【发布时间】:2013-01-14 22:19:08
【问题描述】:

有没有办法用 PHPUnit 创建一个模拟类,然后我可以使用它的类名创建一个新实例?

我有一个定义两种方法的接口。比如:

interface FooInterface {
    function getA();
    function getB();
}

然后我有另一个类,它接受一个类名,创建该类的一个实例,检查它是否是它所期望的实例 (FooInterface),然后调用该类的两个方法来获取一些信息。

class FooInfo {
    protected $a;
    protected $b;

    public function __construct($fooClass) {
        $foo = new $fooClass;

        if (!($foo instanceof FooInterface)) {
            throw new \Exception();
        }

        $this->a = $foo->getA();
        $this->b = $foo->getB();
    }
}

我知道如何模拟一个对象就好了。问题是,由于这个类接受一个类名,而不是一个对象(它是根据需要创建给定类的实例的 Manager 的一部分),我不能使用普通的模拟对象。

我尝试制作一个模拟对象,然后使用该类名。它似乎可以很好地创建对象,甚至似乎具有我模拟出来的功能。但是,它似乎没有遵循我稍后设置的 will($this->returnValue('myValue')) 部分。

public function testConstruct()
{
    $foo = $this->getMockForAbstractClass('Foo', array('getA', 'getB'));
    $foo->expects($this->any())->method->('getA')->will($this->returnValue('a'));
    $foo->expects($this->any())->method->('getB')->will($this->returnValue('b'));

    $copyClass = get_class($foo);
    $copy = new $copyClass();

    // Passes
    $this->assertTrue(method_exists($copy, 'getA');

    // Fails, $copy->getA() returns null.
    $this->assertEquals($copy->getA(), $foo->getA());
}

所以,它确实有被模拟的函数,但它们都返回 null。

有什么想法吗?

【问题讨论】:

    标签: php unit-testing mocking phpunit


    【解决方案1】:

    在类的构造函数中使用 new 关键字是一个相当糟糕的习惯,正是由于您现在遇到的原因,即使考虑到您的灵活用例。

    您的测试将不起作用,因为您创建的模拟将永远不会被使用,因为您的类将始终创建注入类名的真实实例。

    也就是说,你想做的事情可以用padraic的优秀模拟库mockery来完成! 您需要的是一个“实例模拟”:

    $mock = \Mockery::mock('overload:MyNamespace\MyClass');
    

    您可以在该模拟上定义您的期望,该模拟将在实例化后立即传输到真实对象。

    将 mockery 与 phpUnit 集成起来很简单,并且在项目的自述文件中有很好的解释。

    顺便说一句。每个单元测试最好只做出一个断言!

    【讨论】:

    • 谢谢。实际上,我确实已经删除了类名,因为我意识到这也有点奇怪。不过,如果我需要的话,我会记住 Mockery。
    • 如果有人需要进一步解释,您可以查看this
    • 如果这是用嘲弄来模拟现有实例的唯一方法,那么你应该重新发明轮子。
    【解决方案2】:

    getAgetB 方法返回 null,因为您没有指定它们应该返回什么。您确实通过调用一些方法为抽象 $foo 指定了这一点。没有办法。

    由于很难(如果不是不可能)测试该功能,我会重写代码本身。如果您的构造函数需要类实例而不是类名,则测试会很容易。如果您还必须接受字符串,您可以编写几行代码来检查字符串输入并在提供字符串时创建一个类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-13
      • 2015-06-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多