instanceof扩展接口与PhpUnit

时间:2017-04-15 13:13:48

标签: php mocking phpunit

上下文:

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

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,因为它们应该在测试用例之外?

1 个答案:

答案 0 :(得分:2)

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

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)

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

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

class MyResource implements Resource
{
    ...
}

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

function testResourceImplementsTitleAndDescription()
{
    $resource = new MyResource();

    $this->assertInstanceOf(Title::class, $resource);
    $this->assertInstanceOf(Description::class, $resource);
}

正如你所看到的,我在这里不使用模拟,因为我想知道实际的对象而不是在它的位置创建的虚拟对象。