使用PHPUnit模拟对象测试使用get_class的代码

7
使用PHPUnit和模拟对象,我正在尝试测试一些使用get_class来确定对象是否被过滤器包含的代码。
这是要测试的类:
class BlockFilter implements FilterInterface
{
    private $classes;

    public function __construct(array $classes = array())
    {
        $this->classes = $classes;
    }

    public function isIncluded(NodeTraversableInterface $node)
    {
        if (Type::BLOCK != $node->getDocumentType()) {
            return false;
        }

        if (! empty($this->classes)) {
            /*** HERE IS THE PROBLEM: ***/
            return in_array(get_class($node), $this->classes);
        }

        return true;
    }
}

这是我的单元测试中的方法:

public function testIfContainerBlockIsIncluded()
{
    $containerBlock = $this->getMock('Pwn\ContentBundle\Document\ContainerBlock');
    $containerBlock->expects($this->any())->method('getDocumentType')->will($this->returnValue(Type::BLOCK));

    $filter = new BlockFilter(array('Pwn\ContentBundle\Document\ContainerBlock'));
    $this->assertTrue($filter->isIncluded($containerBlock));
}

模拟对象$containerBlock的行为类似于真实对象Pwn\ContentBundle\Document\ContainerBlock;即使使用instanceof也可以正常工作(我认为这是因为PHPUnit将其作为真实类的子类)。

正在测试的代码使用get_class获取类的字符串值,并将其与预期类名数组进行比较。不幸的是,对于模拟对象,get_class返回的是这样的结果:

Mock_ContainerBlock_ac231064

每次调用时,_ac231064后缀都会发生改变。
这导致我的测试失败了,那么我有哪些选择?
1. 重新设计代码以避免使用get_class?这意味着在编写可测试代码时不应使用get_class。 2. 使用ContainerBlock类的真实实例而不是模拟对象?这意味着我们实际上同时测试了两个类。 3. 您们将要建议的其他超级聪明的技巧? ;)
感谢您的任何帮助...
1个回答

3

在测试中传递Mock的类名:

new BlockFilter(array(get_class($this->containerBlock)));

1
好主意,我觉得这个想法其实一直在我脑海里,但是这样做似乎会让测试变得“人为”,我可能不太可能发现测试本身的错误。所以我现在在思考,与使用模拟对象相比,使用真实对象是否更好或更糟。 - fazy
@LarsJ 我认为这没有任何问题。你的 BlockFilter 在测试数组中的类名,因此传递 Mock 的类名是完全可以的。顺便说一句,我会模拟接口而不是 ContainerBlock,因为 isIncluded 上的 TypeHint 要求如此。 - Gordon
1
再次感谢您的想法和评论;我已经付诸实践,它运行良好。 - fazy

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