clean test code
TRANSCRIPT
![Page 1: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/1.jpg)
David Völkel, @davidvoelkel
Clean Test Code Agile Testing Days 11.11.2014
![Page 2: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/2.jpg)
2
@davidvoelkel
Dev & Consultant
Software Craftsman
Test-Driven Development Software Design @softwerkskammer
![Page 3: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/3.jpg)
Dirty Test Code
technical debt
velocity decrease
costs
latency agile
![Page 4: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/4.jpg)
Clean Tests 4 attributes
reliable
readable
redundancy-free
focused
Conclusion
Q&A
![Page 5: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/5.jpg)
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
![Page 6: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/6.jpg)
Reliable Mind complexity, so avoid
• boolean assertions
assertTrue(age >= 18);
assertThat(age, greaterThan(17))
![Page 7: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/7.jpg)
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"));
![Page 8: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/8.jpg)
Readable Read >> write Executable specifications Living documentation
![Page 9: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/9.jpg)
Abstraction avoid irrelevant details
![Page 10: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/10.jpg)
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! “
![Page 11: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/11.jpg)
“In the order form the user should be able to finish the order process by pressing the cancel button”
![Page 12: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/12.jpg)
@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”
![Page 13: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/13.jpg)
@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”
![Page 14: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/14.jpg)
@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);
}
ORDER_FORM
CANCEL
runningProcesses(0);
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”
![Page 15: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/15.jpg)
@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”
![Page 16: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/16.jpg)
Names
![Page 17: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/17.jpg)
Test class names public class AnOrderProcess {
OrderProcess process;
@Before public void createOrderProcess() {
process = new OrderProcess();
}
object under test
![Page 18: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/18.jpg)
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
![Page 19: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/19.jpg)
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
![Page 20: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/20.jpg)
@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
![Page 21: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/21.jpg)
@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
![Page 22: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/22.jpg)
Audience? test maintainer search test failure cause business
![Page 23: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/23.jpg)
Communicate
comprehensively OuT + setup + execute + verify Distribute: Class, method name, verify
![Page 24: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/24.jpg)
Redundancy-free
![Page 25: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/25.jpg)
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
![Page 26: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/26.jpg)
OuT
Hierarchical tests
setup
execute verify
![Page 27: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/27.jpg)
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
![Page 28: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/28.jpg)
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
![Page 29: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/29.jpg)
"Single Concept per Test" "One Execute per Test" "One Assert per Test"
Focused
![Page 30: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/30.jpg)
State vs. testability
Focused
![Page 31: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/31.jpg)
„Listen to your tests!“
Hard-to-test Code
![Page 32: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/32.jpg)
Clean Tests 1. reliable 2. readable 3. redundancy-free 4. focussed
![Page 33: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/33.jpg)
Clean Test Code, stay agile!
![Page 34: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/34.jpg)
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
![Page 35: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/35.jpg)
David Völkel
codecentric AG
Twitter: @davidvoelkel
www.codecentric.de
blog.codecentric.de
Q&A
35
![Page 36: Clean Test Code](https://reader034.vdocuments.us/reader034/viewer/2022052311/55a15ce21a28ab556a8b4614/html5/thumbnails/36.jpg)
Creative Commons ShareAlike 4.0
The images used are public domain and can be found on Wikipedia.
License
36