Software Development Magazine - Project Management, Programming, Software Testing |
Scrum Expert - Articles, tools, videos, news and other resources on Agile, Scrum and Kanban |
Agile Development with ICONIX Process - Page 3
Doug Rosenberg, Matt Stephens and Mark Collins-Cope
Introduction
In the class diagram in Figure 4, we've indicated that CustomerDetailsValidator is a <<control>> stereotype. This isn't essential for a class diagram, but it does help to tag the control classes so that we can tell at a glance which ones have (or require) unit tests.
Next, we want to write the actual test methods. Remember, these are being driven by the controllers, but they are written from the perspective of the Boundary objects and in a sense are directly validating the design we've created using the sequence diagram, before we get to the "real" coding stage. In the course of writing the test methods, we may identify further operations that might have been missed during sequence diagramming.
Our first stab at the testCheckRequiredFields() method looks like this:
public voidtestCheckRequiredFields() throws Exception {
List fields = new ArrayList();
Customer customer = new Customer (fields);
boolean allFieldsPresent = customer.checkRequiredFields();
assertTrue("All required fields should be present",
allFieldsPresent);
}
Naturally enough, trying to compile this initially fails, because we don't yet have a CustomerDetailsValidator class (let alone a checkRequiredFields() method). These are easy enough to add, though:
public class CustomerDetailsValidator {
public CustomerDetailsValidator (List fields) {
}
public boolean checkRequiredFields() {
return false; // make the test fail initially.
}
}
Let's now compile and run the test. Understandably, we get a failure, because checkRequiredFields() is returning false (indicating that the fields didn't contain all the required fields):
CustomerDetailsValidatorTest
.F.
Time: 0.016
There was 1 failure:
1) testCheckRequiredFields(CustomerDetailsValidatorTest)
junit.framework.AssertionFailedError:
All required fields should be present
at CustomerDetailsValidatorTest.testCheckRequiredFields(
CustomerDetailsValidatorTest.java:21)
FAILURES!!!
Tests run: 2, Failures: 1, Errors: 0
However, where did this ArrayList of fields come from, and what should it contain? In the testCheckRequiredFields() method, we've created it as a blank ArrayList, but it has spontaneously sprung into existence-an instant warning sign that we must have skipped a design step. Checking back, this happened because we didn't properly address the question of what the Customer fields are (and how they're created) in the sequence diagram (see Figure 3). Let's hit the brakes and sort that out right now (see Figure 5).
Figure 5. Revisiting the sequence diagram to add more detail
Revisiting the sequence diagram identified that we really need a Map (a list of name/value pairs that can be looked up individually by name) and not a sequential List.
Now that we've averted that potential design mishap, let's get back to the CustomerDetailsValidator test. As you may recall, the test was failing, so let's add some code to test for our required fields:
public voidtestCheckRequiredFields() throws Exception {
Map fields = new HashMap();
fields.put("userName", "bob");
fields.put("firstName", "Robert");
fields.put("lastName", "Smith");
Customer customer = new Customer(fields);
boolean allFieldsPresent = customer.checkRequiredFields();
assertTrue("All required fields should be present",
allFieldsPresent);
}
A quick run-through of this test shows that it's still failing (as we'd expect). So now let's add something to CustomerDetailsValidator to make the test pass:
public classCustomerDetailsValidator {
private Map fields;
public CustomerDetailsValidator (Map fields)
{
this.fields = fields;
}
public boolean checkRequiredFields() {
return fields.containsKey("userName") &&
fields.containsKey("firstName") &&
fields.containsKey("lastName");
}
}
Let's now feed this through our voracious unit tester:
CustomerDetailsValidatorTest
..
Time: 0.016
OK (2 tests)
The tests passed!
Summing Up
Hopefully this article gave you a taster of what's involved in combining a code-centric, unit test-driven design methodology (TDD) with an UML-based, use case-driven methodology (ICONIX Process). In Agile Development with ICONIX Process, we take this example further, showing how to strengthen the tests and the use cases by adding controllers for form validation, and by writing unit tests for each of the alternative courses ("rainy day scenarios") in the use cases.
References
Agile Development with ICONIX Process: People, Process, and Pragmatism
by Doug Rosenberg, Mark Collins-Cope, Matt Stephens, Publisher: Apress, ISBN: 1590594649
Related Methods & Tools articles
More Agile and Unified Modeling Language UML Knowledge
Page 2 Back to the archive list
This article was originally published in the Spring 2005 issue of Methods & Tools
Methods & Tools Testmatick.com Software Testing Magazine The Scrum Expert |