Essential Test-Driven Development

32
TL Halfday Tutorial 6/4/2013 1:00 PM "Essential Test-driven Development" Presented by: Rob Myers Agile Institute Brought to you by: 340 Corporate Way, Suite 300, Orange Park, FL 32073 8882688770 9042780524 [email protected] www.sqe.com

description

Test-driven Development (TDD) is a powerful technique for combining software design, unit testing, and coding in a continuous process to increase reliability and produce better code design. Using the TDD approach, developers write programs in very short development cycles: first the developer writes a failing automated test case that defines a new function or improvement, then produces code to pass that test, and finally refactors the new code to acceptable standards. The developer repeats this process many times until the behavior is complete and fully tested. Rob Myers demonstrates the essential TDD techniques, including unit testing with the common xUnit family of open source development frameworks, refactoring as just-in-time design, plus Fake It, Triangulate, and Obvious Implementation. During this hands-on session, you’ll use exercises to practice the techniques. With many years of product development experience using TDD, Rob will address the questions that arise during your own relaxed exploration of test-driven development.

Transcript of Essential Test-Driven Development

Page 1: Essential Test-Driven Development

 

 

TL Half‐day Tutorial 6/4/2013 1:00 PM 

       

"Essential Test-driven Development"    

Presented by:

Rob Myers Agile Institute

         

Brought to you by:  

  

340 Corporate Way, Suite 300, Orange Park, FL 32073 888‐268‐8770 ∙ 904‐278‐0524 ∙ [email protected] ∙ www.sqe.com

Page 2: Essential Test-Driven Development

Rob Myers Agile Institute

Rob Myers is founder of the Agile Institute and a founding member of the Agile Cooperative. With twenty-seven years of professional experience on software development teams, Rob has consulted for leading companies in aerospace, government, medical, software, and financial sectors. He has been training and coaching organizations in Scrum and Extreme Programming (XP) management and development practices since 1999. Rob’s courses—including Essential Test-Driven Development and Essential Agile Principles and Practices—are a blend of enjoyable, interactive, hands-on labs plus practical dialog toward preserving sanity in the workplace. Rob performs short- and long-term coaching to encourage, solidify, and improve the team's agile practices.  

Page 3: Essential Test-Driven Development

1

As you enter the room…

1. Take a 3x5 card or piece of paper, create two columns.

2. Write these skills in the first column: TDD, Refactoring,

