【问题标题】:instanceof extended interfaces with PhpUnitinstanceof 与 PhpUnit 的扩展接口
【发布时间】:2017-09-11 14:17:31
【问题描述】:

上下文:

我有这个接口,它扩展了其他三个接口:

interface BasicTagsInterface extends TitleTagInterface, DescriptionTagInterface, KeywordsTagInterface {}
  • TitleTagInterface 只有一个 getSeoTitle 方法。
  • DescriptionTagInterface 只有一个 getSeoDescription 方法。
  • KeywordsTagInterface 只有一个 getSeoKeywords 方法。

这是我想用 PhpUnit 测试的方法:

public function fromResource($resource)
{
    if ($resource instanceof TitleTagInterface) {
        $this->setTitle($resource->getSeoTitle());
    }
    if ($resource instanceof DescriptionTagInterface) {
        $this->setDescription($resource->getSeoDescription());
    }
    if ($resource instanceof KeywordsTagInterface) {
        $this->setKeywords($resource->getSeoKeywords());
    }
}

这就是我目前尝试测试它的方式:

$resource = $this->getMockBuilder(BasicTagsInterface::class)->setMethods([
    'getSeoTitle',
    'getSeoDescription',
    'getSeoKeywords',
])->getMock();

$resource->method('getSeoTitle')->willReturn('Awesome site');
$resource->method('getSeoDescription')->willReturn('My awesome site is so cool!');
$resource->method('getSeoKeywords')->willReturn('awesome, cool');
// TagBuilder::fromResource() is the method above
$this->tagBuilder->fromResource($resource);

我也试过了:

class A implements BasicTagsInterface {
    public function getSeoDescription(){}
    public function getSeoKeywords(){}
    public function getSeoTitle(){}
}

// And in the test method:
$resource = $this->getMockBuilder(A::class) // etc.

问题: 使用此配置,$resource instanseof BasicTagsInterface 返回 true,其他 instanceof 测试返回 false

我的问题: 如何模拟扩展其他接口的接口并让扩展接口上的 instanceof 测试返回 true,因为它们应该在测试用例之外?

【问题讨论】:

    标签: php mocking phpunit


    【解决方案1】:

    我在您的测试中找不到任何断言,但对我来说一切正常:

    interface Title { function getTitle(); }
    interface Description { function getDescription(); }
    interface Resource extends Description, Title {}
    
    class MyObject {
        public $title;
        public $description;
    
        function fromResource(Resource $resource) {
            if ($resource instanceof Title) {
                $this->setTitle($resource->getTitle());
            }
            if ($resource instanceof Description) {
                $this->setDescription($resource->getDescription());
            }
        }
    
        function setTitle($title) { $this->title = $title; }
        function setDescription($description) { $this->description = $description; }
    }
    
    class Question43426479Test extends TestCase {
        function testFromResource() {
            $resourceMock = $this->getMockBuilder(Resource::class)->getMock();
            $resourceMock->method('getTitle')->willReturn('SomeTitle');
            $resourceMock->method('getDescription')->willReturn('SomeDescription');
    
            $object = new MyObject();
            $object->fromResource($resourceMock);
    
            $this->assertEquals('SomeTitle', $object->title);
            $this->assertEquals('SomeDescription', $object->description);
        }
    }
    

    当我运行测试时,我得到以下输出:

    .                                                                   1 / 1 (100%)
    
    Time: 66 ms, Memory: 8.00MB
    
    OK (1 test, 2 assertions)
    

    如您所见,我的测试有 2 个断言。我不测试接口类型,因为无论如何我都在传递一个虚拟对象并且我不关心它的接口,但它是否在传递给我的 Object-class' fromResource 方法时产生了正确的结果。相反,我断言该类中的预期结果,即是否调用了 setter。

    如果你想测试你的资源是否属于特定类型,我会创建一个实现接口的类:

    class MyResource implements Resource
    {
        ...
    }
    

    然后编写一个单独的测试,确保当我构造一个 MyResource 时,它​​是所需接口的一个实例:

    function testResourceImplementsTitleAndDescription()
    {
        $resource = new MyResource();
    
        $this->assertInstanceOf(Title::class, $resource);
        $this->assertInstanceOf(Description::class, $resource);
    }
    

    如您所见,我在这里没有使用 Mock,因为我想了解实际对象而不是在其位置创建的虚拟对象。

    【讨论】:

    • 好吧,真不好意思,这是一个命名空间问题...抱歉,感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 2017-02-28
    • 2020-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多