4

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?

6

Lets say you have a nice suite of tests. As you've been coding it's gotten a little big. Still nice and fast though. So you're running a bunch of tests each time. Now you've decided to change how the engine works. You do and some engine tests fail. that's fine. But now some Dashboard tests are failing. Which is odd because you didn't think you changed the Dashboard. Maybe you did it accidentally. And now you're looking for a bug in the wrong spot.

Speed isn't only reason to use doubles. Isolation is important. Not for the sake the test passing when it should, but to keep it from failing when it shouldn't.

  • Your answer is what I understand as the London school, and is completely correct given what OP showed. I just want to note, that there are other approved ways which has looser isolation. A unit of work can be defined as different things. It could be a complete functionality, rather than as little functionality as possible. OP's situation is: when engine is broken, show warning, but a test case could also be when reasonA, show warning. This no longer stubs the engine, and thereby tests the entire functionality / feature. – Chris Wohlert Jun 29 '17 at 10:13
  • @ChrisWohlert correct. I tend to think of those as end to end tests or use case tests. I see nothing wrong with doing both. The important thing is to give each test a good abstraction. A name that makes what it's testing no surprise. And to not obsess on structure. It's easy to focus on structure and invent tests that cover every possible combination and completely overwhelm a little code with tons of tests to the point that it's to painful to refactor the code because of all the tests that will invalidate. Failing tests should tell a story. – candied_orange Jun 29 '17 at 12:49
0

If you write unit tests you want to know where an assertion is not met. So your unit tests should test only little units of your system to be expressive. A unit test should not fail because of something that the test is not written for.

When you test two code artefacts and one dependent code artefact is used in both then two test may fail for a reason the tests were not designed for if you do not stub/mock the artefact they both depend on.

If you do not stub dependent artefacts a test will fail because the artefact under test may fail OR the dependent artefact. If you also test the dependent artefact then you have at least two tests that may fail. You loose expressiveness.

In huge systems I often see ten or more tests fail for the same reason. This has something to do that the tests are not isolated.

Another point is partial mocking. Most of the developers will have either no opinion on that or decline this kind of testing. The argument is: If you have to mock partially then you have violated the SRP (Single responsibility principle) before. While this is totally true it has absolutely NOTHING to do with the REAL WORLD.

In the real world we have to deal with imperfect code even if it is well written code that violate the SRP most of the time. This is not that bad at all as you may improve the code anytime. But as correct as the argument is: Nobody is able to write perfect code and certainly not if code for a problem is written the first time. So SRP is very important but in the real world you will find only few code fragments that will be fully SRP.

Another point is that you may have a complex object that has methods behaving in several ways dependent on the current state of the object. To test this you have to bring the object one step further in the state everytime you want to test the next step.

So you have following structure:

test 1

execute method 1 (under test)

assert result

test 2

execute method 1

execute method 2 (under test)

assert result

test 3

execute method 1

execute method 2

execute method 3 (under test)

assert result

Here again: All tests except test 1 may fail because of other reasons than the method under test may fail.

So for me partial mocking is a way to get isolated and expressive tests that will only fail because of reasons the are written for. For the last example you will come up with following structure:

test 1

execute method 1 (under test)

assert result

test 2

execute method 1 (mocked)

execute method 2 (under test)

assert result

test 3

execute method 2 (mocked)

execute method 3 (under test)

assert result

test 4

execute method 3 (mocked)

execute method 4 (under test)

assert result

You see that the only previous step is mocked. So your test will focus on the method under test and its result.

So yes: You should mock/stub dependent artefacts if your set of test cases should be isolated and expressive.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.