Let's say, for example, I want to test that a warning is shown on the Dashboard of a Car only when the Engine is broken.
The method implementing this functionality might look as follows:
class Dashboard {
function showWarningIfEngineIsBroken() {
if ($this->engine->isBroken()) {
$this->showWarning = true;
}
}
Let's say there are 10 scenarios in which $this->engine->isBroken()
would return true
. When testing the dashboard, I would want to make sure that the warning light lights up in any of those scenarios. So one thing I could do is create a test class like this:
class DashboardTest {
function showsWarningIfEngineIsBrokenBecauseOfReasonA() { ... }
function showsWarningIfEngineIsBrokenBecauseOfReasonB() { ... }
function showsWarningIfEngineIsBrokenBecauseOfReasonC() { ... }
// etc.
}
This is no problem, until I also want to test that the doors automatically unlock as soon as the engine breaks. Since that would create a lot of 'duplicate' tests:
class DoorTest {
function unlocksDoorIfEngineIsBrokenBecauseOfReasonA() { ... }
function unlocksDoorIfEngineIsBrokenBecauseOfReasonB() { ... }
function unlocksDoorIfEngineIsBrokenBecauseOfReasonC() { ... }
// etc.
}
One solution would be to test Engine::isBroken()
instead, and in the test for Dashboard and Door, only test what happens if isBroken
either returns true
or false
:
class EngineTest {
function isBrokenReturnsTrueIfEngineIsBrokenBecauseOfReasonA() { ... }
function isBrokenReturnsTrueIfEngineIsBrokenBecauseOfReasonB() { ... }
function isBrokenReturnsTrueIfEngineIsBrokenBecauseOfReasonC() { ... }
// etc.
}
class DashboardTest {
function showsWarningIfEngineIsBroken() {
$stubEngine = ...; // some stub of engine that returns true for `isBroken`
$dashboard = new Dashboard($stubEngine);
$dashboard->showWarningIfEngineIsBroken();
$this->assertTrue($dashboard->showWarning);
}
}
class DoorTest {
function unlocksIfEngineIsBroken() {
$stubEngine = ...; // some stub of engine that returns true for `isBroken`
$door = new Door($stubEngine);
$door->unlockIfEngineIsBroken();
$this->assertTrue($door->isUnlocked);
}
}
So finally the question: as I understand it, test doubles should be used to prevent unit test from getting slow, or making sure it stays isolated. In this case, let's assume $engine->isBroken()
is actually really fast and has no side effects. Is it a good practice to still stub it, since I actually only want to test what the Door
or Dashboard
would do when it either returns true
or false
?