TDD is software development technique to allow writing test before code then the tests will drive the implementation.
Apply red-green refator cycle. Imagine the developer wear 3 colors hat: red
, green
and blue
. In which:
Red
: make test failedGreen
: make test passedBlue
: refactor mode
Lifecycle in TDD:
- Create/Modify test and run (Red had)
- Create new test case/Modify existing test case to cover new function requirement/function improvement. Test case must be focused on requirement's given input, make a call to production function code where is resolved requirement, assert requirement's output. It is acceptable even test code is not compiled due to production code is not existed.
- Try to run and see test's fails.
- Write production code and rerun test (Green hat)
- Resolve compile issue (if any)
- Write a code to implement requiment description with given input and exptected result.
- Rerun test to make sure test is passed.
- Refactor code and rerun test (Blue hat)
- Follow
Clean Code
to refactor on both production code and test code. - Make sure test is still passed.
- Follow
Then repeat a lifecycle above to make enhancement in your application.
From Uncle Bob
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
- Keep an unit test is as smaller as possible, that helps small debug effort, and self-document for programmer readable (with comment code). In additional, Make test execution time is fast, that is core value.
- Don't make test case dependents each other: Not assume test B run after test A, and get given input from test A result. Instead of that, do make expilicit pre-context in tearup step, and clean up test result in teardown
- Avoid inheritance test code, make testcases as component (follow
composite pattern
) for reusable. - Make production code is as small modularity as possible, that helps decoupling code, easy to switch context when testing.
- Treat test code with same respect as production code, they must be readable and maintainable.
- Smoke context to test is welcome, but if smoke is more than 3 layers, please stand up, go to whiteboard and rethink about application interface design.
Eating your own dog food
is right sentence in this case. The developer is first guy to use his code, so he should take it carefully.- Cover acceptance criteria more easier, e.g: each testcase is each acceptance criteria.
- Increase code quality. And of course, product quality because TDD core values are small test and test speed, then more sooner detect issue, everybody can be aware (thanks to
Continous Integration (CI)
). - Improve code architecture design skill because test always call
public interface
method. Must care about application programming interface. - Up to date documentation: by testcase instead of comment a hunderad line in production code.
- Write both production code, and test code by one person is a risk because he can misunderstand the requirement then wrong test, wrong production code. To avoid that, more collaboration for
coding review
. More on that, design application programming interface is not easy job for junior, senior guy should be beside him. - Slow down Agile velocity, especially Start-up or Corp changes the requirements too frequently.
POC
and old code base are not a friends.