Tdd

Preview:

Citation preview

Introduction to !

Test Driven Development

Prasanna N Venkatesan

Unit Testing is a practice where the smallest testable parts of an application (units) are individually and independently tested.

Unit Tests are developer tests. It enables developers to do “Refactoring” and helps in “Code Maintenance”

Test Driven Development (TDD) is a Software development technique which advocates to test the code that's to be written before it is written.

Test Driven Development (TDD) is mostly driven through Unit Tests

Not a Silver Bullet

“TDD doesn't drive good design. TDD gives you immediate feedback about what is likely to be a bad design.”

!

- Kent Beck

Design / Think

Failing Test

Make the test pass

Refactor TDD

Design / Think

Failing Test

Make the test

pass

Refactor TDD

Design / Think!

!

Figure out what test will best move your code towards completion. (Take as much time as you need. This is the hardest step for beginners.)

Design / Think

Failing Test

Make the test

pass

Refactor TDD

Write a Failing Test!

!

Write a very small amount of test code. Only a few lines. Usually no more than five lines. Run the tests and watch the new test fail. The test bar should turn red.

Design / Think

Failing Test

Make the test

pass

Refactor TDD!

Make The Test Pass!

!

Write a very small amount of production code. Again, usually no more than five lines of code. Don't worry about design purity or conceptual elegance. Sometimes you can just hardcode the answer. Run the tests and watch them pass the test bar will turn green.

Design / Think

Failing Test

Make the test

pass

Refactor TDD!

Refactor!

!

Now that your tests are passing, you can make changes without worrying about breaking anything. Look at the code you've written, and ask yourself if you can improve it. After each little refactoring, run the tests and make sure they still pass.

3 Rules of TDD

Rule 1

You can’t write production code unless it makes a failing unit test pass.

Rule 2

You can’t write any more of unit test that is sufficient to fail, and not compiling is failing

Rule 3

You can’t write any more production code than is sufficient to pass one failing unit test.

“But one should not first make the program and then prove its correctness, because then the requirement of providing the proof would only increase the poor programmer's burden. On the contrary: the programmer should let correctness proof and program grow hand in hand.” !

The Humble Programmer, Edsger W. Dijkstra 1972

Test First vs Testing

✦ Think small, iteratively develop. ✦ More focus on the problem to be

solved. ✦ Immediate feedback of the code. ✦ No excuses for not testing.

@Test public void shouldReturnTrueForSaturday(){ DeliveryDate deliveryDate = new DeliveryDate(); boolean result = deliveryDate.isWeekendOrHoliday(); assertTrue("Should be true for Saturday",result); }

First Test - Should Return true for Saturday

public class DeliveryDate { ! public boolean isWeekendOrHoliday() { return false; } }

@Test public void shouldReturnTrueForSaturday(){ DeliveryDate deliveryDate = new DeliveryDate(); boolean result = deliveryDate.isWeekendOrHoliday(); assertTrue("Should be true for Saturday",result); }

First Test - Should Return true for Saturday

public class DeliveryDate { ! public boolean isWeekendOrHoliday() { return true; } }

@Test public void shouldReturnTrueForSaturday(){ Calendar calendarInstance = getCalendarForDay(Calendar.SATURDAY); DeliveryDate deliveryDate = new DeliveryDate(calendarInstance); boolean result = deliveryDate.isWeekendOrHoliday(); assertTrue("Should be true for Saturday",result); }

First Test - Should Return true for Saturday

public class DeliveryDate { private Calendar calendar; public DeliveryDate(Calendar calendar){ this.calendar = calendar; } ! public boolean isWeekendOrHoliday() { if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY){ return true; } return false; } }

@Test public void shouldReturnTrueForSunday(){ Calendar calendarInstance = getCalendarForDay(Calendar.SUNDAY); DeliveryDate deliveryDate = new DeliveryDate(calendarInstance); boolean result = deliveryDate.isWeekendOrHoliday(); assertTrue("Should be true for Sunday",result); }

Second Test - Should Return true for Sunday

public class DeliveryDate { private Calendar calendar; public DeliveryDate(Calendar calendar){ this.calendar = calendar; } ! public boolean isWeekendOrHoliday() { if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){ return true; } return false; } }

@Test public void shouldReturnTrueIfPresentInHolidayList(){ Calendar calendarInstance = getCalendarForDay(Calendar.FRIDAY); DeliveryDate deliveryDate = new DeliveryDate(calendarInstance); boolean result = deliveryDate.isWeekendOrHoliday(asList(calendarInstance.getTime())); assertTrue("Should be true for Holiday",result); }

Third Test - Should Return true for Holidays

public class DeliveryDate { private Calendar calendar; public DeliveryDate(Calendar calendar){ this.calendar = calendar; } ! public boolean isWeekendOrHoliday(List<Date> holidayList) { if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY || holidayList.contains(calendar.getTime())){ return true; } return false; } }

@Test public void shouldReturnFalseIfNotPresentInHolidayListAndNotAWeekend(){ Calendar calendarInstance = getCalendarForDay(Calendar.FRIDAY); DeliveryDate deliveryDate = new DeliveryDate(calendarInstance); boolean result = deliveryDate.isWeekendOrHoliday(new ArrayList<Date>()); assertFalse("Should be false for Friday",result); }

One More to confirm that it’s working.

public class DeliveryDate { private Calendar calendar; public DeliveryDate(Calendar calendar){ this.calendar = calendar; } ! public boolean isWeekendOrHoliday(List<Date> holidayList) { if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY || holidayList.contains(calendar.getTime())){ return true; } return false; } }

@Test public void shouldReturnFalseIfNotPresentInHolidayListAndNotAWeekend(){ Calendar calendarInstance = getCalendarForDay(Calendar.FRIDAY); DeliveryDate deliveryDate = new DeliveryDate(calendarInstance); boolean result = deliveryDate.isWeekendOrHoliday(null); // No idea ?? }

What if ??

Unit Test should be

Fast Independent Repeatable Self-Validating Timely

Test Doubles

Mocks are pre-programmed with expectations which form a specification of the calls they are expected to receive.

Test Doubles

Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.

Test Doubles

Spies are stubs that also record some information based on how they were called.

Test Doubles

✤ Dummy objects that are passed around but never actually used. !

✤ Fake objects actually have working implementation (simpler)

class Washer def wash(clothes,detergent) if(isInWorkingCondition? and power.isOn?){ while(!waterIsFull){ inlet.fillWater(); } 100.times do base.spin(); outlet.dispense(); set clothes.washed = true return clothes unless hasException? } throw DirtyClothesException! end def isInWorkingCondition? // Does too many checks to make sure that end end

class WasherTest def wash_returnTrueOnSuccessfulWash //Mocking External systems when(MockPower.isOn?).thenReturn(true) //Stubbing - Overriding Behaviours def fillWater(){ set waterIsFull = true if 100.times.called } //Spying - Internal behaviours when(isInWorkingCondition?()).then(true) washedClothes = Washer.wash(dirtyClothes,some_detergent) assert washedClothes.getStatus == CLEAN end end

A quick walkthrough of Test Doubles

** END **