TDD for the Win

76
TDD FOR THE WIN Basics to Test Driven Development May 2014 Minh Ngoc Dang

Transcript of TDD for the Win

Page 1: TDD for the Win

TDD FOR THE WINBasics to Test Driven Development

May 2014 Minh Ngoc Dang

Page 2: TDD for the Win

Senior Developer at ThoughtWorks

ThoughtWorks University Trainer

Minh Ngoc Dang

Page 3: TDD for the Win

TESTING

Page 4: TDD for the Win

WHY TESTING?

Lots of reasons! Here are a select few

Verify functionality of application

Catch bugs and prevent them from coming back

Serve as documentation for maintainability purposes

Page 5: TDD for the Win

BUT WHAT ABOUT…?

? How do we make sure we have full test coverage?

? How can we be confident all cases are covered?

? How can we prevent bugs from happening?

Page 6: TDD for the Win

TDD!

Page 7: TDD for the Win

TEST DRIVEN DEVELOPMENT!

Page 8: TDD for the Win

BUT WAIT…WHAT IS IT?

Page 9: TDD for the Win

BUT WAIT…WHAT IS IT?

Page 10: TDD for the Win

TEST DRIVING MEANS

Writing tests before writing code

Write the minimum amount of code necessary to make tests pass

Rinse and repeat

Page 11: TDD for the Win

TESTS FIRST?! BUT WHY?

Page 12: TDD for the Win

WHY TDD?

Back to previously posed questions

Test Coverage?

Cases Covered? Bug Prevention?

Page 13: TDD for the Win

TEST COVERAGE

Tests written before code

Page 14: TDD for the Win

TEST COVERAGE

Tests written before code

Ensure all code covered by tests

Page 15: TDD for the Win

TEST COVERAGE

Tests written before code

Ensure all code covered by tests

High test coverage

Page 16: TDD for the Win

WHY TDD?

Back to previously posed questions

Test Coverage?

Cases Covered? Bug Prevention?

Page 17: TDD for the Win

TEST CASES

Test cases reflect exactly what code does

Page 18: TDD for the Win

TEST CASES

Test cases reflect exactly what code does

Visible list of functionality

Page 19: TDD for the Win

TEST CASES

Test cases reflect exactly what code does

Visible list of functionality

Easy to see which cases are missing

Page 20: TDD for the Win

WHY TDD?

Back to previously posed questions

Test Coverage?

Cases Covered? Bug Prevention?

Page 21: TDD for the Win

BUG PREVENTION

Mental shift in thinking up test case before code

Page 22: TDD for the Win

BUG PREVENTION

Mental shift in thinking up test case before code

Encourage edge case testing

Page 23: TDD for the Win

BUG PREVENTION

Mental shift in thinking up test case before code

Encourage edge case testing

Help prevent bugs in edge cases

Page 24: TDD for the Win

WHY TDD?

Back to previously posed questions

Test Coverage?

Cases Covered? Bug Prevention?

Page 25: TDD for the Win

AND THAT’S NOT ALL!

Page 26: TDD for the Win

TIME SAVER

Ease in debugging

Confidence in refactoring

Page 27: TDD for the Win

DRIVING DESIGN

Help define usage – How should the code be used?

Only write necessary code – YAGNI!

Page 28: TDD for the Win

SO HOW CAN I TDD?

Page 29: TDD for the Win

RED – GREEN - REFACTOR

New Test New Test New Test

Red

GreenRefactor

Red

GreenRefactor

Red

GreenRefactor

Page 30: TDD for the Win

EXAMPLE

You are given strings of different lengths. If the number of vowels are more than 30% of the string length then insert ‘mommy’ for each continuous set of vowels.

his → hmommys hear → hmommyr ✗ hear → hmommymommyr

Page 31: TDD for the Win

TEST CASES

