+ All Categories
Home > Design > Unbearable Test Code Smell

Unbearable Test Code Smell

Date post: 05-Dec-2014
Category:
Upload: steven-mak
View: 1,440 times
Download: 0 times
Share this document with a friend
Description:
 
47
Unbearable Test Smells Steven Mak [email protected] www.odd-e.com twitter: stevenmak 1 Monday, 15 November 2010
Transcript
Page 1: Unbearable Test Code Smell

Unbearable Test Smells

Steven [email protected]: stevenmak

1

Monday, 15 November 2010

Page 2: Unbearable Test Code Smell

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

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

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

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

What is fixture?TEST_GROUP (TEST_thisObject){

void setup() {}void teardown() {}

};

6

Monday, 15 November 2010

Page 7: Unbearable Test Code Smell

Duplication causing fragile tests

Where is the duplication?EXPECT_LOG(“ABC error”);

7

Monday, 15 November 2010

Page 8: Unbearable Test Code Smell

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

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

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

Tests donʼt have assertionsTEST(TEST_GROUP, TEST_THIS){

runThisFunctionLaLaLa();}

11

Monday, 15 November 2010

Page 12: Unbearable Test Code Smell

12

What does it mean by 80% Unit Test Coverage?

Monday, 15 November 2010

Page 13: Unbearable Test Code Smell

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

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

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

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

17

Do you know your xUnit harness?

Monday, 15 November 2010

Page 18: Unbearable Test Code Smell

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

19

Some xUnit harness

Java: JUnit

.Net: NUnit

C/C++: CppUTest

PHP: PHPUnit

Monday, 15 November 2010

Page 20: Unbearable Test Code Smell

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

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

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

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

Tests that crash 50% of the time?!!

24

Monday, 15 November 2010

Page 25: Unbearable Test Code Smell

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

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

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

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

What can we do?

29

Monday, 15 November 2010

Page 30: Unbearable Test Code Smell

Try: one test group per fileBut why canʼt? is it because of... ?

30

Monday, 15 November 2010

Page 31: Unbearable Test Code Smell

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

Dont forget fixtures

TEST_GROUP (TEST_thisObject){ void setup() { }

void teardown() { }};

32

Monday, 15 November 2010

Page 33: Unbearable Test Code Smell

Test Data Buildereth_data_buf->setControl(2)->withParameterA(3)->build();

33

Monday, 15 November 2010

Page 34: Unbearable Test Code Smell

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

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

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

Try: One assertion per test

37

Monday, 15 November 2010

Page 38: Unbearable Test Code Smell

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

At least: One concept per test

39

Monday, 15 November 2010

Page 40: Unbearable Test Code Smell

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

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

Itʼs Design Smell!!!

42

Monday, 15 November 2010

Page 43: Unbearable Test Code Smell

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

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

Still not testable?

45

• Do you follow good design principles?

Monday, 15 November 2010

Page 46: Unbearable Test Code Smell

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

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


Recommended