PHPUnit: 模拟不存在的类

28

在PHPUnit中是否可能创建一个不存在的类的模拟对象?
假设我有一个类,它创建另一个类的实例,例如:

class TaskRunner
{
    public function runTasks()
    {
        // Run through some loop to get tasks and store each in $taskName
        // Get task instance by given task name 
        $task = $this->getTaskInstance($taskName);
        if ($task instanceof AbstractTask) {
            $task->run();
        }
    }

    protected function getTaskInstance($taskName)
    {
        // Just an example
        return new $taskName();
    }
}

我想为runTasks方法运行单元测试,以检查创建的任务实例是否扩展了某个抽象类。
有没有不必在文件系统中创建样本类就能检查继承约束的可能性?
感谢您的所有帮助!


->getMock('AbstractTask')? - PeeHaa
@PeeHaa 好的,这对于确保条件运行正确是可以的。那么对于没有继承自“AbstracTask”的类呢?模拟“stdClass”吗? - Kuba T
你可以使用PHPUnit来模拟具体类,但更好的方式是使用接口进行类型提示。 - PeeHaa
@PeeHaa 当然,我明白你的意思。但是假设有两个类,一个扩展了AbstractClass,第二个没有扩展任何东西,纯基类。我不想在文件系统中创建它们,但我确实想模拟它们。第一个,好的,我可以模拟AbstractClass,这样就可以完成工作了。那第二个呢?它没有实现任何接口,也没有扩展任何其他类。而且我想编写一个测试来测试instanceof条件失败的情况。 - Kuba T
2个回答

49

是的,使用PHPUnit可以对不存在的类进行桩件(stub)/模拟(mock)。只需执行:

 $this->getMockBuilder('NameOfClass')->setMethods(array('foo'))->getMock();

创建一个不存在的类 NameOfClass 的对象,该类提供一个方法 foo(),可以像通常使用 API 配置一样进行配置。

PHPUnit 9 起,您应该将:

  • 'NameOfClass' 替换为 \stdClass::class
  • setMethods 替换为 addMethods
$this->getMockBuilder(\stdclass::class)->addMethods(array('foo'))->getMock();

4
不,PHPUnit不会抛出异常。为什么你不直接尝试一下呢?而不是问这个问题? - Sebastian Bergmann
1
我接了电话,那时候我无法亲自测试它 :) 对不起。 - Kuba T
2
@SebastianBergmann,由于在PHPUnit 9中setMethods()被弃用,所以这段代码在PHPUnit 10中无法工作。在现代PHPUnit模拟API中,是否有可能为不存在的类创建模拟对象? - rodion.arr
4
现在你可以使用 addMethods 方法。请查看 https://github.com/sebastianbergmann/phpunit/pull/3687。 - Jefferson Lima
1
@rodion.arr 我编辑了答案,添加了新的方法addMethods,并将虚拟名称替换为最小存在类:stdClass。 - Alexandre Tranchant
显示剩余2条评论

5

接受的答案非常完美,除了自PHPUnit 9 以来存在一个问题,如果您需要模拟一个特定实例的类。在这种情况下,不能使用\stdclass::class

并且使用

$this->getMockBuilder('UnexistentClass')->addMethods(['foo'])->getMock();

由于addMethod会将给定方法与给定类的方法进行比较,因此会导致Class UnexistentClass does not exist的结果。

如果有人遇到同样的问题,幸运的是,setMethods仍然有效,所以在PHPUnit 9中仍然可以使用此方法。

$this->getMockBuilder('UnexistentClass')->setMethods(['foo'])->getMock();

请注意,setMethods将在PHPUnit 10中被删除。

希望到那时会有一个解决此问题的方法。例如,检查是否将allowMockingUnknownTypes设置为true。如果实现了这个检查,那么以下代码也将起作用:

$this->getMockBuilder('UnexistentClass')->allowMockingUnknownTypes()
->addMethods(['foo'])->getMock();

$this->mock = $this->getMockBuilder('Domain\Customer\Models\Customer')->setMethods(['foo'])->getMock();我使用 PHPUnit 9.5.10,但是我遇到了这个错误: Error: Call to undefined method Domain\Customer\Models\Customer::getConnectionName()有什么想法吗? - Ferenc Bablena
这很可能是因为您将方法设置为“foo”,但在某个地方调用了getConnectionName。因此,您需要设置setMethods(['getConnectionName'])和最终使用的所有其他方法。 - Andrei Cristian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接