unbearable test code smell
DESCRIPTION
TRANSCRIPT
![Page 2: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/2.jpg)
Who am I?
2
Name: Steven Mak
Agile Coach at Odd-e
Lives in Hong Kong
Agile/Scrum, TDD Coaching
I love coding - Java, C/C++, PHP, Perl, C#, VB, and some weird languages
Monday, 15 November 2010
![Page 3: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/3.jpg)
Copy and Paste CodeLong test codes are copied and pasted somewhere else with only a few lines changing
3
Monday, 15 November 2010
![Page 4: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/4.jpg)
DRYDonʼt Repeat Yourself!Donʼt Repeat Yourself!Donʼt Repeat Yourself!Donʼt Repeat Yourself!Donʼt Repeat Yourself!Donʼt Repeat Yourself!Donʼt Repeat Yourself!Donʼt Repeat Yourself!
4
Monday, 15 November 2010
![Page 5: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/5.jpg)
Not knowing the fixturesSome initialisation and clean up codes that are repeated in each tests...
5
Monday, 15 November 2010
![Page 6: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/6.jpg)
What is fixture?TEST_GROUP (TEST_thisObject){
void setup() {}void teardown() {}
};
6
Monday, 15 November 2010
![Page 7: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/7.jpg)
Duplication causing fragile tests
Where is the duplication?EXPECT_LOG(“ABC error”);
7
Monday, 15 November 2010
![Page 8: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/8.jpg)
Duplication causing fragile tests
Where is the duplication?EXPECT_LOG(“ABC error”);
So there is a line in code that prints this log message
8
Monday, 15 November 2010
![Page 9: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/9.jpg)
Duplication causing fragile tests
Put it under centralise header file:#define ABC_ERROR_WITH_EC “ABC error”
The test will then look like:EXPECT_LOG(ABC_ERROR);
9
Monday, 15 November 2010
![Page 10: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/10.jpg)
Over-Optimism?Tests that forgot to cover exceptional cases or just covered the easiest condition
if (aaa() || bbb() || ccc() {...
} else {...
}
10
Monday, 15 November 2010
![Page 11: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/11.jpg)
Tests donʼt have assertionsTEST(TEST_GROUP, TEST_THIS){
runThisFunctionLaLaLa();}
11
Monday, 15 November 2010
![Page 12: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/12.jpg)
12
What does it mean by 80% Unit Test Coverage?
Monday, 15 November 2010
![Page 13: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/13.jpg)
Why xUnits donʼt have CHECK_NOT_EQUAL?
What is the problem with:CHECK(TRUE, xxx != 3);
13
Monday, 15 November 2010
![Page 14: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/14.jpg)
Why xUnits donʼt have CHECK_NOT_EQUAL?
What is the problem with:CHECK(TRUE, xxx != 3);
Is there any good reason why you cannot know the output value?
So, tell me what it is then. 14
Monday, 15 November 2010
![Page 15: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/15.jpg)
OK, fine, so I use CHECK with a specific output value, what now?
What is the problem with:CHECK(TRUE, xxx == 4);
15
Monday, 15 November 2010
![Page 16: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/16.jpg)
OK, fine, so I use CHECK with a specific output value, what now?
What is the problem with:CHECK(TRUE, xxx == 4);
In most xUnits, we have LONGS_EQUAL telling you the actual value when it goes wrong instead of a
“false”16
Monday, 15 November 2010
![Page 17: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/17.jpg)
17
Do you know your xUnit harness?
Monday, 15 November 2010
![Page 18: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/18.jpg)
Further example
try { readConfigurationFile(); assertTrue(true); } catch (IOException e) { assertTrue(false); e.printStackTrace(); }
These are the places you know your team does not know the test harness.
18
Monday, 15 November 2010
![Page 19: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/19.jpg)
19
Some xUnit harness
Java: JUnit
.Net: NUnit
C/C++: CppUTest
PHP: PHPUnit
Monday, 15 November 2010
![Page 20: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/20.jpg)
Whatʼs wrong?
What is the problem with: TEST(TEST_AIH, FAIL_BAD_PARAM)
20
Monday, 15 November 2010
![Page 21: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/21.jpg)
Names donʼt really tell
What is the problem with:TEST(TEST_AIH, FAIL_BAD_PARAM)
Be more precise about how it triggered the failure
21
Monday, 15 November 2010
![Page 22: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/22.jpg)
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
22
Monday, 15 November 2010
![Page 23: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/23.jpg)
Conditional Test Logic?
// verify Vancouver is in the listactual = null;i = flightsFromCalgary.iterator();while (i.hasNext()) { FlightDto flightDto = (FlightDto) i.next(); if (flightDto.getFlightNumber().equals( expectedCalgaryToVan.getFlightNumber())) { actual = flightDto; assertEquals("Flight from Calgary to Vancouver", expectedCalgaryToVan, flightDto); break; }}
23
Monday, 15 November 2010
![Page 24: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/24.jpg)
Tests that crash 50% of the time?!!
24
Monday, 15 November 2010
![Page 25: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/25.jpg)
public void testFlightMileage_asKm2() throws Exception { // set up fixture // exercise constructor Flight newFlight = new Flight(validFlightNumber); // verify constructed object assertEquals(validFlightNumber, newFlight.number); assertEquals("", newFlight.airlineCode); assertNull(newFlight.airline); // set up mileage newFlight.setMileage(1122); // exercise mileage translator int actualKilometres = newFlight.getMileageAsKm(); // verify results int expectedKilometres = 1810; assertEquals( expectedKilometres, actualKilometres); // now try it with a canceled flight newFlight.cancel(); try { newFlight.getMileageAsKm(); fail("Expected exception"); } catch (InvalidRequestException e) { assertEquals( "Cannot get cancelled flight mileage", e.getMessage()); }}
25
Testing everything at a time
Monday, 15 November 2010
![Page 26: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/26.jpg)
Testing everything at a timepublic void testFlightMileage_asKm2() throws Exception { // set up fixture // exercise constructor Flight newFlight = new Flight(validFlightNumber); // verify constructed object assertEquals(validFlightNumber, newFlight.number); assertEquals("", newFlight.airlineCode); assertNull(newFlight.airline); // set up mileage newFlight.setMileage(1122); // exercise mileage translator int actualKilometres = newFlight.getMileageAsKm(); // verify results int expectedKilometres = 1810; assertEquals( expectedKilometres, actualKilometres); // now try it with a canceled flight newFlight.cancel(); try { newFlight.getMileageAsKm(); fail("Expected exception"); } catch (InvalidRequestException e) { assertEquals( "Cannot get cancelled flight mileage", e.getMessage()); }}
26
Comments as deodorant
Monday, 15 November 2010
![Page 27: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/27.jpg)
Testing everything at a timepublic void testFlightMileage_asKm2() throws Exception { // set up fixture // exercise constructor Flight newFlight = new Flight(validFlightNumber); // verify constructed object assertEquals(validFlightNumber, newFlight.number); assertEquals("", newFlight.airlineCode); assertNull(newFlight.airline); // set up mileage newFlight.setMileage(1122); // exercise mileage translator int actualKilometres = newFlight.getMileageAsKm(); // verify results int expectedKilometres = 1810; assertEquals( expectedKilometres, actualKilometres); // now try it with a canceled flight newFlight.cancel(); try { newFlight.getMileageAsKm(); fail("Expected exception"); } catch (InvalidRequestException e) { assertEquals( "Cannot get cancelled flight mileage", e.getMessage()); }}
27
Duplications with application logic?
Monday, 15 November 2010
![Page 28: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/28.jpg)
Inappropriate dependencies• Test setup depending on other tests files• A test file depending on another test file• Stub functions depending on other tests
extern int reg_ecx; // in the stub program
int reg_exc; // in SUT
28
Monday, 15 November 2010
![Page 29: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/29.jpg)
What can we do?
29
Monday, 15 November 2010
![Page 30: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/30.jpg)
Try: one test group per fileBut why canʼt? is it because of... ?
30
Monday, 15 November 2010
![Page 31: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/31.jpg)
Test initialisation hard to read and shared among test groups
in the same test file• Fixtures• Test Data Builder• Parameterised Creation• make-it-easy
31
Monday, 15 November 2010
![Page 32: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/32.jpg)
Dont forget fixtures
TEST_GROUP (TEST_thisObject){ void setup() { }
void teardown() { }};
32
Monday, 15 November 2010
![Page 33: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/33.jpg)
Test Data Buildereth_data_buf->setControl(2)->withParameterA(3)->build();
33
Monday, 15 November 2010
![Page 34: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/34.jpg)
Parameterised Creation@Beforepublic 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);}
34
Monday, 15 November 2010
![Page 35: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/35.jpg)
Parameterised Creationpublic 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)); }}
35
Monday, 15 November 2010
![Page 36: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/36.jpg)
make-it-easy
36
http://code.google.com/p/make-it-easy/
Maker<Apple> appleWith2Leaves = an(Apple, with(2, leaves));Maker<Apple> ripeApple = appleWith2Leaves.but(with(ripeness, 0.9));Maker<Apple> unripeApple = appleWith2Leaves.but(with(ripeness, 0.125)); Apple apple1 = make(ripeApple);Apple apple2 = make(unripeApple); Banana defaultBanana = make(a(Banana));Banana straightBanana = make(a(Banana, with(curve, 0.0)));Banana squishyBanana = make(a(Banana, with(ripeness, 1.0)));
Monday, 15 November 2010
![Page 37: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/37.jpg)
Try: One assertion per test
37
Monday, 15 November 2010
![Page 38: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/38.jpg)
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); }}
38
Monday, 15 November 2010
![Page 39: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/39.jpg)
At least: One concept per test
39
Monday, 15 November 2010
![Page 40: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/40.jpg)
Hamcrest
• Framework for writing declarative match criteria
40http://code.google.com/p/hamcrest/
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));
assertThat(s, containsString("bananas"));assertThat(s, not(containsString("mangoes"));
Or even better
Monday, 15 November 2010
![Page 41: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/41.jpg)
Meaningful Assertion Messages
41
• 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
Monday, 15 November 2010
![Page 42: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/42.jpg)
Itʼs Design Smell!!!
42
Monday, 15 November 2010
![Page 43: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/43.jpg)
Extra Constructor
43
public class LogFileMerge { private URL logFileA, logFileB;
public LogFileMerge() { this(new URL("http://server1/system.log"), new URL("http://server2/system.log")); }
LogFileMerge(URL a, URL b) { this.logFileA = a; this.logFileB = b; }}
Monday, 15 November 2010
![Page 44: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/44.jpg)
Test-Specific SubClass
44
public class CreditCardProcessing {
public boolean isValid(String cardnumber) { return validationCodeMatches(cardnumber) && cardIsActive(cardnumber); }
protected boolean validationCodeMatches(String cardnumber) { // validation logic omitted for brevity... }
protected boolean cardIsActive(String cardnumber) { // access to merchant system's web service // omitted for brevity... }}
Monday, 15 November 2010
![Page 45: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/45.jpg)
Still not testable?
45
• Do you follow good design principles?
Monday, 15 November 2010
![Page 46: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/46.jpg)
Thinking
46
Test code is not second class citizen
Good design principles apply:
• Responsibility
• Dependency
• Low Coupling
• High Cohesion
• Indirection
• Protected Variations
Watch out for organisational dysfunction!
Monday, 15 November 2010
![Page 47: Unbearable Test Code Smell](https://reader034.vdocuments.net/reader034/viewer/2022042606/5482b758b4af9fa00d8b487f/html5/thumbnails/47.jpg)
References• Practical TDD and ATDD for Java Developers - Lasse Koskela
• Growing OO Software, guided by tests - Steve Freeman
• xUnit Test Patterns - Gerard Meszaros
47
Steven [email protected]: stevenmak
Monday, 15 November 2010