When learning the game of Chess, people usually start by learning the basic moves individual pieces can perform. Later, to master the game, one must develop the skill to combine those small, basic moves into larger strategies. If we think about Test Driven Development in similar terms, what would those “basic TDD moves” be?
6. The Three Laws
of TDD
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.
http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
Robert “Uncle Bob” Martin
8. String result = "";
if((number % 3) == 0) result = "fizz";
if((number % 5) == 0) result += "buzz";
if(result.isEmpty())
result = number.toString();
return result;
9. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
fail(“niy”);
}
}
NIY Test
Step on solid ground from the very beginning
10. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(1));
}
}
First Things First
Start by writing an assertion
11. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(1));
}
}
public class FizzBuzz
{
public static int of(int number)
{
return 1;
}
}
12. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(1));
}
@Test public void test_fizzbuzz_2()
{
assertThat(FizzBuzz.of(2), is(2));
}
}
public class FizzBuzz
{
public static int of(int number)
{
return number;
}
}
Uncle Bob’s 3 Laws
Start with Failing Test
Sufficient enough to fail
Uncle Bob’s 3 Laws
Sufficient enough code to pass the test
Triangulation
Approximate with examples
13. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(1));
}
@Test public void test_fizzbuzz_2()
{
assertThat(FizzBuzz.of(2), is(2));
}
@Test public void test_fizzbuzz_3()
{
assertThat(FizzBuzz.of(3), is(“fizz”));
}
}
public class FizzBuzz
{
public static int of(int number)
{
return number;
}
}
Type mismatch!
Design is wrong!
We need
to go
here…
TDD
Write a
failing
test
Make it
pass
Improve
the
Design
But we’re
here!
14. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(1));
}
@Test public void test_fizzbuzz_2()
{
assertThat(FizzBuzz.of(2), is(2));
}
//@Test public void test_fizzbuzz_3()
//{
// assertThat(FizzBuzz.of(3), is(“fizz”));
//}
}
public class FizzBuzz
{
public static int of(int number)
{
return number;
}
}
Better Green than Sorry
Refactor only when tests are passing;
15. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(1));
}
@Test public void test_fizzbuzz_2()
{
assertThat(FizzBuzz.of(2), is(2));
}
//@Test public void test_fizzbuzz_3()
//{
// assertThat(FizzBuzz.of(3), is(“fizz”));
//}
}
public class FizzBuzz
{
public static of( number)
{
return number;
}
}
Better Green than Sorry
Keep all test passing while refactoring
16. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(1));
}
@Test public void test_fizzbuzz_2()
{
assertThat(FizzBuzz.of(2), is(2));
}
//@Test public void test_fizzbuzz_3()
//{
// assertThat(FizzBuzz.of(3), is(“fizz”));
//}
}
public class FizzBuzz
{
public static of(Integer number)
{
return ();
}
}
Predictable Failure
Refactoring against the Red Bar
17. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is( ));
}
@Test public void test_fizzbuzz_2()
{
assertThat(FizzBuzz.of(2), is( ));
}
//@Test public void test_fizzbuzz_3()
//{
// assertThat(FizzBuzz.of(3), is(“fizz”));
//}
}
public class FizzBuzz
{
public static String of(Integer number)
{
return number.toString();
}
}
Better Green than Sorry
Run all the tests, all the time
The House is in Order
All tests must pass before writing
the next test
18. public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(“1”));
}
@Test public void test_fizzbuzz_2()
{
assertThat(FizzBuzz.of(2), is(“2”));
}
@Test public void test_fizzbuzz_3()
{
assertThat(FizzBuzz.of(3), is(“fizz”));
}
@Test public void test_fizzbuzz_6()
{
assertThat(FizzBuzz.of(6), is(“fizz”));
}
@Test public void test_fizzbuzz_5()
{
assertThat(FizzBuzz.of(5), is(“buzz”));
}
@Test public void test_fizzbuzz_10()
{
assertThat(FizzBuzz.of(10), is(“buzz”));
}
@Test public void test_fizzbuzz_15()
{
assertThat(FizzBuzz.of(15), is(“fizzbuzz”));
}
}
public class FizzBuzz
{
public static String of(Integer number)
{
String result = "";
if((number % 3) == 0) result = "fizz";
if((number % 5) == 0) result += "buzz";
if(result.isEmpty())
result = number.toString();
return result;
}
}
public class FizzBuzzTests {
@Test public void test_fizzbuzz()
{
assertThat(FizzBuzz.of(1), is(“1”));
assertThat(FizzBuzz.of(2), is(“2”));
assertThat(FizzBuzz.of(3), is(“fizz”));
assertThat(FizzBuzz.of(6), is(“fizz”));
assertThat(FizzBuzz.of(5), is(“buzz”));
assertThat(FizzBuzz.of(10), is(“buzz”));
assertThat(FizzBuzz.of(15), is(“fizzbuzz”));
}
}
19. public class FizzBuzzTests {
@Test
public void returns_fizz_for_multiples_of_3() {
assertThat(FizzBuzz.of(3), is("fizz"));
assertThat(FizzBuzz.of(6), is("fizz"));
}
@Test
public void returns_buzz_for_multiples_of_5() {
assertThat(FizzBuzz.of(5), is("buzz"));
assertThat(FizzBuzz.of(10), is("buzz"));
}
@Test
public void returns_fizzbuzz_for_multiples_of_both_3_and_5()
{
assertThat(FizzBuzz.of(15), is("fizzbuzz"));
assertThat(FizzBuzz.of(30), is("fizzbuzz"));
}
@Test
public void returns_number_as_is_for_other_numbers()
{
assertThat(FizzBuzz.of(1), is("1"));
assertThat(FizzBuzz.of(2), is(“2"));
int bigNumber= 2 * 4 * 7 * 11 * 23;
assertThat(FizzBuzz.of(bigNumber),
is(Integer.toString(bigNumber)));
}
}
No Guessing Games
Test Names Reflect Intent
Assertive Minimalist
Test only “one thing”
20. public class FizzBuzzTests {
public void returns_fizz_for_multiples_of_3() {…}
public void returns_buzz_for_multiples_of_5() {…}
public void returns_fizzbuzz_for_multiples_of_both_3_and_5() {…}
public void returns_number_as_is_for_other_numbers() {…}
}
Kevlin Henney
“Programming with GUTS”
“Test Smells & Fragrances”
21. public class FizzBuzzTests {
@Test
public void returns_fizz_for_multiples_of_3() {
assertThat(FizzBuzz.of(3), is("fizz"));
assertThat(FizzBuzz.of(6), is("fizz"));
}
@Test
public void returns_buzz_for_multiples_of_5() {
assertThat(FizzBuzz.of(5), is("buzz"));
assertThat(FizzBuzz.of(10), is("buzz"));
}
@Test
public void returns_fizzbuzz_for_multiples_of_both_3_and_5()
{
assertThat(FizzBuzz.of(15), is("fizzbuzz"));
assertThat(FizzBuzz.of(30), is("fizzbuzz"));
}
@Test
public void returns_number_as_is_for_other_numbers()
{
assertThat(FizzBuzz.of(1), is("1"));
assertThat(FizzBuzz.of(2), is(“2"));
int bigNumber= 2 * 4 * 7 * 11 * 23;
assertThat(FizzBuzz.of(bigNumber),
is(Integer.toString(bigNumber)));
}
}
22. public class FizzBuzzTests {
@Test
public void returns_fizz_for_multiples_of_3() {
assertThatFizzbuzzForNumbersIs(“fizz”, 3, 6);
}
@Test
public void returns_buzz_for_multiples_of_5() {
assertThatFizzbuzzForNumbersIs(“buzz”, 5, 10);
}
@Test
public void returns_fizzbuzz_for_multiples_of_both_3_and_5()
{
assertThatFizzbuzzForNumbersIs(“fizzbuzz”, 15, 35);
}
@Test
public void returns_number_as_is_for_other_numbers()
{
int bigNumber= 2 * 4 * 7 * 11 * 23;
assertThatNumbersRemainAsIs(1, 2, bigNumber);
}
}
/* Custom Assertions */
private void assertThatFizzbuzForNumbersIs(
String expectedResult, int... numbers)
{
for(int n: numbers)
{
assertThat(
FizzBuzz.of(n), is(expectedResult));
}
}
private void assertThatNumbersRemainAsIs(
int... numbers)
{
for(Integer n: numbers)
{
assertThat(
FizzBuzz.of(n), is(n.toString()));
}
}
Duplication Fighter
No duplication in production or test code
24. 2 Practice Sessions on the
“String Calculator Kata”
We need to choose a common Programming Language
Find a partner (we’ll switch for Session 2)
Log in to Cyber-Dojo: http://cyber-‐dojo.org
25.
26. Practice Session 1:
Basic TDD “Moves”
Uncle Bob’s Three Laws
Start with a failing test
Write enough of a test to fail
Write only the code that is sufficient to make the failing test pass
NIY Test Start on solid ground from the very beginning
First Things First Start the test by writing an assertion
Triangulation Approximate with examples
Keep the House in Order All tests must pass before moving on to the next test
Duplication Fighter
Remove duplication after a test passes
Keep the tests clean and free of duplication
Better Green than Sorry
Refactor only when all tests are passing
Re-run all tests after every refactoring
No Guessing Games Test names reflect intent
Assertive Minimalist Minimize the number of assertions (test "one thing”)
Predictable Failure Refactoring against the Red Bar
27. Debrief: What was it like?
What are most important insights you gained from the
session?
What was helpful / not-helpful?
What will you do differently in the next session?
28. Practice Session 2:
Extreme Baby Steps
1. Setup source control repository.
2. Setup a timer for 2 minutes interval when you start.
3. Write exactly one test
1. If the timer rings and the test is red then revert and start over.
2. If the test is green before timer rings then commit.
4. Restart timer (no discussions in between timers)
5. Refactor
1. If the timer rings and the refactoring is not complete then revert and start over.
2. If the refactoring is complete before the timer rings then commit.
6. Restart the timer (no discussions in between timers)
7. Go to 3.
http://blog.adrianbolboaca.ro/2013/03/taking-‐baby-‐steps
29. Debrief: What was it like?
What are most important insights you gained from this
session?
What was the overall feeling?
How does the result compares to what you initially
thought the outcome might be?
How/Where could this approach be useful?
What will you do differently tomorrow, back in the
office?
30. Learning more
Kevlin Henney Presentations
Programming with GUTS
https://www.infoq.com/presentations/testing-communication
Test Smells & Fragrances
https://www.youtube.com/watch?v=wCx_6kOo99M