Clean Test Code

36
David Völkel, @davidvoelkel Clean Test Code Agile Testing Days 11.11.2014

Transcript of Clean Test Code

Page 1: Clean Test Code

David Völkel, @davidvoelkel

Clean Test Code Agile Testing Days 11.11.2014

Page 2: Clean Test Code

2

@davidvoelkel

Dev & Consultant

Software Craftsman

Test-Driven Development Software Design @softwerkskammer

Page 3: Clean Test Code

Dirty Test Code

technical debt

velocity decrease

costs

latency agile

Page 4: Clean Test Code

Clean Tests 4 attributes

reliable

readable

redundancy-free

focused

Conclusion

Q&A

Page 5: Clean Test Code

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

Reliable Mind complexity, so avoid

• boolean assertions

assertTrue(age >= 18);

assertThat(age, greaterThan(17))

Page 7: Clean Test Code

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

Readable Read >> write Executable specifications Living documentation

Page 9: Clean Test Code

Abstraction avoid irrelevant details

Page 10: Clean Test Code

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

“In the order form the user should be able to finish the order process by pressing the cancel button”

Page 12: Clean Test Code

@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

@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

@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

@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

Names

Page 17: Clean Test Code

Test class names public class AnOrderProcess {

OrderProcess process;

@Before public void createOrderProcess() {

process = new OrderProcess();

}

object under test

Page 18: Clean Test Code

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

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

@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

@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

Audience? test maintainer search test failure cause business

Page 23: Clean Test Code

Communicate

comprehensively OuT + setup + execute + verify Distribute: Class, method name, verify

Page 24: Clean Test Code

Redundancy-free

Page 25: Clean Test Code

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

OuT

Hierarchical tests

setup

execute verify

Page 27: Clean Test Code

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

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

"Single Concept per Test" "One Execute per Test" "One Assert per Test"

Focused

Page 30: Clean Test Code

State vs. testability

Focused

Page 31: Clean Test Code

„Listen to your tests!“

Hard-to-test Code

Page 32: Clean Test Code

Clean Tests 1. reliable 2. readable 3. redundancy-free 4. focussed

Page 33: Clean Test Code

Clean Test Code, stay agile!

Page 34: Clean Test Code

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

David Völkel

codecentric AG

Twitter: @davidvoelkel

[email protected]

www.codecentric.de

blog.codecentric.de

Q&A

35