I'm trying to migrate a bunch of tests from SimpleTest to PHPUnit and I was wondering if there is an equivalent for SimpleTest's partial mocks.
Update: I can't seem to find anything in the docs which suggests that this feature is available, but it occurred to me that I could just use a subclass. Is this a good or bad idea?
class StuffDoer {
protected function doesLongRunningThing() {
sleep(10);
return "stuff";
}
public function doStuff() {
return $this->doesLongRunningThing();
}
}
class StuffDoerTest {
protected function doesLongRunningThing() {
return "test stuff";
}
}
class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
public function testStuffDoer() {
$sd = new StuffDoerTest();
$result = $sd->doStuff();
$this->assertEquals($result, "test stuff");
}
}
From reading the linked page, a SimpleTest partial mock seems to be a mock where only some of the methods are overridden. If this is correct, that functionality is handled by a normal PHPUnit mock.
Inside a PHPUnit_Framework_TestCase
, you create a mock with
$mock = $this->getMock('Class_To_Mock');
Which creates an mock instance where all methods do nothing and return null. If you want to only override some of the methods, the second parameter to getMock
is an array of methods to override.
$mock = $this->getMock('Class_To_Mock', array('insert', 'update'));
will create an mock instance of Class_To_Mock
with the insert
and update
functions removed, ready for their return values to be specified.
This information is in the phpunit docs.
Note, this answer shows more up to date code examples, for PHPUnit versions starting 5.4
PHPUnit_Framework_TestCase::getMock
is deprecated since phpunit 5.4. We can use setMethods
instead.
setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(null), then no methods will be replaced.
https://phpunit.de/manual/current/en/test-doubles.html
$observer = $this->getMockBuilder(Observer::class)
->setMethods(['update'])
->getMock();
Note that the above getMock
is PHPUnit_Framework_MockObject_MockBuilder::getMock
. (phpunit5.6)
I don't think PHPUnit supports partial mocks for the system under test. If you're trying to isolate methods then I'm sure your implementation works - I've done that too.
However, I try to avoid doing this, for a couple of reasons.
First, it couples your test very tightly to the internal implementation of the class. Do you really care whether a method called doesLongRunningThing
was called, or is it more important that the "LongRunningThing" got done?
Second, when I run into this it always makes me wonder whether I've got one class doing the job of two. An extract class refactoring might be in order. The testing becomes much easier if doesLongRunningThing()
becomes its own class, even with a single method.
I believe the solution is to inject the services your SUT depends on (http://en.wikipedia.org/wiki/Dependency_injection). This also makes the DoesLongRunningThing
implementation more testable.
Without jumping into interfaces, here's what I would do:
class DoesLongRunningThing {
public function execute() {
sleep(10);
return "stuff";
}
}
class StuffDoer {
protected $doesLongRunningThing;
public function setLongRunningThinger(DoesLongRunningThing $obj) {
$this->doesLongRunningThing = $obj;
}
public function doStuff() {
return $this->doesLongRunningThing->execute();
}
}
Now it's easy to mock:
class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
public function testStuffDoer() {
$dlrtMock = $this->getMock('DoesLongRunningThing');
$dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));
$sd = new StuffDoer();
$sd->setLongRunningThinger($dlrtMock);
$result = $sd->doStuff();
$this->assertEquals($result, "test stuff");
}
}
来源:https://stackoverflow.com/questions/1164192/equivalent-of-simpletest-partial-mocks-in-phpunit