2/2/14

Test-Driven Development : A practitioner's guide

What is  TDD

Test-driven development is a programming technique that requires you to write actual code and automated test code simultaneously. This ensures that you test your code—and enables you to retest your code quickly and easily, since it’s automated.

The way to Start TDD is the dumb mans approach
  • you write the test first
  • then you write your working code
  • until test  pass you change your code
  • once test is pass .. add more test cases
  • then again change the code
  • repeat above steps as necessary


lets start with a code here i'm try to get the Fibonacci number


first of all you write the test

public class FibonacciTest {

     
    @Test
    public void testGetFibonacci() {
        Fibonacci f = new Fibonacci();
        int result =f.getFibonacci(0);
        assertEquals(result, 0);
        
    }
}

as my test says there should be a class name Fibonacci and a method getFibonacci that should return int value and accept a int parameter

once we pass the value 0 to getFibonacci it should return 0 (since Fibonacci 0 = 0)

lest write the base code

public class Fibonacci {

    public int getFibonacci(int n) {
        return 0;
    }
}

this is not even Fibonacci but all we wanted to to is get the test pass ,.... and now its passing so
lets add one more test case :D

public class FibonacciTest {

     
    @Test
    public void testGetFibonacci() {
        Fibonacci f = new Fibonacci();
        int n[] = new int[2];
        n[0] = 0;
        n[1] = 1;
        
        
        for (int i = 0; i < n.length; i++) {
            
           int result = f.getFibonacci(i);
           assertEquals(n[i], result); 
        }
        
    }
}
in here I'm try to use an int array as the Fibonacci series index is the position and  value will be the Fibonacci value corresponding
        int n[] = new int[2];
        n[0] = 0; -> Fibonacci 0 =0
        n[1] = 1; -> Fibonacci 1 =1

Now the test start to fail so you can go back to code and modify
public class Fibonacci {

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


Ok now run the test and see its passing

lets add another test case  n[2] = 1; -> Fibonacci 2 =1 too

public class FibonacciTest {

     
    @Test
    public void testGetFibonacci() {
        Fibonacci f = new Fibonacci();
        int n[] = new int[3];
        n[0] = 0;
        n[1] = 1;
        n[2] = 1;
        
        for (int i = 0; i < n.length; i++) {
            
           int result = f.getFibonacci(i);
           assertEquals(n[i], result); 
        }
        
    }
}

well that's not add a much value still test pass
lets add next value of series Fibonacci 3 =2

public class FibonacciTest {

     
    @Test
    public void testGetFibonacci() {
        Fibonacci f = new Fibonacci();
        int n[] = new int[4];
        n[0] = 0;
        n[1] = 1;
        n[2] = 1;
        n[3] = 2;

        for (int i = 0; i < n.length; i++) {
            
           int result = f.getFibonacci(i);
           assertEquals(n[i], result); 
        }
        
    }
}

Ok now test is falling lets move to code and edit
 public class Fibonacci {

    public int getFibonacci(int n) {
       if (n == 0) {
            return 0;
        } else if(n<3) {
            return 1;
        }else{
          return 2;
        }
    }
}

Still I'm using mocking here and now my code have lot of if else and returns
shall we add more test case :D
public class FibonacciTest {

     
    @Test
    public void testGetFibonacci() {
        Fibonacci f = new Fibonacci();
        int n[] = new int[5];
        n[0] = 0;
        n[1] = 1;
        n[2] = 1;
        n[3] = 2;
        n[4] = 3;

        for (int i = 0; i < n.length; i++) {
            
           int result = f.getFibonacci(i);
           assertEquals(n[i], result); 
        }
        
    }
}

Ok test fails that means more space to improve code

let see if you check the values of Fibonacci (0,1,1,2,3,5,8,13,21,34,55,89,144 .... so on) Fibonacci value of n is Fibonacci value of n-1 +   Fibonacci value of n-2
 and all the base a re less than Fibonacci 0 ,1,2, so from Fibonacci 3 and up its summation of two previous consecutive Fibonacci numbers

after n =3 lets call getFibonacci(n)  again with  getFibonacci(n-1) + getFibonacci(n-2)

 public class Fibonacci {

    public int getFibonacci(int n) {
       if (n == 0) {
            return 0;
        } else if(n<3) {
            return 1;
        }else{
          return getFibonacci(n-1)+ getFibonacci(n-2);
        }
    }
}

And add more test to see it working

public class FibonacciTest {

     
    @Test
    public void testGetFibonacci() {
        Fibonacci f = new Fibonacci();
        int n[] = new int[10];
        n[0] = 0;
        n[1] = 1;
        n[2] = 1;
        n[3] = 2;
        n[4] = 3;
        n[5] = 5;
        n[6] = 8;
        n[7] = 13;
        n[8] = 21;
        n[9] = 34;

        for (int i = 0; i < n.length; i++) {
            
           int result = f.getFibonacci(i);
           assertEquals(n[i], result); 
        }
        
    }
}

so here is my way of TDD ... now I have a good test and some how good Code (still have lot of returns in code ) but since I have the perfect test I can refactor my code with out hassle

I will hope this will help you to get the 101 about TDD skill level need for this is Java and junit testing nothing much

in the first few months when I use TDD i was not much a fan of this but once you get it by heart you will feel the awesomeness of TDD

Pro tips
when you writing classes and want to test properly try to stick with hi-cohesion and low-coupling thing
and try to avoid initiate inside methods instead of try to use strategy pattern and getters and setters

then lets see with  Code refactoring : A practitioner's guide :D

0 comments:

Post a Comment