Test-Driven Development: A walkthrough using Java

What
Is

TDD ?

Why Developers don't use         at first?

"There is no time to write unit tests"

TDD

"Haven't used before and my code is good"

"It won't make my code bullet-proof"

Requires a paradigm shift

It's Simple but...

is not just about Testing.

TDD

Better structure in code

Reduce time debugging

Make a plan, then code

TDD

Best Practices.

Naming Conventions :

Place Test and Implementation in different packages.

Use descriptive names for methods.

public final void whenMoreThan2NumbersAreUsedThenExceptionIsThrown() {
        StringCalculator.add("1,2,3");
    }

Test

Processes:

  • Write tests before implementation code.
  • Rerun all tests every time implementation code changes.
  • All test should pass before new test is written.
  • Refactor only after all test are passing.

Development Practices:

Write simplest code to pass the test. 

Assertions goes first.

Minimize assertions in each test

A practical example:

List of requirements to make a String Calculator:

  • Create a simple String calculator with a method int Add(string numbers)
  • The method can take 0, 1 or 2 numbers, and will return their sum (for an empty string it will return 0) for example “” or “1” or “1,2”
  • Allow the Add method to handle an unknown amount of numbers

Requirement 1:

The method can take up to 2 string numbers, each one separated by a comma(,)

class CalculatorTest extends groovy.util.GroovyTestCase {

    @Test(expected = RuntimeException.class)

    public final void whenMoreThan2NumbersAreUsedThrowException() {
        StringCalculator.add("1,2,3");
    }
    @Test
    public final void when2NumbersAreUsedDoNotThrowException() {
        StringCalculator.add("1,2");
        Assert.assertTrue(true);
    }
    @Test(expected = RuntimeException.class)
    public final void whenNonNumberIsUsedThenExceptionIsThrown() {
        StringCalculator.add("1,X");
    }


}

Test: 

The method can take up to 2 string numbers, each one separated by a comma(,)

public class StringCalculator1 {
    public static final void add(final String numbers) {
        String[] numbersArray = numbers.split(",");
        if (numbersArray.length > 2) {
            throw new RuntimeException("Up to 2 numbers separated by comma (,) are allowed");
        } else {
            for (String number : numbersArray) {
                Integer.parseInt(number); // If it is not a number, parseInt will throw an exception
            }
        }
    }
}

Implementation:

Requirement 2:

@Test
public final void whenEmptyStringIsUsedThenReturnValueIs0() {
    Assert.assertEquals(0, StringCalculator.add(""));
}

Test

public static final int add(final String numbers) { // Changed void to int
    String[] numbersArray = numbers.split(",");
    if (numbersArray.length > 2) {
        throw new RuntimeException("Up to 2 numbers separated by comma (,) are allowed");
    } else {
        for (String number : numbersArray) {
            if (!number.isEmpty()) {
                Integer.parseInt(number);
            }
        }
    }
    return 0; // Added return
}

Implementation

For an empty string the method will return 0

TDD

Let's see

  in action!

Text

Monday Talks 03.09.15

By Luis Hernandez

Monday Talks 03.09.15

  • 642