Disclaimer: Please note that this page is under intensive “draft” mode, so it is being actively updated.
If you want to add anything or make corrections, please see instructions contributing.
You can also join our Discord discussions.
TODO: Add the history of TDD, and the key people involved in development
TODO: Add Fowler’s article where he remarks that there were so many different definitions of unit test evne when XP began, and the classification sociable vs solitary unit test. Explain difference interaction-based vs state-based vs output-based verifification.
TODO: Definition of integration test - different variants. For example, with sociable unit test, tests spanning multiple classes are NOT integration tests per se, they are still unit tests…. But with solitary unit test, tests spanning multiple classes are integration tests… Do intgeration tests include or not include business logic? (the two interpretations)
TODO: Interpretation of acceptance tests from e2e perspective (that’s what many people see this as) - testing from the end user…. vs unit tests being acceptance tests (quoting Ian Cooper’s video TDD Revisited)
TODO: Problems of definition of e2e tests, spanning vs not spanning third-party systems
- 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.
- Don’t write a line of new code unless you first have a failing automated test
- Eliminate duplication
- Write a test for the next bit of functionality you want to add.
- Write the functional code until the test passes.
- Refactor both new and old code to make it well structured.
Write a failing test, well named, that test a behavior, not a method.
We should test behaviors not classes !
TODO: Here wording needs to be clarified. Our unit tests are calling classes/methods inside the test itself, but the emphasis is on behavior… Also the difference of interpretatation of behavior as outcomes (testing behavior through state), versus interpretation of behavior as interactions.
Make it pass with the minimum required code, this step should be fast, it must take less than 1 minute,
if not, it means your step is too big ! see baby steps for more
This is a step that is crucial and most of the time skipped by developers.
In this step, you’ll can remove duplicate, introduce some designs patterns, improve code readability.
Never forget to run tests after !!!
This will validate your modifications !
TODO: We also need to point out difference between localized vs more global refactoring… and avoiding premature design patterns.
Tests must be chosen well in order to accomplish the next minimum line of code you want to add.
Like a chess player, each step you take must be calculated and planned in order to complete the whole algorithm step by step.
This means, you don’t write a random unit test that won’t help you complete the algorithm, but a precise test, which targets a precise addition.
As many tests as necessary to validate business behavior.
This means that a method for operating a pacemaker will contain many more tests than a method for dropping candy from a candy machine.
The single assert rule means,
you should have a single logical assertion.
However, you can have multiple assert statements that can be composed.
– Uncle bob
The Single Assert Rule maintains that a single logical assertion should follow a single logical action.
“We should name our test functions for the action and assertion.” (Uncle bob)
public void addMoneyUpdateCustomerBalance(){ // -- }
public void computeXAndYReturnZZ(){ // -- }
public void startSystemShouldEnableFeaturesXY(){ // -- }
TODO: Need to list the different variants of naming, for example another convention is with should
Custom assertions are pretty easy to create, no need for external dependency:
Example:
class Test {
@Test
public void shouldInvertName() {
assertEquals("First", invert("First"));
assertEquals("Last, First", invert("First Last"));
assertEquals("Last, First", invert(" First Last "));
}
public String invert(String fullname) {
// --
}
}
This can be replaced by
class Test {
private void assertInverted(final String input, final String expected) {
assertEquals(expected, invert(input));
}
@Test
public void shouldInvertName() {
assertInverted("First", "First");
assertInverted("First Last", "Last, First");
assertInverted(" First Last ", "Last, First");
}
public String invert(String fullname) {
// --
}
}
TODO: Describing setup helpers and assertion helpers
TODO: Let’s review this section, what’s the source of this?
Test should be SOLID as well as production code, a great explanation can be viewed in this Uncle Bob episode:
Test Design - Clean Code: Advanced TDD, Episode 21
“Test code may be more important than production code since you can recreate the production code from the tests, but you can’t recreate the tests from the production code.” (Uncle bob, Article)
Great explanation in Uncle bob article
“Why do you get stuck? Because you were not adding sufficient generality to the production code. You were making the tests too specific, too quickly. The solution is to backtrack and then add specificity to the tests more slowly, while adding generality to the production code more quickly. This frequently forces you to choose a different set of tests to follow.” (Uncle Bob)