Reputation: 1247
The context:
I have this interface which extends three other interfaces:
interface BasicTagsInterface extends TitleTagInterface, DescriptionTagInterface, KeywordsTagInterface {}
TitleTagInterface
only have a getSeoTitle
method.DescriptionTagInterface
only have a getSeoDescription
method.KeywordsTagInterface
only have a getSeoKeywords
method.This is the method i'd like to test with 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());
}
}
And this is how I tried to test it so far:
$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);
I've also tried:
class A implements BasicTagsInterface {
public function getSeoDescription(){}
public function getSeoKeywords(){}
public function getSeoTitle(){}
}
// And in the test method:
$resource = $this->getMockBuilder(A::class) // etc.
The problem:
With this configuration, $resource instanseof BasicTagsInterface
returns true
and the other instanceof
tests return false
.
My question:
How can I mock an interface which extends other interfaces and get the instanceof
tests on the extended interfaces return true as they should outside a test case ?
Upvotes: 1
Views: 992
Reputation: 17166
I can't find any assertions in your test, but for me everything works fine:
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);
}
}
When I run the test I get the following output:
. 1 / 1 (100%)
Time: 66 ms, Memory: 8.00MB
OK (1 test, 2 assertions)
As you can see my test has 2 assertions. I don't test for the interface types, because I'm passing a dummy object anyway and I don't care about it's interfaces, but whether it produced the correct results when passed to my Object-class' fromResource method. Instead I assert for the expected outcome in that class, i.e. whether the setters were called.
If you want to test whether your Resource is of a specific type, I would instead create a class implementing the interface:
class MyResource implements Resource
{
...
}
and then write a separate test ensuring that when I construct a MyResource it is an instance of the desired interfaces:
function testResourceImplementsTitleAndDescription()
{
$resource = new MyResource();
$this->assertInstanceOf(Title::class, $resource);
$this->assertInstanceOf(Description::class, $resource);
}
As you can see I don't use a Mock here, because I want to know about the actual object not a dummy created in it's place.
Upvotes: 2