Essential Test-Driven Development
-
Upload
techwellpresentations -
Category
Technology
-
view
55 -
download
0
description
Transcript of 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
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.
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
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
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
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
}
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()
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
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
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
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
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
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
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
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
}
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
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
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
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
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
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
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;
}}
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
// ...
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
// ...
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
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
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
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.
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
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
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
30
17 April 2013 © Agile Institute 2008-2013 59
http://PowersOfTwo.agileInstitute.com/
17 April 2013 © Agile Institute 2008-2013 60
@agilecoach