I have been reading the PHPUnit docs online and have been using the mock object matchers but I am not sure when you should or shouldn’t use them. For the example below, let’s say I have a class Foo I am writing a test for:
Foo class
<?php class Foo { public function isUserNamedTom(User $user):bool { return strtoupper($user->getName()) === 'TOM'; } }
My Test
<?php use PHPUnitFrameworkTestCase; class FooTest extends TestCase { public function testIsUserNamedTom():void { $userMock = $this->getMockBuilder(User:class) ->disableOriginalConstructor() ->getMock(); $userMock->expects($this->once()) ->method('getName') ->willReturn('tom'); $fooService = new Foo(); $response = $fooService->isUserNamedTom($userMock); $this->assertTrue($response); } }
My question is should I be using $this->once() or not in this example and if I am not then what are the reasons.
Advertisement
Answer
Your tests should test the output of the method under test with as little knowledge as possible about the implementation of the method under test. You want the test to fail when the method no longer does what it’s supposed to do, but you don’t want it to fail when the method does the right thing in a different way.
In this case it seems irrelevant for the test of isUserNamedTom(User $user)
whether user->getName()
is called once, twice or never. The only important thing is that isUserNamedTom(User $user)
returns true
if and only if $user
is named “tom”, “Tom”, “tOm”, etc.
Therefore: No, I wouldn’t check for $this->once()
here.
I would even try to get by without mocking and pass an instantiated user object instead. But whether this is possible depends on your User
class.
public function testIsUserNamedTom():void { $user = (new User())->setName('tom'); $this->assertTrue($fooService->isUserNamedTom($user)); }
Basically, when writing tests, it’s always good to ask yourself under what circumstances you want them to fail. In your example, my guess is that your test shouldn’t fail if someone refactored isUserNamedTom(User $user)
so that the method no longer calls $user->getName()
(but maybe uses a public property $user->name
).