7

Uncle Bob's three rules of test-driven development state the following:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

Some people argue that the second rule implies that you must only ever add one failing test (an example of an isolated behavior). But others argue that it could also mean you may add multiple failing tests in a test suite, as long as you stop working on each separate test as soon as it fails.

I would like to know what advantages you get from limiting yourself to writing only one single new failing test and then stop writing test code until you made it pass.

  • see also: Can I start with a passing unit test? – gnat Apr 11 '16 at 19:22
  • I've read it, but I am explicitly not talking about writing tests that pass at first. I am talking about writing as much code on each test to make it fail. – aef Apr 11 '16 at 19:25
  • That sounds contradictory. Imagine you added a new test. Either it fails (which means you stop working on it, as you wrote above), or it does not fail (which means you started with a passing test, which is covered by that other question). Please clarify. – Doc Brown Apr 11 '16 at 19:33
  • Then it doesn't seem to be my contradiction. You are not allowed to write any more of a unit test than is sufficient to fail might imply that a test did not fail at some point. After all, one character might trigger a syntax error that causes compilation to fail. I am talking about writing a test, execute it to verify that it fails, then repeat this process for a few more tests. And only then, you start implementing. The question would be if the described practise is somehow inferior to writing a failing test and then instantly make it pass and only then start writing another failing test. – aef Apr 11 '16 at 19:40
10

When you write two tests at once, and start implementing code to make the first test pass, you cannot run the whole test suite and get a "green" result, because the second test will still fail. You can look at the detailed output of your unit test framework and check visually that exactly the test failed which you expected, of course, but that has a potential to become cumbersome and error-prone, especially when your number of unit tests starts to grow. And if you have, for example, something like a CI system installed with a rule that no failing tests must be checked in, because failing tests count as broken builds, then this means you cannot check in your code until you implemented enough code to make all tests pass.

Note that this is an idealized model, it is made to train yourself to work in the smallest possible steps, giving you a compileable, fully tested program every few minutes. The idea is that the smaller your steps are from one "fully working state" to another, the better you can control the process.

Of course, when doing "real-world programming", if you prefer, you can pick any order of implementation of tests and production code you want, when you think bigger steps will suit your needs better. Nevertheless, when you work this way, it might be a good idea to explicitly disable or comment-out any test you have written beforehand until you start to work on the related part of production code.

  • "No failing tests" would be a useful rule for master, not branches. – DeadMG Apr 11 '16 at 21:08
  • @DeadMG: this is just an example to illustrate my answer. See it this way: when working with feature branches, the smaller one makes the TDD cycle, the earlier there is a chance to reintegrate into master. – Doc Brown Apr 12 '16 at 6:28
7

The reason for staying with one failing unit test at a time is focus. When you have that single failing unit test, you know exactly what to work on; you know what part of the code to think about; you know what problem you are solving immediately. If you allow yourself to write more tests as you work on solving one problem, pretty soon you are solving two - or five or ten - problems, and with that lack of focus, you're not really solving anything.

By all means, as you discover additional need for tests, even while your test is still failing: make a note of it. Review your notes after getting the test to pass and pick one to work on while it's still fresh in your mind. But if you write that additional failing test prematurely, you step further away from all green, and you don't want to be far away from that state.

Also, if you find yourself feeling the need for additional tests, let that feeling guide you to write smaller tests to begin with.

This is the basic TDD dogma; in practice many of us occasionally violate it and write that second failing test. But when we're at our best, it's just one failing test at a time, and focus is the reason.

  • 1
    OTOH, constantly switching between the Test and the Code causes issues in focus too. For example, yoy need to bring up the spec each time and find requirement 234.b. Next test, you need to remember where you were and look for 234.c. Some programmers work better in tiny steps, one test at a time. Others work better looking at the larger picture. A good development process should take that into account instead of standing on Uncle Bob's dogma. – user949300 Apr 11 '16 at 21:10
0

Doing your own projects, you're welcome to write as many tests as you like, but my experience in real business is, that testing or refactoring are non-functional requirements only in your opinion and your customer has to pay for the time you need to write your tests. Although a test is inherent part of every issue in your project it should not be overdone.

It's the same discussion about how many unit-tests ensure my software's quality compared to one good integration test? Think of Uncle-Bob's words testing a model class:

  • (test) "it should return a message": model = new NewsModel()
  • (test fails) NewsModel is undefined
  • (impl fixes test) class NewsModel {}
  • (test) model.getMessage()
  • (test failes) NewsModel::getMessage is undefined
  • (impl fixes test) public getMessage() {}
  • ...

I disagree with that strict red/green/refactor since one good integration test should reveal misbehavior and I don't think it's Uncle Bob's intention you to write model tests in that way. The message is only that you should not get lost in hours of testing, but write good tests and at least don't double-test behavior.

If you're able to form every little piece of requirement of your customer into a test, you're going to ship outstanding software, if your implementation makes the tests pass. Same thing with bugs: write one test which reproduces it (red) then fix it (green) and at least refactor it.

And now back to your question again: Having only one test fail, isolates that misbehavior while ensuring that all other components behaving correctly. If you're trying to fix that and then two tests fail you might went in a wrong direction.

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.