使用PHPUnit模拟对象,能否期望调用魔术方法__call()?

35

我在测试中使用了一个模拟对象。真实对象PageRepository使用__call()实现了一个魔术方法,因此如果您调用$pageRepository->findOneByXXXX($value_of_field_XXXX),它将搜索与该参数匹配的记录。

是否有一种方法可以模拟该方法?

真实方法调用看起来像这样:

$homepage = $pageRepository->findOneBySlug('homepage');

测试会像这样:

$mockPageRepository->expects($this->any())
    ->method('findOneBySlug')
    ->will($this->returnValue(new Page()));

但它不起作用-- PHPUnit没有检测到方法调用。唯一让它看到该方法的方法是在PageRepository中明确定义该方法。

2个回答

47

PHPUnit的getMock()需要传入第二个参数,该参数为一个数组,包含需要被mock的方法名称。 如果你在该数组中包含了某个方法名称,则mock对象将会包含该名称的方法,并且expects()等方法也会在其上正常工作。

即使对于“真实”类中未定义的方法,也适用于此。因此以下内容应该可以解决问题:

$mockPageRepository = $this->getMock('PageRepository', array('findOneBySlug'));

记住,你必须明确地包含任何其他需要被模拟的方法,因为只有数组中命名的方法才定义为模拟对象。


9
注意,如果您需要通过模拟构建器执行复杂操作(例如禁用原始构造函数),则必须显式调用 setMethods(),例如 $this->getMockBuilder('ClassName')->setMethods(['findById'])->getMock();,因为getMock()builder上不接受任何参数。 - J-P

5

我发现了更好的方法来做这件事。虽然还不完美,但你不必手动指定所有mocking类的方法,只需要使用魔法方法即可:

    $methods = get_class_methods(PageRepository::class);
    $methods[] = 'findOneBySlug';
    $pageRepositoryMock = $this->getMockBuilder(PageRepository::class)
        ->disableOriginalConstructor()
        ->disableOriginalClone()
        ->disableArgumentCloning()
        ->disallowMockingUnknownTypes()
        ->setMethods($methods)
        ->getMock();

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