Theory
Alternatively : you can watch the slides about the theory
TDD
TDD (Test Driven Development) is cool!
It makes sure we develop the right thing, step by step
Short version
TDD's steps
flowchart TB
start([Start])
==> red([Write only 1 failing test])
==> green([Make all tests pass])
==> refactor([Refactor])
==> finish([Finish])
refactor -.->|Incomplete ?| red
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
- Write only 1 failing test
- Make all tests pass
- Refactor
- Repeat while it's incomplete
- Until finish
Long version
Red step
flowchart TB
subgraph red[Red]
direction LR
fix_layout(( )) ==> write_a_test
write_a_test[Write only 1 failing test]
==> run_tests{{Run tests}}
run_tests
-.->|Pass Write another test| write_a_test
end
start([Start]) === fix_layout
run_tests ==>|Fail| green([Green])
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
classDef fix_layout stroke:white,fill:transparent;
class fix_layout fix_layout
- Write only 1 failing test
- Run tests
- If the tests pass then write another test
- Else if the tests fail then go to the green step
Green step
flowchart TB
subgraph green[Green]
direction LR
fix_layout(( )) ==> write_the_minimum_code
write_the_minimum_code[Write the minimum code to make all tests pass]
==> run_tests{{Run tests}}
run_tests
-.->|Fail Try something else| write_the_minimum_code
end
red([Red]) === fix_layout
run_tests ==>|Pass| refactor([Refactor])
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
classDef fix_layout stroke:white,fill:transparent;
class fix_layout fix_layout
- Write the minimum code to make all tests pass
- Run tests
- If the tests fail then try something else
- Else if the tests pass then go to the refactor step
Refactor step
flowchart TB
subgraph refactor[Refactor]
direction LR
rewrite_code[Rewrite code without changing the behavior]
==> run_tests{{Run tests}}
-.->|Pass Another things to refactor ?| rewrite_code
run_tests
-->|Fail Change something else| rewrite_code
end
green([Green]) ==> rewrite_code
run_tests ==>|Pass| finish([Finish])
run_tests -.->|Pass Another feature to add ?| red([Red])
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
classDef fix_layout stroke:white,fill:transparent;
class fix_layout fix_layout
- Rewrite code without changing the behavior
- Run tests
- If the tests fail then change something else and repeat the refactor step
- Else if the tests pass and
- there is another things to refactor then repeat the refactor step
- there is another feature to add then go to the red step
- there nothing else to do then it's finished
TCR
TCR (test && commit || revert) is cool!
It encourages doing baby steps, reducing the waste when we are wrong
TCR's single step
flowchart TB
gamble{{Gambling on test results}}
gamble ==>|Pass| commit(Commit)
gamble -->|Fail| revert(Revert)
- Gambling on test results
- If the tests pass then commit
- If the tests fail then revert
flowchart TB
start([Start])
==> green([Change some code])
==> finish([Finish])
green -.->|Incomplete ?| green
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
- Change some code
- Repeat while it's incomplete
- Until finish
But it doesn't allow us to see the tests failing
So:
-
Maybe we test nothing (assert forgotten)
def test_should_be_Buzz_given_5(): input = 5 actual = fizz_buzz(input) # Oops! Assert has been forgotten -
Maybe we are testing something that is not the thing we should be testing
it("should be Fizz given 3", () => { const input = 3; const actual = fizzBuzz(input); expect(input).toBe("Fizz"); // Oops! Asserts on the input value instead of the actual value });
Detailed view
flowchart TB
subgraph green[Green]
direction TB
start([Start])
==> change_code[Change some code]
==> run_tests{{"Run tests <code>test && commit || revert</code>"}}
==>|Pass| commit(Commit)
==> finish([Finish])
commit
-->|Another things to change ?| change_code
run_tests
-.->|Fail| revert(Revert)
-.->|Try something else| change_code
end
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
- Change some code
- Run tests
test && commit || revert - If the tests fail then
- Revert
- Try something else
- Repeat
- If the tests pass then
- Commit
- If there is another things to change then repeat
- If there nothing else to do then it's finished
TCRDD
TCRDD = TCR + TDD
TCRDD blends the constraints of the two methods to benefit from their advantages
TCRDD's phases
flowchart TB
red{{Write only 1 failing test}}
green{{Make all tests pass}}
refactor{{Refactor}}
red ~~~ red_commit
green ~~~ green_commit
refactor ~~~ refactor_commit
red ==>|Fail| red_commit(Commit) ==> green
green ==>|Pass| green_commit(Commit) ==> refactor
refactor ==>|Pass| refactor_commit(Commit)
red -->|Pass| red_revert(Revert) --> red
green -->|Fail| green_revert(Revert) --> green
refactor -->|Fail| refactor_revert(Revert) --> refactor
red_revert ~~~ green
green_revert ~~~ refactor
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
- Red phase
- Write only 1 failing test
- If the tests pass then
- Revert
- Retry the red phase
- Else if the tests fail then
- Commit
- Go to the green phase
- Green phase
- Make all tests pass
- If the tests fail then
- Revert
- Retry the green phase
- Else if the tests pass then
- Commit
- Go to the refactor phase
- Refactor phase
- Refactor
- If the tests fail then
- Revert
- Retry the refactor phase
- Else if the tests pass then
- Commit
Red phase
flowchart TB
subgraph red[Red]
direction LR
fix_layout(( )) ==> write_a_test
write_a_test[Write only 1 test]
==> gamble[/Gamble that the test fail <code>git gamble --red</code>/]
==> run_tests{{Actually run tests}}
==>|Fail| commit(Commit)
run_tests
-->|Pass| revert(Revert)
-->|Write another test| write_a_test
end
start([Start]) ==> fix_layout
commit ==> green([Green])
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
classDef fix_layout stroke:white,fill:transparent;
class fix_layout fix_layout
- Write only 1 test
- Gamble that the test fail
git gamble --red - Actually run tests
- If the tests pass then
- Revert
- Write another test
- Else if the tests fail then
- Commit
- Go to the green phase
Green phase
flowchart TB
subgraph green[Green]
direction LR
fix_layout(( )) ==> write_the_minimum_code
write_the_minimum_code[Write the minimum code]
==> gamble[/Gamble that the tests pass <code>git gamble --green</code>/]
==> run_tests{{Actually run tests}}
==>|Pass| commit(Commit)
run_tests
-->|Fail| revert(Revert)
-->|Try something else| write_the_minimum_code
end
red([Red]) ==> fix_layout
commit ==> refactor([Refactor])
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
classDef fix_layout stroke:white,fill:transparent;
class fix_layout fix_layout
- Write the minimum code
- Gamble that the tests pass
git gamble --green - Actually run tests
- If the tests fail then
- Revert
- Try something else
- Else if the tests pass then
- Commit
- Go to the refactor phase
Refactor phase
flowchart TB
subgraph refactor[Refactor]
direction LR
rewrite_code[Rewrite code without changing the behavior]
==> gamble[/Gamble that the tests pass <code>git gamble --refactor</code>/]
==> run_tests{{Actually run tests}}
==>|Pass| commit(Commit)
-.->|Another things to refactor ?| rewrite_code
run_tests
-->|Fail| revert(Revert)
-->|Change something else| rewrite_code
end
green([Green]) ==> rewrite_code
commit ==> finish([Finish])
commit -.->|Another feature to add ?| red([Red])
classDef red_phase font-weight:bold,color:black,fill:coral;
class red red_phase
classDef green_phase font-weight:bold,color:black,fill:#1cba1c;
class green green_phase
classDef refactor_phase font-weight:bold,color:black,fill:#668cff;
class refactor refactor_phase
classDef fix_layout stroke:white,fill:transparent;
class fix_layout fix_layout
- Rewrite code without changing the behavior
- Gamble that the tests pass
git gamble --refactor - Actually run tests
- If the tests fail then
- Revert
- Change something else
- Else if the tests pass then
- Commit
- If there is another things to refactor then go to the refactor phase
- Else if there is another feature to add then go to the red phase
- Else if there nothing else to do then it's finished
git-gamble is a tool that helps to use the TCRDD method