“” → “” // empty string“str” → “str” // no vowels“a” → “mommy” // single vowel“blah” → “blah” // < 30% length“bla” → “blmommy” // > 30% length“blaa” → “blmommy” // continuous vowels“blaaha” → “blmommyhmommy”

// multi sets of vowels“blA” → “blmommy” // capital lettersNull → raise exception // null checks

Page 32: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() { String word = ""; Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word); assertEquals("", mommifiedWord); } }

Page 33: TDD for the Win

SOURCEpublic class Mommifier { public String mommify(String word) { return null; } }

Page 34: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() { String word = ""; Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word); assertEquals("", mommifiedWord); } }

Page 35: TDD for the Win

SOURCEpublic class Mommifier { public String mommify(String word) { return ""; } }

Page 36: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() { String word = ""; Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word); assertEquals("", mommifiedWord); } }

Page 37: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() { String word = "str";

Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word); assertEquals("str", mommifiedWord); }}

Page 38: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() { String word = "str";

Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word); assertEquals("str", mommifiedWord); }}

Page 39: TDD for the Win

SOURCEpublic class Mommifier { public String mommify(String word) { return word; } }

Page 40: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() { String word = "str";

Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word); assertEquals("str", mommifiedWord); }}

Page 41: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() {...}

@Test public void shouldNotMommifyWordsWithNoVowels() {...}

@Test public void shouldMommifyVowel() { String word = "a";

Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word);

assertEquals("mommy", mommifiedWord); }}

Page 42: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() {...}

@Test public void shouldNotMommifyWordsWithNoVowels() {...}

@Test public void shouldMommifyVowel() { String word = "a";

Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word);

assertEquals("mommy", mommifiedWord); }}

Page 43: TDD for the Win

SOURCEpublic class Mommifier {

public String mommify(String word) { for (char character : word.toCharArray()) { if (character == 'a' || character == 'e' || character == 'i' || character == 'o' || character == 'u'){ return "mommy"; } } return word; }

}

Page 44: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() {...}

@Test public void shouldNotMommifyWordsWithNoVowels() {...}

@Test public void shouldMommifyVowel() { String word = "a";

Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word);

assertEquals("mommy", mommifiedWord); }}

Page 45: TDD for the Win

SOURCEpublic class Mommifier {

public String mommify(String word) { for (char character : word.toCharArray()) { if (character == 'a' || character == 'e' || character == 'i' || character == 'o' || character == 'u'){ return "mommy"; } } return word; }

}

Page 46: TDD for the Win

SOURCEpublic class Mommifier {

private static final String VOWELS = "aeiou"; private static final String REPLACEMENT_WORD = "mommy";

public String mommify(String word) { for (Character character : word.toCharArray()) { if (isAVowel(character)) { return REPLACEMENT_WORD; } } return word; }

private boolean isAVowel(Character character) { return VOWELS.contains(character.toString()); }

}

Page 47: TDD for the Win

TESTimport org.junit.Test;import static org.junit.Assert.assertEquals;

public class MommifierTest {

@Test public void shouldNotMommifyEmptyString() {...}

@Test public void shouldNotMommifyWordsWithNoVowels() {...}

@Test public void shouldMommifyVowel() { String word = "a";

Mommifier mommifier = new Mommifier(); String mommifiedWord = mommifier.mommify(word);

assertEquals("mommy", mommifiedWord); }}

Page 48: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() { mommifier = new Mommifier(); } @Test public void shouldNotMommifyEmptyString() { assertEquals("", mommifier.mommify("")); }

@Test public void shouldNotMommifyWordsWithNoVowels() { assertEquals("str", mommifier.mommify("str")); }

@Test public void shouldMommifyVowel() { assertEquals("mommy", mommifier.mommify("a")); }

Page 49: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() { mommifier = new Mommifier(); } @Test public void shouldNotMommifyEmptyString() { assertEquals("", mommifier.mommify("")); }

@Test public void shouldNotMommifyWordsWithNoVowels() { assertEquals("str", mommifier.mommify("str")); }