OO, Java (or C#), Eclipse (or IDEA or VisualStudio)

3. For each skill, rate yourself from 0 (never heard of it) to

10 (invented it), and record in the second column.

4. Find someone (a) whom you don’t usually work with; and

(b) who has somewhat complementary skill levels (with (b) who has somewhat complementary skill levels (with

them, your average is 4 to 6). Write down this person’s

name, but only if both a and b are true.

5. Repeat step 4 until you have two names.

17 April 2013 © Agile Institute 2008-2013 1

…get comfortable.

1. Choose someone from

your list to work with.y

2. Gather your belongings

and select a workstation

for you and your new

lab partner.

3. Settle in at that

workstation.

17 April 2013 © Agile Institute 2008-2013 2

Page 4: Essential Test-Driven Development

2

EssentialEssentialTest-Driven Development

Rob Myersfor

17 April 2013 © Agile Institute 2008-2013 3

forAgile Development Practices

West04 June 2013

Unit testing i is soooo

DEPRESSING

17 April 2013 © Agile Institute 2008-2013 4

Page 5: Essential Test-Driven Development

3

TDD is fun,and provides

much more than just unit-tests!just unit-tests!

17 April 2013 © Agile Institute 2008-2013 5

“The results of the case studies indicate

that the pre-release defect density of the

four products decreased between 40%p

and 90% relative to similar projects that

did not use the TDD practice.

Subjectively, the teams experienced a

15 35% i i i i i l d l

17 April 2013 © Agile Institute 2008-2013 6

15–35% increase in initial development

time after adopting TDD.”http://research.microsoft.com/en-us/projects/esm/nagappan_tdd.pdf, Nagappan et al,

© Springer Science + Business Media, LLC 2008

Page 6: Essential Test-Driven Development

4

TDD Demo

17 April 2013 © Agile Institute 2008-2013 7

No Magic

import org.junit.*;

public class FooTests {@Testpublic void fooBarIsBaz() {

Foo foo = new Foo();Assert.assertEquals("Baz", foo.bar());

}

17 April 2013 © Agile Institute 2008-2013 8

}

Page 7: Essential Test-Driven Development

5

Requirements & Conventions

import org.junit.*;

public class FooTests {@Testpublic void fooBarIsBaz() {

Foo foo = new Foo();Assert.assertEquals("Baz", foo.bar());

}

17 April 2013 © Agile Institute 2008-2013 9

}

Expected Actual

BarFoo

Basic UML Class Diagrams

+ void AbstractMethod(object parameter)

Baz

- int privateVariable

+ int PublicMethod()

17 April 2013 © Agile Institute 2008-2013 10

- void PrivateMethod()

Page 8: Essential Test-Driven Development

6

Global Currency Money-Market Account

17 April 2013 © Agile Institute 2008-2013 11

Global

17 April 2013 © Agile Institute 2008-2013 12

GlobalATM

Access

Page 9: Essential Test-Driven Development

7

Stories

• As Rob the US account holder, I want to be able to

withdraw USD from a US ATM, but select which currency

holdings to withdraw from.

• As Rob, traveling in Europe, I want to be able to withdraw

EUR from either my EUR holdings or my USD holdings. The

default should be the most beneficial to me at the time.

• Shortly after the end of each month, I want to receive a y ,

report of my holdings and balance. The total balance

should appear in the currency of my account address

(USD).

17 April 2013 © Agile Institute 2008-2013 13

Primary Objects

Currency

Account

Holding

17 April 2013 © Agile Institute 2008-2013 14

o d g

Page 10: Essential Test-Driven Development

8

More Detail

Currency

CurrencyConverter

Holding

valuecurrencycurrencyConverter

convert value to

17 April 2013 © Agile Institute 2008-2013 15

u e cy o e teco e t a ue toanother currency

Start Simple

To DoWhen currencies are the same.When currencies are the same.When value is zero.

17 April 2013 © Agile Institute 2008-2013 16

Page 11: Essential Test-Driven Development

9

Specification, and Interface

@Testpublic void givesSameValueWhenSameCurrencyRequested() {

CurrencyConverter converter = new CurrencyConverter();String sameCurrency = "USD";double sameValue = 100.0000;double converted = converter.convert(

sameValue, sameCurrency, sameCurrency);Assert.assertEquals(sameValue, converted, 0.00004);

}

17 April 2013 © Agile Institute 2008-2013 17

Will not compile. Will not compile.

“Thank You, But…”

public class CurrencyConverter {public double convert(

double value, String from, String to) {throw new RuntimeException(

"D'oh! convert() not YET implemented!");}

}

17 April 2013 © Agile Institute 2008-2013 18

Page 12: Essential Test-Driven Development

10

“…I Want to See it Fail Successfully”

public class CurrencyConverter {public double convert(

double value, String from, String to) {return 0.0;

}}

17 April 2013 © Agile Institute 2008-2013 19

Technique: “Fake It”

public class CurrencyConverter {public double convert(

double value, String from, String to) {return 100.0;

}}

17 April 2013 © Agile Institute 2008-2013 20

Page 13: Essential Test-Driven Development

11

a. Refactor Away the Duplication@Testpublic void givesSameValueWhenSameCurrencyRequested() {

CurrencyConverter converter = new CurrencyConverter();String sameCurrency = "USD";String sameCurrency USD ;double sameValue = 100.0000;double converted = converter.convert(

sameValue, sameCurrency, sameCurrency);Assert.assertEquals(sameValue, converted, 0.00004);

}

17 April 2013 © Agile Institute 2008-2013 21

public double convert(double value, String from, String to) {return 100.0;

}

b. Triangulate

17 April 2013 © Agile Institute 2008-2013 22

Page 14: Essential Test-Driven Development

12

We don’t add any behavior

without a failing test…

17 April 2013 © Agile Institute 2008-2013 23

Rodin’s The Thinker, photo by CJ on Wikipedia

Technique: “Triangulation”

@Testpublic void givesZeroWhenValueIsZero() {

CurrencyConverter converter = new CurrencyConverter();double zero = 0.0000;double converted = converter.convert(

zero, "USD", "EUR");Assert.assertEquals(zero, converted, 0.00004);

}

17 April 2013 © Agile Institute 2008-2013 24

Page 15: Essential Test-Driven Development

13

Still “Fake”?

public class CurrencyConverter {public double convert(

double value, String from, String to) {return value;

}}

17 April 2013 © Agile Institute 2008-2013 25

Maintain the Tests

@Testpublic void givesZeroWhenValueIsZero() {

CurrencyConverter converter = new CurrencyConverter();// ...

}

@Testpublic void givesSameValueWhenSameCurrencyRequested() {

CurrencyConverter converter = new CurrencyConverter();// ...

17 April 2013 © Agile Institute 2008-2013 26

}

Page 16: Essential Test-Driven Development

14

@Before Runs Before Each Testprivate CurrencyConverter converter;

@Beforepublic void initialize() {

converter = new CurrencyConverter();}

@Testpublic void givesZeroWhenValueIsZero() {

// ...}

17 April 2013 © Agile Institute 2008-2013 27

@Testpublic void givesSameValueWhenSameCurrencyRequested() {

// ...}

What is the Expected Answer?

@Testpublic void convertsDollarsToEuros() {

double converted = converter.convert(100.0000, "USD", "EUR");

Assert.assertEquals(???, converted, 0.00004);}

17 April 2013 © Agile Institute 2008-2013 28

Page 17: Essential Test-Driven Development

15

Let’s Invent a Scenario…

@Testpublic void convertsDollarsToEuros() {

double converted = converter.convert(100.0000, "USD", "EUR");

Assert.assertEquals(50.0000, converted, 0.00004);}

17 April 2013 © Agile Institute 2008-2013 29

…“Fake It”…

public double convert(double value, String from, String to) {if (to.equals(from))

return value;return value * 0.5000;

}

17 April 2013 © Agile Institute 2008-2013 30

Page 18: Essential Test-Driven Development

16

…Refactor…

public double convert(double value, String from, String to) {if (to.Equals(from))

return value;return value * conversionRate(from, to);

}

private double conversionRate(String from, String to) {return 0.5000;

}

17 April 2013 © Agile Institute 2008-2013 31

…And Make a Note of It

To DoWhen currencies are the same.When currencies are the same.When value is zero.Fix the hard-coded conversionRate

17 April 2013 © Agile Institute 2008-2013 32

Page 19: Essential Test-Driven Development

17

Well, What is It???

17 April 2013 © Agile Institute 2008-2013 33

Where does it come from?

Circumstantial Coupling?

CurrencyConverter

17 April 2013 © Agile Institute 2008-2013 34

Page 20: Essential Test-Driven Development

18

Managing the Transaction

CurrencyConverterAccount

ConversionRates

Holding

Holding

Holding

Holding

17 April 2013 © Agile Institute 2008-2013 35

o d g

Another Object Emerges

To Do

When currencies are the sameWhen currencies are the same.

When value is zero.

Fix hard-coded conversionRate().

Write ConversionRates.

Make a factory for ConversionRatesthat uses the FOREX.com Web Service.

17 April 2013 © Agile Institute 2008-2013 36

Page 21: Essential Test-Driven Development

19

import org.junit.Assert;import org.junit.Test;

New Test Class

public class ConversionRatesTest {@Testpublic void storesAndRetrievesRates() {

ConversionRates rates = new ConversionRates();String from = "USD";String to = "EUR";double rate = 0.7424;rates.putRate(from, to, rate);Assert.assertEquals(rate, rates.getRate(from, to),

0.0004);}

}

17 April 2013 © Agile Institute 2008-2013 37

Fail

public class ConversionRates {public void putRate(String from, String to, double rate) {}

public double getRate(String from, String to) {return 0;

}}

17 April 2013 © Agile Institute 2008-2013 38

Page 22: Essential Test-Driven Development

20

Technique: “Obvious Implementation”import java.util.HashMap;import java.util.Map;

public class ConversionRates {public class ConversionRates {private Map<String, Double> rates

= new HashMap<String, Double>();

public void putRate(String from, String to, double rate) {rates.put(from + to, rate);

}

17 April 2013 © Agile Institute 2008-2013 39

public double getRate(String from, String to) {return rates.get(from + to);

}}

Isolate Behaviorpublic class ConversionRates {

private Map<String, Double> rates= new HashMap<String, Double>();

public void putRate(String from, String to, double rate) {rates.put(key(from, to), rate);

}public double getRate(String from, String to) {

return rates.get(key(from, to));}

17 April 2013 © Agile Institute 2008-2013 40

private String key(String from, String to) {return from + to;

}}

Page 23: Essential Test-Driven Development

21

Next?

To Do

When currencies are the sameWhen currencies are the same.

When value is zero.

Fix hard-coded conversionRate().

Write ConversionRates.

Make a factory for ConversionRatesthat uses the FOREX.com Web Service.

17 April 2013 © Agile Institute 2008-2013 41

Fix @Before Method

public class CurrencyConverterTests {private CurrencyConverter converter;

@Beforepublic void initialize() {

ConversionRates rates = new ConversionRates();rates.putRate("USD", "EUR", 0.5000);converter = new CurrencyConverter(rates);

}

17 April 2013 © Agile Institute 2008-2013 42

// ...

Page 24: Essential Test-Driven Development

22

Partially Refactored…

public class CurrencyConverter {private ConversionRates rates;public CurrencyConverter(ConversionRates rates) {

this.rates = rates;}

private double conversionRate(String from, String to) {return 0.5000;

}

17 April 2013 © Agile Institute 2008-2013 43

// ...

Replace the Hard-Coded Value

public class CurrencyConverter {private ConversionRates rates;public CurrencyConverter(ConversionRates rates) {

this.rates = rates;}

private double conversionRate(String from, String to) {return rates.getRate(from, to);

}

17 April 2013 © Agile Institute 2008-2013 44

// ...

Page 25: Essential Test-Driven Development

23

What Remains?

To DoWhen currencies are the same.When currencies are the same.When value is zero.Fix hard-coded conversionRate().Write ConversionRates.Make a factory for ConversionRatesMake a factory for ConversionRatesthat uses the FOREX.com Web Service.

17 April 2013 © Agile Institute 2008-2013 45

What is the Missing Piece?

ConversionRates rates =

ConversionRates.byAccountAnd???(account, ???);

17 April 2013 © Agile Institute 2008-2013 46

Page 26: Essential Test-Driven Development

24

Ask What If…?

To DoMake a byAccountAndDate() factory Make a byAccountAndDate() factory for ConversionRates that uses the FOREX.com Web Service.ConversionRates returns 1/rate if inverse rate found.ConversionRates throws when rate not found.

17 April 2013 © Agile Institute 2008-2013 47

Testing Exceptional Behavior

@Test(expected=RateNotFoundException.class)public void throwsExceptionIfRateNotFoundII() {

ConversionRates rates = new ConversionRates();String from = "BAR";String to = "BAZ";rates.getRate(from, to);

}

17 April 2013 © Agile Institute 2008-2013 48

Page 27: Essential Test-Driven Development

25

A New Exception

public class RateNotFoundException extends RuntimeException {}

17 April 2013 © Agile Institute 2008-2013 49

17 April 2013 © Agile Institute 2008-2013 50

Page 28: Essential Test-Driven Development

26

Back in ConversionRates

public double getRate(String from, String to) {if (!rates.containsKey(key(from, to)))

throw new RateNotFoundException();return rates.get(key(from, to));

}

17 April 2013 © Agile Institute 2008-2013 51

1. Write one unit test.

2. Build or add to the object under test until everything compiles.

steps

3. Red: Watch the test fail!

4. Green: Make all the tests pass by changing the object under test.

17 April 2013 © Agile Institute 2008-2013 52

5. Clean: Refactor mercilessly!

6. Repeat.

Page 29: Essential Test-Driven Development

27

Runs all the tests.Expresses every idea required.

Says everything once and only once.Has no superfluous parts.

17 April 2013 © Agile Institute 2008-2013 53

Exercise A: Password-Strength Checker

In order to be an acceptable password, a string must:

Have a length greater than 7 characters.

Contain at least one alphabetic Co ta at east o e a p abet ccharacter.

Contain at least one digit.

17 April 2013 © Agile Institute 2008-2013 54

Page 30: Essential Test-Driven Development

28

1. Write one unit test.

2. Build or add to the object under test until everything compiles.

steps

3. Red: Watch the test fail!

4. Green: Make all the tests pass by changing the object under test.

17 April 2013 © Agile Institute 2008-2013 55

5. Clean: Refactor mercilessly!

6. Repeat.

Iteration 2

• Admins and regular users:

• Admin passwords must also...

• Be > 10 chars long

• Contain a special character

• People want to know all the reasons why their password has failed.

• Other stronger rules may apply later. ;-)

• HINT: What is varying most frequently?

Be sure to take a break when you need one!

17 April 2013 © Agile Institute 2008-2013 56

Page 31: Essential Test-Driven Development

29

1. Write one unit test.

2. Build or add to the object under test until everything compiles.

steps

3. Red: Watch the test fail!

4. Green: Make all the tests pass by changing the object under test.

17 April 2013 © Agile Institute 2008-2013 57

5. Clean: Refactor mercilessly!

6. Repeat.

1. _______________________________________

2. _______________________________________

3. _______________________________________

CLOSING

17 April 2013 © Agile Institute 2008-2013 58

Page 32: Essential Test-Driven Development

30

17 April 2013 © Agile Institute 2008-2013 59

[email protected]

http://PowersOfTwo.agileInstitute.com/

17 April 2013 © Agile Institute 2008-2013 60

@agilecoach