David Völkel, @davidvoelkel 
Clean Test Code 
Agile Testing Days 11.11.2014
2 
@davidvoelkel 
Dev & Consultant 
Software Craftsman 
Test-Driven Development 
Software Design 
@softwerkskammer
Dirty Test Code 
technical debt 
velocity decrease 
costs 
latency 
agile
Clean Tests 
4 attributes reliable readable redundancy-free focused Conclusion Q&A
xUnit- Patterns 
BDD 
Functional tests 
setup 
arrange 
given 
Input rows 
execute 
act 
when 
“Fixture“ 
verify 
assert 
then 
Output rows 
[teardown] 
[annihilate] 
Test Phases 
Input 
Output
Reliable 
Mind complexity, so avoid 
•boolean assertions 
assertTrue(age >= 18); 
assertThat(age, greaterThan(17))
Reliable 
Mind complexity, so avoid 
•boolean assertions 
•conditionals & complex loops 
boolean containsName = false; 
for (String name: names) { 
if (name.equals("David")) { 
containsName = true; 
} 
} 
assertTrue(containsName); 
assertThat(names, hasItem("David"));
Readable 
Read >> write 
Executable specifications 
Living documentation
Abstraction 
avoid irrelevant details
Test Method Body 
Gerard Meszaros: 
„When it is not important for something to be seen in the test method, it is important that it not be seen in the test method! “
“In the order form the user should be able to finish the order process by pressing the cancel button”
@Test public void cancelProcess_inOrderForm() { 
server.runningProcesses(0); 
ProcessStatus process = server.startProcess(Process.ORDER); 
processInstanceId = process.getInstanceId(); 
server.runningProcesses(1); 
server.expectUserForm(processInstanceId, ORDER_FORM); 
Form form = new Form(); 
form.setOrderDate("01.01.2015"); 
form.setStandardDelivery(false); 
String taskId = server.taskIdFor(processInstanceId); 
controller.submit(form, 
Mockito.mock(BindingResult.class), null, 
taskId, null, CANCEL); 
server.runningProcesses(0); 
} 
“In the order form the user should be able to finish the order process by pressing the cancel button”
@Test public void cancelProcess_inOrderForm() { 
server.runningProcesses(0); 
ProcessStatus process = server.startProcess(Process.ORDER); 
processInstanceId = process.getInstanceId(); 
server.runningProcesses(1); 
server.expectUserForm(processInstanceId, ORDER_FORM); 
Form form = new Form(); 
form.setOrderDate("01.01.2015"); 
form.setStandardDelivery(false); 
String taskId = server.taskIdFor(processInstanceId); 
controller.submit(form, 
Mockito.mock(BindingResult.class), null, 
taskId, null, CANCEL); 
server.runningProcesses(0); 
} 
“In the order form the user should be able to finish the order process by pressing the cancel button” 
„Testscript“ 
“Incidental Details”
@Test public void cancelProcess_inOrderForm() { 
server.runningProcesses(0); 
ProcessStatus process = server.stareProcess(Process.ORDER); 
processInstanceId = process.getInstanceId(); 
server.runningProcesses(1); 
server.expectUserForm(processInstanceId, ORDER_FORM); 
Form form = new Form(); 
form.setOrderDate("01.01.2015"); 
form.setStandardDelivery(false); 
String taskId = server.taskIdFor(processInstanceId); 
controller.submit(form, 
Mockito.mock(BindingResult.class), null, 
taskId, null, CANCEL); 
server.runningProcesses(0); 
} 
FORM 
CANCEL 
Signal-Noise-Ratio? 
Single Level of Abstraction? 
“In the order form the user should be able to finish the order process by pressing the cancel button”
@Test public void cancelProcess_inOrderForm() { 
inForm(ORDER_FORM); 
submitFormWithButton(CANCEL); 
noProcessRunning(); 
} 
“In the order form the user should be able to finish the order process by pressing the cancel button”
Names
Test class names 
public class AnOrderProcess { 
OrderProcess process; 
@Before public void createOrderProcess() { 
process = new OrderProcess(); 
} 
object under test
public class AnOrderProcess { 
OrderProcess process; 
@Before public void createOrderProcess() { 
process = new OrderProcess(); 
} 
@Test public void inOrderForm_cancel() { 
process.setState(ORDER_FORM); 
process.submit(CANCEL_BUTTON); 
assertThat("process canceled", process.isCanceled(), equalTo(true)); 
} 
Test method names 
object under test
public class AnOrderProcess { 
OrderProcess process; 
@Before public void createOrderProcess() { 
process = new OrderProcess(); 
} 
@Test public void inOrderForm_cancel() { 
process.setState(ORDER_FORM); 
process.submit(CANCEL_BUTTON); 
assertThat("process canceled", process.isCanceled(), equalTo(true)); 
} 
Test method names 
object under test 
setup
@Before public void createOrderProcess() { 
process = new OrderProcess(); 
} 
@Test public void inOrderForm_cancel() { 
process.setState(ORDER_FORM); 
process.submit(CANCEL_BUTTON); 
assertThat("process canceled", process.isCanceled(), equalTo(true)); 
} 
Test method names 
setup 
execute
@Before public void createOrderProcess() { 
process = new OrderProcess(); 
} 
@Test public void inOrderForm_cancel() { 
process.setState(ORDER_FORM); 
process.submit(CANCEL_BUTTON); 
assertThat("process canceled", process.isCanceled(), equalTo(true)); 
} 
Test method names 
setup 
execute 
verify
Audience? 
test maintainer 
search test failure cause 
business
Communicate 
comprehensively 
OuT + setup + execute + verify 
Distribute: Class, method name, verify
Redundancy-free
Hierarchical tests 
public class AnOrderProcess { 
@Before public void createOrderProcess() { 
process = new OrderProcess(); 
} 
public class InOrderForm { 
@Before public void inOrderForm() { 
process.setState(ORDER_FORM); 
} 
@Test public void whenCanceled_processIsStopped() { 
process.submit(CANCEL_BUTTON); 
assertThat("process stopped", process.isStopped(), equalTo(true)); 
} 
@Test public void whenBought_orderIsConfirmed() { 
process.submit(BUY_NOW_BUTTON); 
assertThat("state", process.getState(), equalTo(ORDER_CONFIRMED)); 
} 
} 
… 
extract common 
setup
OuT 
Hierarchical tests 
setup 
execute 
verify
Helper methods 
Object Mother 
Test Data Builder 
aCustomer("David"); 
CustomerTest.aCustomer("David"); 
CustomerMother.aCustomer("David"); 
CustomerMother.aCustomer("David", „Völkel"); 
CustomerMother.aCustomer(null, „Völkel", null, null, null, null, birthday); 
aCustomer().withLastName("Völkel") 
.withBirthDay(birthDay) 
.build(); 
Common test data
void assertMoneyEquals(Money expected, Money actual) { 
assertEquals("currency", expected.getCurrency(),actual.getCurrency()); 
assertEquals("amount", expected.getAmount(), actual.getAmount()); 
} 
Matcher<Money> equalTo(Money money) { 
return allOf( 
property(money, Money::getCurrency, "currency"), 
property(money, Money::getAmount, "amount")); 
} 
Custom asserts via helper methods 
Composed Hamcrest Matchers 
Common verify
"Single Concept per Test" 
"One Execute per Test" 
"One Assert per Test" 
Focused
State vs. testability 
Focused
„Listen to your tests!“ 
Hard-to-test Code
Clean Tests 
1.reliable 
2.readable 
3.redundancy-free 
4.focussed
Clean Test Code, 
stay agile!
Resources 
Books 
„Clean Code“, Robert C. Martin 
„xUnit Test Patterns“, Gerard Meszaros 
„Effective Unittesting“, Lasse Koskela 
„Growing Object Oriented Software“, Steve Freeman, Nat Pryce 
„Specification by Example”, Gojko Adzic 
Videos 
Episodes 20-22 http://cleancoders.com/, 
Robert C. Martin
David Völkel 
codecentric AG 
Twitter: @davidvoelkel 
david.voelkel@codecentric.de 
www.codecentric.de 
blog.codecentric.de 
Q&A 
35
Creative Commons ShareAlike 4.0 
The images used are public domain and can be found on Wikipedia. 
License 
36

Clean Test Code

  • 1.
    David Völkel, @davidvoelkel Clean Test Code Agile Testing Days 11.11.2014
  • 2.
    2 @davidvoelkel Dev& Consultant Software Craftsman Test-Driven Development Software Design @softwerkskammer
  • 3.
    Dirty Test Code technical debt velocity decrease costs latency agile
  • 4.
    Clean Tests 4attributes reliable readable redundancy-free focused Conclusion Q&A
  • 5.
    xUnit- Patterns BDD Functional tests setup arrange given Input rows execute act when “Fixture“ verify assert then Output rows [teardown] [annihilate] Test Phases Input Output
  • 6.
    Reliable Mind complexity,so avoid •boolean assertions assertTrue(age >= 18); assertThat(age, greaterThan(17))
  • 7.
    Reliable Mind complexity,so avoid •boolean assertions •conditionals & complex loops boolean containsName = false; for (String name: names) { if (name.equals("David")) { containsName = true; } } assertTrue(containsName); assertThat(names, hasItem("David"));
  • 8.
    Readable Read >>write Executable specifications Living documentation
  • 9.
  • 10.
    Test Method Body Gerard Meszaros: „When it is not important for something to be seen in the test method, it is important that it not be seen in the test method! “
  • 11.
    “In the orderform the user should be able to finish the order process by pressing the cancel button”
  • 12.
    @Test public voidcancelProcess_inOrderForm() { server.runningProcesses(0); ProcessStatus process = server.startProcess(Process.ORDER); processInstanceId = process.getInstanceId(); server.runningProcesses(1); server.expectUserForm(processInstanceId, ORDER_FORM); Form form = new Form(); form.setOrderDate("01.01.2015"); form.setStandardDelivery(false); String taskId = server.taskIdFor(processInstanceId); controller.submit(form, Mockito.mock(BindingResult.class), null, taskId, null, CANCEL); server.runningProcesses(0); } “In the order form the user should be able to finish the order process by pressing the cancel button”
  • 13.
    @Test public voidcancelProcess_inOrderForm() { server.runningProcesses(0); ProcessStatus process = server.startProcess(Process.ORDER); processInstanceId = process.getInstanceId(); server.runningProcesses(1); server.expectUserForm(processInstanceId, ORDER_FORM); Form form = new Form(); form.setOrderDate("01.01.2015"); form.setStandardDelivery(false); String taskId = server.taskIdFor(processInstanceId); controller.submit(form, Mockito.mock(BindingResult.class), null, taskId, null, CANCEL); server.runningProcesses(0); } “In the order form the user should be able to finish the order process by pressing the cancel button” „Testscript“ “Incidental Details”
  • 14.
    @Test public voidcancelProcess_inOrderForm() { server.runningProcesses(0); ProcessStatus process = server.stareProcess(Process.ORDER); processInstanceId = process.getInstanceId(); server.runningProcesses(1); server.expectUserForm(processInstanceId, ORDER_FORM); Form form = new Form(); form.setOrderDate("01.01.2015"); form.setStandardDelivery(false); String taskId = server.taskIdFor(processInstanceId); controller.submit(form, Mockito.mock(BindingResult.class), null, taskId, null, CANCEL); server.runningProcesses(0); } FORM CANCEL Signal-Noise-Ratio? Single Level of Abstraction? “In the order form the user should be able to finish the order process by pressing the cancel button”
  • 15.
    @Test public voidcancelProcess_inOrderForm() { inForm(ORDER_FORM); submitFormWithButton(CANCEL); noProcessRunning(); } “In the order form the user should be able to finish the order process by pressing the cancel button”
  • 16.
  • 17.
    Test class names public class AnOrderProcess { OrderProcess process; @Before public void createOrderProcess() { process = new OrderProcess(); } object under test
  • 18.
    public class AnOrderProcess{ OrderProcess process; @Before public void createOrderProcess() { process = new OrderProcess(); } @Test public void inOrderForm_cancel() { process.setState(ORDER_FORM); process.submit(CANCEL_BUTTON); assertThat("process canceled", process.isCanceled(), equalTo(true)); } Test method names object under test
  • 19.
    public class AnOrderProcess{ OrderProcess process; @Before public void createOrderProcess() { process = new OrderProcess(); } @Test public void inOrderForm_cancel() { process.setState(ORDER_FORM); process.submit(CANCEL_BUTTON); assertThat("process canceled", process.isCanceled(), equalTo(true)); } Test method names object under test setup
  • 20.
    @Before public voidcreateOrderProcess() { process = new OrderProcess(); } @Test public void inOrderForm_cancel() { process.setState(ORDER_FORM); process.submit(CANCEL_BUTTON); assertThat("process canceled", process.isCanceled(), equalTo(true)); } Test method names setup execute
  • 21.
    @Before public voidcreateOrderProcess() { process = new OrderProcess(); } @Test public void inOrderForm_cancel() { process.setState(ORDER_FORM); process.submit(CANCEL_BUTTON); assertThat("process canceled", process.isCanceled(), equalTo(true)); } Test method names setup execute verify
  • 22.
    Audience? test maintainer search test failure cause business
  • 23.
    Communicate comprehensively OuT+ setup + execute + verify Distribute: Class, method name, verify
  • 24.
  • 25.
    Hierarchical tests publicclass AnOrderProcess { @Before public void createOrderProcess() { process = new OrderProcess(); } public class InOrderForm { @Before public void inOrderForm() { process.setState(ORDER_FORM); } @Test public void whenCanceled_processIsStopped() { process.submit(CANCEL_BUTTON); assertThat("process stopped", process.isStopped(), equalTo(true)); } @Test public void whenBought_orderIsConfirmed() { process.submit(BUY_NOW_BUTTON); assertThat("state", process.getState(), equalTo(ORDER_CONFIRMED)); } } … extract common setup
  • 26.
    OuT Hierarchical tests setup execute verify
  • 27.
    Helper methods ObjectMother Test Data Builder aCustomer("David"); CustomerTest.aCustomer("David"); CustomerMother.aCustomer("David"); CustomerMother.aCustomer("David", „Völkel"); CustomerMother.aCustomer(null, „Völkel", null, null, null, null, birthday); aCustomer().withLastName("Völkel") .withBirthDay(birthDay) .build(); Common test data
  • 28.
    void assertMoneyEquals(Money expected,Money actual) { assertEquals("currency", expected.getCurrency(),actual.getCurrency()); assertEquals("amount", expected.getAmount(), actual.getAmount()); } Matcher<Money> equalTo(Money money) { return allOf( property(money, Money::getCurrency, "currency"), property(money, Money::getAmount, "amount")); } Custom asserts via helper methods Composed Hamcrest Matchers Common verify
  • 29.
    "Single Concept perTest" "One Execute per Test" "One Assert per Test" Focused
  • 30.
  • 31.
    „Listen to yourtests!“ Hard-to-test Code
  • 32.
    Clean Tests 1.reliable 2.readable 3.redundancy-free 4.focussed
  • 33.
    Clean Test Code, stay agile!
  • 34.
    Resources Books „CleanCode“, Robert C. Martin „xUnit Test Patterns“, Gerard Meszaros „Effective Unittesting“, Lasse Koskela „Growing Object Oriented Software“, Steve Freeman, Nat Pryce „Specification by Example”, Gojko Adzic Videos Episodes 20-22 http://cleancoders.com/, Robert C. Martin
  • 35.
    David Völkel codecentricAG Twitter: @davidvoelkel david.voelkel@codecentric.de www.codecentric.de blog.codecentric.de Q&A 35
  • 36.
    Creative Commons ShareAlike4.0 The images used are public domain and can be found on Wikipedia. License 36