@Test public void shouldMommifyVowel() { assertEquals("mommy", mommifier.mommify("a")); }

Page 50: TDD for the Win

TESTimport ...;

public class MommifierTest {

private Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...}

@Test public void shouldMommifyConsonantsAndSingleVowel() { assertEquals("blmommy", mommifier.mommify("bla")); }}

Page 51: TDD for the Win

TESTimport ...;

public class MommifierTest {

private Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...}

@Test public void shouldMommifyConsonantsAndSingleVowel() { assertEquals("blmommy", mommifier.mommify("bla")); }}

Page 52: TDD for the Win

SOURCEpublic class Mommifier {

private static final String VOWELS = "aeiou"; private static final String REPLACEMENT_WORD = "mommy";

public String mommify(String word) { String mommifiedWord = ""; for (Character character : word.toCharArray()) { if (isAVowel(character)) { mommifiedWord += REPLACEMENT_WORD; } else { mommifiedWord += character; } } return mommifiedWord; }

private boolean isAVowel(Character character) { return VOWELS.contains(character.toString()); }}

Page 53: TDD for the Win

TESTimport ...;

public class MommifierTest {

private Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...}

@Test public void shouldMommifyConsonantsAndSingleVowel() { assertEquals("blmommy", mommifier.mommify("bla")); }}

Page 54: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...}

@Test public void shouldNotMommifyLessThan30PercentVowels() { assertEquals("blah", mommifier.mommify("blah")); }

Page 55: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...}

@Test public void shouldNotMommifyLessThan30PercentVowels() { assertEquals("blah", mommifier.mommify("blah")); }

Page 56: TDD for the Win

SOURCE ...

private static final double THRESHOLD = 0.3; public String mommify(String word) { int vowelCount = 0; String mommifiedWord = ""; for (Character character : word.toCharArray()) { if (isAVowel(character)) { mommifiedWord += REPLACEMENT_WORD; vowelCount++; } else { mommifiedWord += character; } } if((double)vowelCount/word.toCharArray().length

< THRESHOLD){ return word; } return mommifiedWord; }

...

Page 57: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...}

@Test public void shouldNotMommifyLessThan30PercentVowels() { assertEquals("blah", mommifier.mommify("blah")); }

Page 58: TDD for the Win

SOURCE public String mommify(String word) { if(vowelCountLessThanThreshold(word)){ return word; } return replaceVowels(word); }

private boolean vowelCountLessThanThreshold(String word){...}

private String replaceVowels(String word) {...}

private boolean isAVowel(Character character) {...}

Page 59: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...}

@Test public void shouldNotMommifyLessThan30PercentVowels() { assertEquals("blah", mommifier.mommify("blah")); }

Page 60: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...}

