As well as being useful and correct, an effective test should be readable. An unreadable test is a test that gives no one confidence. Even if it is correct and useful, no one can tell.
What are these tests supposed to do?
public void testSomething4() {
dataSetup(5, 17, new Account());
verifyExpectations("ID17", "17.0", "22.0", null);
}
public void testSomething5() {
Account acct = new Account();
acct.setOverdueMonths(3);
dataSetup(5, 17, acct);
verifyExpectations("ID17", "17.0", "22.0", "Overdue 90 days");
}
private void dataSetup(int i, int j, Displayable acct) {
acct.setId("ID" + j);
acct.setBalance(j);
acct.setNewCharges(i);
widget.addDisplayLine(acct);
}
private void verifyExpectations(String id, String balance, String total, String errors) {
DisplayLine line = widget.getDisplayLine(id);
assertEquals(balance, line.getPreviousBalance());
assertEquals(total, line.getCurrentBalance());
assertEquals(errors, line.getErrorMessage());
}
Each of two tests sets values on an account, and adds that account to some widget. Unfortunately, however, the helper methods dataSetup() and verifyExpectations() obscure the tests rather than clarifying. The reader will need to build an understanding of what these methods do before she can determine whether the test is needed and correct.
Ideally, a reader should be able to understand what is being tested and how by looking solely at the test method. In tests, we can tolerate a bit more redundancy if it improves clarity.
public void testDisplayLines() {
Account acct = new Account();
acct.setId("ID17");
acct.setBalance(17);
acct.setNewCharges(5);
widget.addDisplayLines(acct);
DisplayLine line = widget.getDisplayLine("ID17");
assertEquals("17.0", line.getPreviousBalance());
assertEquals("22.0", line.getCurrentBalance());
assertEquals("Overdue 90 days", line.getErrorMessage());
}
public void testDisplayLinesWithOverdueAccount() {
Account acct = new Account();
acct.setOverdueMonths(3);
acct.setId("ID17");
acct.setBalance(17);
acct.setNewCharges(5);
widget.addDisplayLines(acct);
DisplayLine line = widget.getDisplayLine("ID17");
assertEquals("17.0", line.getPreviousBalance());
assertEquals("22.0", line.getCurrentBalance());
assertEquals(null, line.getErrorMessage());
}
The test methods have been given meaningful names. They have been refactored to expose the basic progression of any good test: set-up, exercise the code under test, and then assertions that test its correctness. In fact, we have formatted each test to visually separate each phase. Now we set the account id with a constant string, rather than “computing” it from the balance.
With this change, there is more redundancy, but we’ve only increased the line count by 3. By formatting the redundant parts identically, it is relatively easy to visually “diff” the two tests to understand how the two similar tests differ.
There are ways we can make these tests ever more readable, but that’s a topic for another post…
[...] @ 9:00 am Tags: builder pattern, java, junit, tdd, test data, testing A specific instance of writing tests to be read is to use a builder to create the test [...]
Pingback by Consider a Builder for Test Data « Effective Java Testing — December 14, 2008 @ 9:42 pm