TDD – Test Driven Development

TDD or Test Driven Development is related to the test-first programming concepts of extreme programming.

Test driven development where you write test before you develop a final code for your application.

Life cycle of test-driven development:

  1. Write a test
  2. Run the written test
  3. Write code that corrects the failing test
  4. Clean up your code
  5. Go to step 1.

In test driven development approach first step is to “write a test” and each new feature begins by writing a test.

Developers must have clear understanding of the specifications/requirements before they start writing any test that defines the function or improvements of the function.

The test-first programming for covering the requirements and exceptional conditions can be accomplished through use cases or user stories.

This is a differentiating feature of test-driven development versus writing unit tests after the code is written. This makes the developer focus on the requirements before writing the code.

Second, run all tests including new tests using a collection of test data configured to test a program function under various conditions to validate whether tests are working properly or not. If new tests pass without requiring new code then the required behavior already exists.

Third step is to write some code that causes the test to pass. The new code written at this stage is not perfect and may, for example, pass the test in an inelegant way. That is acceptable because it will be improved during the refactor of the growing code base. Here the only purpose of the written code is to pass the test.

The developer must not write code that is beyond the functionality that the test checks. If all test cases pass, the developer can be confident that the new code meets the test requirements, and does not break or degrade any existing features. If they do not, the new code must be adjusted until they do.

Fourth, the growing code base must be cleaned up regularly during test-driven development. Duplicate or redundant code must be removed in this phase.

Object, class, module, variable and method names should clearly represent their current purpose and use to improve readability and maintainability.

By continually re-running the test cases throughout each refactoring phase, the developer can be confident that process is not altering any existing functionality.

Fifth, starting with another new test, the cycle is then repeated to push forward the functionality. If new code does not rapidly satisfy a new test, or other tests fail unexpectedly, the programmer should undo or revert in preference to excessive debugging.

Continuous integration helps by providing revertible checkpoints.

TDD Example

Here we will see the example to implement Fibonacci method using Java.

In Fibonacci series, next number is the sum of previous two numbers.

For example, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 etc. The first two numbers of Fibonacci series are 0 and 1.

The mathematical definition:

f(n)=0, if n=0
f(n)=1, if n=1
f(n)=f(n-1)+f(n-2), if n>1

Let’s start. f(0) should be 0.

Write first test case:

@Test
public final void testFibonacci() {
       Assert.assertEquals(0, fibonacci(0));
}

As we write this test we will get an compilation error complaining about the missing fibonacci method.

Let’s create the fibonacci method:

public static int fibonacci(int n) {
	return 0;
}

Now running the test case makes the bar green.

Let’s create the test for fibonacci(1)=1:

@Test
public final void testFibonacci() {
	Assert.assertEquals(0, fibonacci(0));
	Assert.assertEquals(1, fibonacci(1));
}

Running the above test case will make the bar red because fibonacci series with value 1 doesn’t exist. So update the fibonacci method as below:

public static int fibonacci(int n) {
	if (n == 0)
		return 0;
	return 1;
}

Now the bar will be green.

We should change the code inside the test case because it has duplicate code. We will update the test case in such a way so that it will make one test for all possible values.

@Test
public final void testFibonacci() {
	int[][] testCases = { { 0, 0 }, { 1, 1 } };
	for (int i = 0; i < testCases.length; i++) {
		Assert.assertEquals(testCases[i][1], fibonacci(testCases[i][0]));
	}
}

Now by extending the test case as shown below will make the bar red, i.e., test will fail.

@Test
public final void testFibonacci() {
	int[][] testCases = { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 } };
	for (int i = 0; i < testCases.length; i++) {
		Assert.assertEquals(testCases[i][1], fibonacci(testCases[i][0]));
	}
}

Updating the code for fibonacci series as shown below will make it pass:

public static int fibonacci(int n) {
	if (n == 0)
		return 0;
	if (n == 1)
		return 1;
	return 2;
}

But the above code can be written also as:

public static int fibonacci(int n) {
	if (n == 0)
		return 0;
	if (n == 1)
		return 1;
	return 1 + 1;
}

Even we can change the above code for fibonacci series for n terms as shown below:

public static int fibonacci(int n) {
	if (n == 0)
		return 0;
	if (n == 1)
		return 1;
	return fibonacci(n - 1) + 1;
}

Now you could write more test cases to test the code:

@Test
public final void testFibonacci() {
	int[][] testCases = { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }, { 7, 13 },
			{ 8, 21 }, { 9, 34 }, { 10, 55 } };
	for (int i = 0; i < testCases.length; i++) {
		Assert.assertEquals(testCases[i][1], fibonacci(testCases[i][0]));
	}
}

And eventually the above code for fibonacci series could be changed to:

public static int fibonacci(int n) {
	if (n == 0)
		return 0;
	if (n == 1)
		return 1;
	return fibonacci(n - 1) + fibonacci(n - 2);
}

That’s all. This was a basic example on writing TDD code.

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *