2. Who am I?
• Name: Steven Mak
• Agile Coach at Odd-e
• Lives in Hong Kong
• Agile, TDD Coaching
• I love coding - Java, C/C++,
PHP, Perl, C#, VB and
some weird languages
• I can speak Mandarin,
Cantonese, and English
2
Thursday, 4 November 2010
3. Agenda
• Test-Driven Development in 1 slide
• Readability
• Modifying tons of tests whenever we change code?!
3
Thursday, 4 November 2010
5. Test driven development
• 1 Rule:
- Only ever write code to fix a failing test
• 3 Steps:
1. Write a test (which fails “red”)
2. Code (to make test pass “green”)
3. Refactor (test still passes “green”)
5
Thursday, 4 November 2010
6. Role of TDD in iterative
development
• Iteration cycle: 2-4 weeks
• Daily build: Every day (of course)
• Continuous integration cycle: multiple times a day (preferably every 10
minutes)
• TDD Cycle: A few minutes (usually 3 - 15 min per cycle)
Iteration start
Iteration end
Integrate Integrate Integrate
6
Thursday, 4 November 2010
9. Have you seen tests like this?
TEST (TEST_AIH, TEST1)
TEST (TEST_AIH, TEST2)
TEST (TEST_AIH, TEST3)
TEST (TEST_AIH, TEST4)
9
Thursday, 4 November 2010
10. Whatʼs wrong?
TEST (TEST_AIH, FAIL_BAD_PARAM)
10
Thursday, 4 November 2010
11. What names tell us?
• Who
- Name of the SUT class
- Name of the method or feature being exercised
• Input
- Important characteristics of any input values
- Anything relevant about the state
• Output
- The outputs expected
- The expected post-exercise state
11
Thursday, 4 November 2010
12. TestDox convention
public class ListTests {
@Test public void throwsAnExceptionWhenRemovingAnItemItDoesntHold() { […]
What it does?
Credit: Growing OO Software, Guided by Tests
12
Thursday, 4 November 2010
13. TestDox convention
public class ListTests {
@Test public void throwsAnExceptionWhenRemovingAnItemItDoesntHold() { […]
Pre-condition / Input
Credit: Growing OO Software, Guided by Tests
13
Thursday, 4 November 2010
15. Test file organisation
• Keep Test Logic Out of Production Code
• Same logical package but physically store them
in a parallel source tree
15
Thursday, 4 November 2010
16. Test case classes
• Test case class per class?
• Test case class per feature?
• Test case class per fixture?
16
Thursday, 4 November 2010
17. Four-Phase Test Pattern
• Setup - establish the preconditions to the test
• Exercise - Do something to the system
• Verify - Check the expected outcome
• Cleanup - Return the SUT to its initial state after
the test
17
Thursday, 4 November 2010
18. Example
Setting up
TEST(LightScheduler, ScheduleWeekEndItsSaturday)
{
LightScheduler_ScheduleTurnOn(3, WEEKEND, 100);
FakeTimeService_SetDay(SATURDAY);
FakeTimeService_SetMinute(100);
LightScheduler_Wakeup(); Exercise
LONGS_EQUAL(3, FakeLightController_getLastId());
LONGS_EQUAL(LIGHT_ON, FakeLightController_getLastState());
}
Verify
Credit: Test Driven Development for Embedded C
18
Thursday, 4 November 2010
19. BDD Style
• Given some precondition
• When something happens
• Then something that is dependent on Given
and When should be true
19
Thursday, 4 November 2010
20. Example
TEST(LightScheduler, ScheduleOffWeekendAndItsSaturdayAndItsTime)
{
LightScheduler_ScheduleTurnOff(lightNumber, WEEKEND, scheduledMinute);
whenItBecomes(SATURDAY, scheduledMinute);
thenExpect(lightNumber, LIGHT_OFF);
}
Credit: Test Driven Development for Embedded C
20
Thursday, 4 November 2010
22. Have your tried fixture?
TEST_GROUP (TEST_thisObject)
{
void setup()
{
}
void teardown()
{
}
};
22
Thursday, 4 November 2010
23. Complex Data Creation
@Before
public void setUp() throws Exception {
alice = new Person();
alice.setId(1L);
alice.setFirstname("Alice");
alice.setLastname("Adams");
alice.setSsn("111111");
billy = new Person();
billy.setId(2L);
billy.setFirstname("Billy");
billy.setLastname("Burke");
billy.setSsn("222222");
clark = new Person();
clark.setId(3L);
clark.setFirstname("Clark");
clark.setLastname("Cable");
clark.setSsn("333333");
alice.isInLoveWith(billy);
}
Credit: Test Driven - Practical TDD and Acceptance TDD for Java Developers
23
Thursday, 4 November 2010
24. Parameterised Creation
public class ParameterizedCreationMethodExample {
private Person alice, billy, clark;
@Before
public void setUp() throws Exception {
clark = createPerson("Clark", "Cable");
billy = createPerson("Billy", "Burke");
alice = createPerson("Alice", "Adams");
alice.isInLoveWith(billy);
}
private Person createPerson(String firstName, String lastName) {
Person person = new Person();
person.setFirstname(firstName);
person.setLastname(lastName);
person.setId(UniqueNumber.next());
person.setSsn(String.valueOf(UniqueNumber.next()));
return person;
}
@Test
public void aliceShouldAcceptWhenProposedToByBilly()
throws Exception {
billy.proposeTo(alice);
assertTrue(alice.isEngagedWith(billy));
}
}
Credit: Test Driven - Practical TDD and Acceptance TDD for Java Developers
24
Thursday, 4 November 2010
25. More complex data creation
@Test public void chargesCustomerForTotalCostOfAllOrderedItems() {
Order order = new Order(
new Customer("Sherlock Holmes",
new Address("221b Baker Street",
"London",
new PostCode("NW1", "3RX"))));
order.addLine(new OrderLine("Deerstalker Hat", 1));
order.addLine(new OrderLine("Tweed Cape", 1));
[…]
}
25
Thursday, 4 November 2010
26. Test Data Builder
new OrderBuilder()
.fromCustomer(
new CustomerBuilder()
.withAddress(new AddressBuilder().withNoPostcode().build())
.build())
.build();
26
Thursday, 4 November 2010
27. Test Data Builder
public class OrderBuilder {
private Customer customer = new CustomerBuilder().build();
private List<OrderLine> lines = new ArrayList<OrderLine>();
private BigDecimal discountRate = BigDecimal.ZERO;
public static OrderBuilder anOrder() {
return new OrderBuilder();
}
public OrderBuilder withCustomer(Customer customer) {
this.customer = customer;
return this;
}
public OrderBuilder withOrderLines(OrderLines lines) {
this.lines = lines;
return this;
}
public OrderBuilder withDiscount(BigDecimal discountRate) {
this.discountRate = discountRate;
return this;
}
public Order build() {
Order order = new Order(customer);
for (OrderLine line : lines) order.addLine(line);
order.setDiscountRate(discountRate);
}
}
27
}
Thursday, 4 November 2010
30. Customised Assertions
#define CHECK_OBJ(a,b) CHECK_OBJ(a,b, __FILE__,__FILE__)
void CHECK_OBJ(struct* yourObj, struct* myObj, const char* file, int line)
{
if (structs are not equal) {
SimpleString errorMessage = StringFromFormat(
“My struct: %d, %p, %s”, myObj->d, myObj->p, myObj->s);
FAIL_LOCATION(errorMessage.asCharString(), file, line);
}
}
30
Thursday, 4 November 2010
31. At least: One concept per test
31
Thursday, 4 November 2010
32. Hamcrest
• Framework for writing declarative match criteria
String s = "yes we have no bananas today";
Matcher<String> containsBananas = new StringContains("bananas");
Matcher<String> containsMangoes = new StringContains("mangoes");
assertTrue(containsBananas.matches(s));
assertFalse(containsMangoes.matches(s));
Or even better
assertThat(s, containsString("bananas"));
assertThat(s, not(containsString("mangoes"));
http://code.google.com/p/hamcrest/ 32
Thursday, 4 November 2010
33. Meaningful Assertion Messages
• Donʼt repeat what the built-in test framework
outputs to the console (e.g. name of the test
method)
• Donʼt repeat what the test name explains
• If you donʼt have anything good to say, you donʼt
have to say anything
• Write what should have happened, or what failed
to happen, and possibly mention when it should
have happened
33
Thursday, 4 November 2010
34. Describe what happened
assertTrue(“Expected a > b but a was ‘“ +
a.toString() +
“‘ and b was ‘“ +
b.toString() + “‘“,
a > b);
Credit: The Art of Unit Testing
34
Thursday, 4 November 2010
37. Test/Code Duplication
• Too many expectations or too specific expectations lead to tests that fail
due to the slightest of refactorings
• Ways to avoid
-
Keep principles of behaviour verification in when writing
expectations
-
Consider using stubs instead of mocks if the expectations do not
help meet the goal of the test
37
Thursday, 4 November 2010
38. Donʼt forget higher level testing
• Tests pass with test doubles... but might still fail at production due to
unexpected / untested interactions
38
Thursday, 4 November 2010
39. Mock Overload
• Excessive use of mocks and expectations
- perhaps a design issue, are you following SRP?
39
Thursday, 4 November 2010
40. Principles
• Donʼt mock code you donʼt own - create your own interface to wrap the
interaction with the external API
• Only Mock your nearest neighbour
-
The law of Demeter
FRIENDS
40
Thursday, 4 November 2010
41. Indirection
• Assign the responsibility to an intermediate object to mediate between
other components or services so that they are not directly coupled
41
Thursday, 4 November 2010
42. Not testable?
• Do you follow good design principles?
42
Thursday, 4 November 2010
43. References
• Practical TDD and ATDD for Java Developers - Lasse Koskela
• Practices for scaling Agile and Lean practices - Craig Larman and Bas
Vodde
• Growing OO Software, guided by tests - Steve Freeman and Nat Pryce
• TDD for Embedded C - James Grenning
• xUnit Test Patterns - Gerard Meszaros
• Working Effectively with Legacy Code - Michael Feathers
• Clean Code - Robert Martin
43
Thursday, 4 November 2010
44. Doing it better is not harder.
Itʼs easier - do it better!
Thank you!
44
Thursday, 4 November 2010