12
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

Embed Size (px)

Citation preview

Page 1: 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

Page 2: Test driven development

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

Page 3: Test driven development

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’.

Page 4: Test driven development

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) {

Page 5: Test driven development

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); }

Page 6: Test driven development

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); }

Page 7: Test driven development

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)));

}

Page 8: Test driven development

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;

Page 9: Test driven development

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; } }

Page 10: Test driven development

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

Page 11: Test driven development

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))); }

Page 12: Test driven development

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 ());

}