@Test public void shouldMommifyContinuousVowelsOnlyOnce() { assertEquals("blmommyh”, mommifier.mommify("blaah")); }

Page 61: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...}

@Test public void shouldMommifyContinuousVowelsOnlyOnce() { assertEquals("blmommyh”, mommifier.mommify("blaah")); }

Page 62: TDD for the Win

SOURCE...

public String mommify(String word) {...}

private String replaceVowels(String word) { String mommifiedWord = ""; for (Character character : word.toCharArray()) { if (!isVowel(character)) { mommifiedWord += character; } else if(!mommifiedWord.endsWith(REPLACEMENT_WORD)){ mommifiedWord += REPLACEMENT_WORD; } } return mommifiedWord; }

private boolean vowelCountLessThanThreshold(String word){...}

private boolean isVowel(Character character) {...}

...

Page 63: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...}

@Test public void shouldMommifyContinuousVowelsOnlyOnce() { assertEquals("blmommyh”, mommifier.mommify("blaah")); }

Page 64: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...} @Test public void shouldMommifyContinuousVowelsOnlyOnce() {...}

@Test public void shouldMommifyMultipleSetsOfVowels() { assertEquals("blmommyhmommy”, mommifier.mommify("blaha")); }

Page 65: TDD for the Win

TESTprivate Mommifier mommifier;

@Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...} @Test public void shouldMommifyContinuousVowelsOnlyOnce() {...}

@Test public void shouldMommifyMultipleSetsOfVowels() { assertEquals("blmommyhmommy”, mommifier.mommify("blaha")); }

Page 66: TDD for the Win

TEST @Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...} @Test public void shouldMommifyContinuousVowelsOnlyOnce() {...} @Test public void shouldMommifyMultipleSetsOfVowels() {...} @Test public void shouldMommifyCapitalVowels() { assertEquals("BLmommy", mommifier.mommify("BLA")); }

Page 67: TDD for the Win

TEST @Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...} @Test public void shouldMommifyContinuousVowelsOnlyOnce() {...} @Test public void shouldMommifyMultipleSetsOfVowels() {...} @Test public void shouldMommifyCapitalVowels() { assertEquals("BLmommy", mommifier.mommify("BLA")); }

Page 68: TDD for the Win

SOURCEpublic class Mommifier {

private static final String VOWELS = "aeiou"; private static final String REPLACEMENT_WORD = "mommy"; private static final double THRESHOLD = 0.3;

public String mommify(String word) {...}

private String replaceVowels(String word) {...}

private boolean vowelCountLessThanThreshold(String word){...}

private boolean isVowel(Character character) { Character lowerCase = Character.toLowerCase(character); return VOWELS.contains(lowerCase.toString()); }}

Page 69: TDD for the Win

TEST @Before public void setUp() {...} @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...} @Test public void shouldMommifyContinuousVowelsOnlyOnce() {...} @Test public void shouldMommifyMultipleSetsOfVowels() {...} @Test public void shouldMommifyCapitalVowels() { assertEquals("BLmommy", mommifier.mommify("BLA")); }

Page 70: TDD for the Win

TEST @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...} @Test public void shouldMommifyContinuousVowelsOnlyOnce() {...} @Test public void shouldMommifyMultipleSetsOfVowels() {...} @Test public void shouldMommifyCapitalVowels() {...}

@Test(expected = NullPointerException.class) public void shouldErrorOnNull() { mommifier.mommify(null); }

Page 71: TDD for the Win

TEST @Test public void shouldNotMommifyEmptyString() {...} @Test public void shouldNotMommifyWordsWithNoVowels() {...} @Test public void shouldMommifyVowel() {...} @Test public void shouldMommifyConsonantsAndSingleVowel() {...} @Test public void shouldNotMommifyLessThan30PercentVowels() {...} @Test public void shouldMommifyContinuousVowelsOnlyOnce() {...} @Test public void shouldMommifyMultipleSetsOfVowels() {...} @Test public void shouldMommifyCapitalVowels() {...}

@Test(expected = NullPointerException.class) public void shouldErrorOnNull() { mommifier.mommify(null); }

Page 72: TDD for the Win

TEST CASES

“” → “”“str” → “str” “a” → “mommy”“blah” → “blah”“bla” → “blmommy” “blaa” → “blmommy”“blaaha” → “blmommyhmommy” “blA” → “blmommy”Null → raise exception

Page 73: TDD for the Win

TEST CASES

public void shouldNotMommifyEmptyStringpublic void shouldNotMommifyWordsWithNoVowelspublic void shouldMommifyVowelpublic void shouldMommifyConsonantsAndSingleVowelpublic void shouldNotMommifyLessThan30PercentVowelspublic void shouldMommifyContinuousVowelsOnlyOncepublic void shouldMommifyMultipleSetsOfVowelspublic void shouldMommifyCapitalVowelspublic void shouldErrorOnNull

Page 74: TDD for the Win

TOP DOWN TESTING

Page 75: TDD for the Win

SOME USEFUL BOOKS

Page 76: TDD for the Win

THANK YOU

Gabriel GavassoAnand Iyengar

Thao DangHung Dang

Mommifier Dojo by TWU team