Skip to main content

TDD, Where Dit It All Go Wrong

Information in this page is based on my watching the youtube video called, TDD, Where Dit It All Go Wrong (Ian Cooper)

Test-Driven Development by Example by Kent Beck
I think the most important thing to take away from and understanding of that book is this—when you are practicing test driven development, do not test implementation details, test behaviors.

How to do TDD right

Wrong things to do

  • Many practice TDD by using adding a new method to a class as the trigger to write a test.
  • A test-case per class approach fails to capture the ethos for TDD.
  • Adding a new class is not the trigger for writing tests.
  • Do not write tests for implementation details these change!

Correct things to do

  • In TDD, creating a new test is that you have a requirement you want to implement.
  • the trigger is implementing a requirement.
  • Test the public API.
    • exports from a module.
    • the stable idea of your software; how you implement the requirement is unstable.
    • the contract your software has with the world.
  • Writing tests to cover the use cases or stories.
The object of TDD is to test behaviors in the system.
When we write a test, we imagine the perfect interface for our operation. We are telling ourselves a story about how the operation will look from the outside. Our story won't always come true, but it's better to start from the best possible application programming interface (API) and work backward.
The unit of isolation is the test, not the thing under test.
When we are focusing on testing individual methods of a class, We can't refactor easily because implementation details are exposed to tests.

Red-Green-Refactor

Refactor is the key in this red-green-refactor cycle.

Red

Write a little test that doesn't work, and perhaps doesn't even compile at first in the absence of correct implementation.

Green

Make the test work quickly, committing whatever sins necessary in the process.
What we're doing is making the test passed by the quickest (and perhaps dirtiest) process possible.
do not try to make it beautiful, do not put patterns in there yet, and just write line after line of code until it works.

Refactor

Eliminate all of the duplications created in merely getting the test to work in green phase (make good code).

The different phases have different purposes. They call for different styles of solution, different aesthetic viewpoints. The first three phases need to go by quickly, so we get to a known state with the new functionality. We can commit any number of sins to get there, because speed trumps design, just for that brief moment.

– Kent Beck, TDD by Example

Kent's point is essentially you can't do two things at once easily. You can't both understand the solution to the problem and engineer the code right.

Clean Code When?

The refactoring step is when we produce clean code.
  • It's when you remove duplication – Beck
  • It's when you sanitize the code smells – Fowler
  • It's when you apply patterns – Kerievsky
Because now you have a clear idea of what your solution is.
You do not write new unit tests when refactoring to clean code.
Refactoring is a process of safe moves that let you essentially change the design of the code. They do not change the behavior. Your behavior is covered by the original test.

Dependency is the key problem in software development at all scales

– Kent Beck, TDD BY Example

We need to eliminate dependency between our tests and our code.
Coupling is what kills all software so strive to decouple your tests from your implementation details. Focus on your public contract, your stable API and leave your implementation details free of tests and avoid heavy mocking.
This allows us to refactor implementations without changing the tests. Don't bake implementation details into tests!

Code Smells

Refactoring by Martin Fowler
Fowler's book provides both step-by-step, safe recipes for performing a refactoring as well as outlining the smells in code that trigger those refactorings.

Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure. It is a disciplined way to clean up code that minimizes the chances of introducing bugs. In essence when you refactor you are improving the design of the code after it has been written.

– Martin Fowler, Refactoring: Improving the Design of Existing Code

Kerievsky suggests we don't try to implement patterns in the system under test. We implement patterns as improvements to the code in the refactoring step.
The ideal step for applying patterns is the refactoring step because at this point I know what the solution is; I've written one (in the green phase). Now I can clean up by making a pattern.