1. We are going to add a new feature to the OwnerController class (that is specified as follows):
/** * @GetMapping("/owners") public String processFindForm(Owner owner, ...) * Look up the owner in the database by the given last name. * If a single owner is found, redirect to /owners/{ownerId}. * If several owners are found, allow selection of an owner in owners/ownersList. */
TDD requires us to write the tests before implementing the functionality. So, we start by manually implementing tests for this new processFindForm method.
2. We first have to set up the test class wiring the required beans:
6. We then run the manually written tests to check whether our implementation is correct. These tests succeed. So, are we finished? Let's use the Diffblue Cover plugin for IntelliJ to see what it produces for us.
7. We receive a couple of tests. Let’s review them:
This first test shows us behaviour for the case where no owner is found. That’s a case we haven’t considered in the requirements and therefore missed it when we wrote our manual tests. Thus our implementation might also be incorrect in that situation.
8. We have to amend our requirements to make them complete, i.e. we need to define the behaviour for the case where no owner is found. In this case we don’t want to show the owner selection list, but instead redirect back to the find owners page and show an error:
/** * @GetMapping("/owners") public String processFindForm(Owner owner, …) * If no owner is found, return to owners/findOwners. * Look up the owner in the database by the given last name. * If a single owner is found, redirect to /owners/{ownerId}. * If several owners are found, allow selection of an owner in owners/ownersList. */
9. We also need to modify the test to make it match with this additional requirement:
12. In this test we search for owners without actually specifying a last name. What should happen in this case? It’s not specified in our requirements. Again, we need to fill this gap in the requirements:
/** * @GetMapping("/owners") public String processFindForm(Owner owner, …) * If no owner is found, return to owners/findOwners. * Look up the owner in the database by the given last name. * If a single owner is found, redirect to /owners/{ownerId}. * If several owners are found, allow selection of an owner in owners/ownersList.* * If no last name is given, allow selection among all owners. */
The above test is correct as there is only a single owner returned in our mock. We could add another test that returns multiple owners and redirects to owners/ownersList to cover that case too.
13. To complete the implementation, we make the following change:
publicclassOwnerController {…@GetMapping("/owners")publicStringprocessFindForm(Owner owner,BindingResult result,Map<String,Object> model) {// allow parameterless GET request for /owners to return all recordsif (owner.getLastName() ==null) {owner.setLastName(""); // empty string signifies broadest possible search }Collection<Owner> results =this.owners.findByLastName(owner.getLastName());if (results.isEmpty()) {result.rejectValue("lastName","notFound","not found");return"owners/findOwners"; } elseif (results.size() ==1) { owner =results.iterator().next();return"redirect:/owners/"+owner.getId(); } else {model.put("selections", results);return"owners/ownersList"; }}
14. All the tests should pass now and cover the entire implementation.
The diagram below shows our journey through this tutorial:
Summary
In this use case, we started the TDD process as usual by analyzing the requirements and implementing the unit tests. Then we implemented the feature to make the unit tests pass. At this point we would usually stop and commit the code. The value add that Diffblue Cover Plugin for IntelliJ provides for us is that it gives us additional tests that might include cases that we haven’t considered in our initial requirements analysis. That means that Diffblue Cover helps us identify gaps in the requirements. We did that by reviewing the tests Diffblue Cover created for us, amending the requirements with the missing cases and adapting the unit tests to match these unforeseen requirements. We then completed the implementation to make the unit tests pass.