2

Uncle Bob says in 'The Clean Coder', chapter 5 'Test Driven Development':

  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.

Where is the place for refactoring?

2
  • 9
    You refactor during step 9 3/4. Yet another reason to ignore the overrated Uncle Bob.
    – user949300
    Commented Nov 29, 2018 at 16:45
  • 5
    Did you read the next two paragraphs immediately following those rules? They describe the process of writing a failing test -> writing the code to pass test -> writing another failing test -> refactoring the code to pass all tests. You're constantly refactoring your code with this cycle.
    – Eric King
    Commented Nov 29, 2018 at 17:14

4 Answers 4

8

Robert C. Martin 'The Clean Coder', chapter 5 'Test Driven Development'

For what it is worth, you'll see those same rules on his website: Three Rules of TDD.

Where is the place for refactoring?

It happens on a different cadence. See The Cycles of TDD.

Second-by-Second nano-cycle: The Three Laws of TDD.

the goal is always to promote the line by line granularity that I experienced while working with Kent so long ago."

vs.

Minute-by-Minute: micro-cycle: Red-Green-Refactor

This cycle is typically executed once for every complete unit test, or once every dozen or so cycles of the three laws. The rules of this cycle are simple.

  • Create a unit tests that fails
  • Write production code that makes that test pass.
  • Clean up the mess you just made.

For comparison, consider how Kent Beck described TDD in Test Driven Development by Example.

  1. Quickly add a test
  2. Run all tests and see the new one fail
  3. Make a little change
  4. Run all tests and see them all succeed
  5. Refactor to remove duplication

In other words, what Beck describes is the Red-Green-Refactor workflow - write the whole test. That introduces a bunch of compiler errors (because the examples in those days were written in Java). Write a little bit of code to fix each error in turn. Now you have a failing test (RED). Write a little bit more code to make the test pass. Now you have an implementation that satisfies all of your tests (GREEN). Now clean up the mess (REFACTOR).

What the three laws of TDD are giving you is a nano workflow within RED-GREEN. You tick-tock between writing test code and writing production code, fixing each error as it comes, until the your implementation delivers the correct behavior.

From what I can tell, where the nano-cycle really stands out is in those places where you are extending the system under test; adding a new method, or a new class. Those are the cases where you see compiler errors that need to be fixed. In the case where you are creating a test to evaluate a changed behavior, there's little to distinguish the nano-cycle from test calibration.

5

I think you're missing an implied rule:

  • You are allowed to refactor production code that is under an existing test so long as you change absolutely no behavior in doing so.

Because that's what refactoring is. 2*4 is the same as 4*2. So the same test for 8 works on either.

If you're assuming you can't do that, because say you're wanting the test to go red remember, failing to compile is a red test. All you gotta do to go red is start typing.

This idea is important because if aliens suddenly abduct you, your wife calls saying the baby is coming now, or the building catches fire, it's nice if you're ready. You want your code to scream it's problems clearly when you get back to work. You don't want your code to ever be in a state where things look fine when they aren't.

You need to add a new test when you're getting ready to write a transformation that will change behavior. Don't confuse these two different kinds of changes.

It might also be worth noting that sometimes tests need refactoring themselves.

4
  • 1
    Not sure what "change absolutely no behavior" means. If the refactor changes from a Bubble-Sort to a Quicksort, that definitely changes some behavior, right?
    – user949300
    Commented Nov 29, 2018 at 22:54
  • 2
    @user949300 no. That impacts performance not behavior. Commented Nov 30, 2018 at 0:09
  • What if you change from quicksort to something else and you have test for 'partition' function? Does it change bechavior? Commented Dec 1, 2018 at 8:09
  • @MaciejWawrzyńczuk "something else" is a bit vague but as long as the behavior is the same that's fine. The partition function is a detail. When I'm testing if sorting works, however it's done, I don't know or care if it has a partition function. I just care that it sorts. Commented Dec 1, 2018 at 15:09
4

Those are the rules for TDD. Refactoring happens, when you have green tests, and are not adding any tests and/or production code. I.e after 3. and before the next round of 1.

In the classic cycle, those three rules would describe the "Red" (1, 2) and "Green" (1, 3) parts of the cycle.

2

While TDD and a lot of the "rules" around it are good, they are largely written by programmers that have trouble expressing themselves clearly in English. In this case Uncle Bob wasn't suggesting only do this, he was frustrated that people weren't even getting this little part of the process right.

My advice, follow the PDSA cycle, and go listen to a few presentations by Kevlin Henney on YouTube. Look specifically for one called "GUTS".

To summarise:

  • Plan out what you are going to achieve by writing a test. It must run, it must fail to pass, it must state clearly what will be achieved. Instead of writing a new test, a test may need to be changed. If you are deleting a test replace it with its negation (the system will no longer do X).
  • Develop the code to make the tests all pass.
  • Study what you have achieved. Look at it and see what could be improved, made simpler etc. No coding during this step. Just look, think, discuss, and learn.
  • Act on what you have learnt by making small improvements, adding tasks to your backlog, or improving how you will code in the future.

  • Repeat.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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