Overview on TDD (Test Driven Development) & ATDD (Acceptance Test Driven Development)
Test driven development
-
Upload
suresh-thammishetty -
Category
Business
-
view
117 -
download
0
Transcript of Test driven development
Test Driven Development
Test-driven development (TDD) is a software development process that
relies on the repetition of a very short development cycle: first the
developer writes an (initially failing) automated test case that defines a
new function, then produces the minimum amount of code to pass that
test, and finally refactors the new code to acceptable standards
Test Driven Development
Page 1
Contents Test Driven Development: ..................................................................................................................... 2
Removing Dollar Side Effects: ........................................................................................................... 5
Equality for All: ................................................................................................................................... 6
Privacy: ............................................................................................................................................... 7
Franc-ly Speaking: .............................................................................................................................. 8
Equality for All, Redux: ...................................................................................................................... 9
Comparing Dollars and Francs:........................................................................................................ 11
Test Driven Development
Page 2
Test Driven Development:
Test-driven development (TDD) is a software development process that relies on the repetition
of a very short development cycle: first the developer writes an (initially failing) automated test
case that defines a new function, then produces the minimum amount of code to pass that test,
and finally refactors the new code to acceptable standards i.e.,
1. Quickly Add A Test
2. Run all the tests and see the new test fail
3. Change the Code
4. Run all the tests and see them all succeed
5. Refactor to remove duplication
Let’s discuss with one small example of multiplication:
Public void Multiplication () { Dollar Five=new Dollar (5); Five.times (2); AssertEquals (10, Five.amount); }
If we try running this test, it doesn’t even compile. It’s easy to fix. We have for Compilation Errors:
No class Dollar
No Constructor
No method times (int)
No field amount
Now start resolving one at a time:
We can come out of one error by defining a Class Dollar
Class Dollar
If we compile now, we will have only three errors. Now we need a Parameterized Constructor Dollar (int amount) { }
If we compile now, we will have only two errors. Now we need a method times (int multiplier)
Void times (int multiplier) { }
If we compile now, we will have only one error. Now we need an amount field
Int amount;
Now we don’t have any compilation errors but if we run this we will see one error noticed that we are expecting ‘10’ as a result, instead we saw ‘0’.
Test Driven Development
Page 3
Now are not going to like this solution, but our immediate goal is not to get the perfect answer but to pass the test.
Let’s make small change in the above code. This could pass our test
Int amount=10;
If we run this modified code, the test will pass however. But this is not a generalized code.
We can pass the test by the following code also
Int amount=5*2;
Here the code will be changed like this
Int amount; Void times (int multiplier) { Amount =5*2; }
The test still passes.
TDD is not about taking tiny steps, it’s about able to take tiny steps.
From the code we can have one question
Where can we get the value ‘5’? That was passed to the constructor. So we have to save it in the ‘amount’ variable
So the code will be like this
Dollar (int amount) { This.amount=amount; }
Then we can use this amount variable in times method
Then the code will be like this
Void times (int multiplier) { Amount=amount*2; }
Here ‘2’ is the multiplier. Then we can replace the value ‘2’ by variable ‘multiplier’
Then the code will be like this
Void times (int multiplier) {
Test Driven Development
Page 4
Amount=amount*multiplier; }
In short we replace
“Amount=amount*multiplier” by “amount*=multiplier”
Now the refactored code will be
Void times (int multiplier) { Amount*=multiplier; } The Complete Code is
Class Dollar { Int amount; Dollar (int amount) { This.amount=amount; } Void times (int multiplier) { Amount*=multiplier; } } We can use/call the method like Void Multiplication { Dollar Five=new dollar (5) Five.times (3); AssertEquals (15,five.amount);
Dollar Three=new dollar (3) Three.times (3); AssertEquals (9,three.amount); }
Test Driven Development
Page 5
Removing Dollar Side Effects:
Consider the below case
Void Multiplication { Dollar Five=new dollar (5) Five.times (2); AssertEquals (10, Five.amount); Five.times (3); AssertEquals (15, Five.amount); }
In this case, the second assert statement will because after the first call to times (), five isn’t five anymore. It’s actually 10. To make the second assert statement pass we need to return a Dollar object instead of a value.
This can be achieved as follows
Void Multiplication { Dollar Five=new dollar (5) Dollar Product = Five.times (2); AssertEquals (10, Product.amount); Product = Five.times (3); AssertEquals (15, Product.amount); }
The above test will not compile because our times () is returning value. To compile the test we need to change the times () function as follows:
Dollar times (int multiplier) { Return new Dollar (amount*multiplier); }
Test Driven Development
Page 6
Equality for All:
If I have an integer and I add 1 to it, I don’t expect the original integer to change; I expect to use
the new value. Objects usually don’t behave that way.
We can use objects as values, as we are using our Dollar now. The pattern for this is Value Object.
One of the constraints on Value Objects is that the values of the instance variables of the object
never change once they have been set in the constructor.
There is one huge advantage to using value objects; you don’t have to worry about aliasing
problems. Say I have one Check and I set its amount to $5, and then I set another Check’s amount
to the same $5. Some of the worst bugs in my career have come when changing the first Check’s
value inadvertently changed the second Check’s value. This is aliasing.
Public void testEquality () {
AssertTrue (new Dollar (5).equals (new Dollar (5))); }
Now we have to define equals () method with the fake result to make the code compiled
Public Boolean equals (Object object) {
Return true; }
Now we have to refactor the equals () method.
Public Boolean equals (Object object) {
Dollar dollar = (Dollar) object; Return amount == dollar.amount;
}
This is the generalized code
This results the correct output for the following code
Public void testEquality () {
AssertTrue (new Dollar (5).equals (new Dollar (5))); AssertTrue (new Dollar (5).equals (new Dollar (3)));
}
Test Driven Development
Page 7
Privacy:
From the above section (Equality for All), the below can we be refactored
Void DollarMultiplication { Dollar Five=new dollar (5) Dollar Product = Five.times (2); AssertEquals (10, Product.amount); Product = Five.times (3); AssertEquals (15, Product.amount); }
We can refactor the above code like
Void DollarMultiplication { Dollar Five=new dollar (5) Dollar Product = Five.times (2); AssertEquals (New Dollar (10), Product); Product = Five.times (3); AssertEquals (New Dollar (15), Product); }
We can even refactor the code by removing the temporary variable ‘Product’
Void DollarMultiplication { Dollar Five=new dollar (5) AssertEquals (New Dollar (10), Five.times (2)); AssertEquals (New Dollar (15), Five.times (2)); }
With these changes, the ‘Amount’ variable is only used by the instances of ‘Dollar’ class. So we
can make the Amount variable as ‘Private’
Private int Amount;
Test Driven Development
Page 8
Franc-ly Speaking:
If we can get the object franc to work the way the object dollar works now, we’ll be closer to
being able to write and run the mixed addition test.
Let’s write the above DollarMultiplication code for FrancMultiplication
Void FrancMultiplication { Franc Five=new dollar (5) AssertEquals (New Franc (10), Five.times (2)); AssertEquals (New Franc (15), Five.times (2)); }
So now we got error again. So we have to follow all the above steps. Then finally we get the
below code
Class Franc { Private int Amount; Franc times (int multiplier) { Return new Franc (Amount*multiplier); } Public Boolean equals (Object object) { Franc franc = (Franc) object; Return amount == franc.Amount; } }
Test Driven Development
Page 9
Equality for All, Redux:
Now we have the same function equals () for different objects (Dollar, Franc). We need to
generalize the equals () method which should be applicable for any objects.
Actually we got a new test case for the object Franc. But it is similar to the Object Dollar. It is
achieved just by Copying and Pasting the code. Now we have to clean up the code so that we
make use of single function for various objects.
One possibility is to make one of our Class extends the other. Instead, we are going to find a
common super class for the two classes.
Now from the above diagram, we have to create a superclass (i.e., Money), and then we have to
create classes ‘Dollar’ and ‘Franc’ which extends ‘Money’ class.
Class Money { }
Now we have to Create Dollar class by extending to Money
Class Dollar extends Money {
Private Int Amount; }
Will it run? No, the test still run but we have to declare the Amount Variable in ‘Money’ class as
protected. Like,
Class Money { Protected int Amount; }
Now no need of declaring the ‘Amount’ Variable in ‘Dollar’ and ‘Franc’ classes as it this variable it
made available from ‘Money’ Class
Now the class declarations will be
Class Dollar extends Money { }
And
Test Driven Development
Page 10
Class Dollar extends Money { }
Now we have to work on equals () method in ‘Dollar’ class. Firstly we have to create a temporary
variable of type ‘Money’ instead of ‘Dollar’
Dollar: Public Boolean equals (Object object) {
Money dollar = (Dollar) object; Return amount == dollar.amount;
}
Now we have change the cast
Dollar: Public Boolean equals (Object object) {
Money dollar = (Money) object; Return amount == dollar.amount;
}
Now we have to change the variable name
Dollar: Public Boolean equals (Object object) {
Money money = (Money) object; Return amount == money.amount;
}
Finally we have to move the equals function from Dollar class to Money Class
Money: Public Boolean equals (Object object) {
Money money = (Money) object; Return amount == money.amount;
}
The same thing is applicable in Franc Class also. So we have reduced the plenty of code till now.
Now we can use the same equals () method of Money class for both Dollar and Franc Class
Public Void testEquality () { AssertTrue (new Dollar (5).Equals (new Dollar (5))); AssertTrue (new Dollar (5).Equals (new Dollar (6))); AssertTrue (new Franc (5).Equals (new Franc (5))); AssertTrue (new Franc (5).Equals (new Franc (6))); }
Test Driven Development
Page 11
Comparing Dollars and Francs:
Now we got another problem. What happens when we compare Franc with Dollar?
Public Void testEquality () { AssertTrue (new Dollar (5).Equals (new Dollar (5))); AssertTrue (new Dollar (5).Equals (new Dollar (6))); AssertTrue (new Franc (5).Equals (new Franc (5))); AssertTrue (new Franc (5).Equals (new Franc (6))); AssertTrue (new Franc (5).Equals (new Dollar (5)));
AssertTrue (new Dollar (5).Equals (new Franc (5))); }
It results fail. The result is true when we compare 5 Dollars with 5 Francs, which should not. Now we have to fix this code in the equals () method of Money class. equals () method needs to check that whether it is comparing Dollars with Francs and vice versa. We can fix this by comparing class type of two objects.
It means two Moneys are equal only when the amount and Amount Types are equal.
So, we have to modify the code as follows
Money: Public Boolean equals (Object object) {
Money money = (Money) object; Return amount == money.amount && getClass (). Equals (money.getClass ());
}