Integration testing: Validation and relationships

In my previous two posts about integration testing with jetty [1][2], I have showed how to test a simple web application with Jetty and how to expand this application with a fake DAO implementation to test display and updates of data through the web interface. In this penultimate post, I will describe how to work with more advanced edit scenarios.

Most web applications have some degree of requirements for validation of input and a bit more advanced data structure than the one we’ve worked with so far. I will expand the example to show how to do validation using Spring-MVC and how to handle many-to-one relationships between domain objects.

Validating input

Let’s start with something simple: A name cannot contain certain characters. Here is a unit test:

Running the test should produce a red bar. In order for this to work, we have to do something to validate. The simplest thing I can think of is to modify Category.setName:

At this point in time, everything works like we want it to. Spring-MVC catches the exception and displays the exception code in the corresponding @spring.showErrors tag in the view. Here’s the relevant fragment of the FreeMarker template for edit.ftl: <p>Name: <@spring.formInput "category.name" /> <@spring.showErrors "<br>"/></p>.

More validation

Let’s do another validation example: Let’s say that the name must be unique. Here is a test for that:

This is hard to test in Category.setName, as we need to check what other names are out there. Enter Spring-MVC validators. If a SimpleFormController has a validator, this object gets to set validation errors before Spring-MVC calls the submit action. If the any errors are set, the submit action is not called. Instead the user will be taken back to the form page. This is just perfect for what we’re doing. Since we always want the same validator in CategoryController, I call setValidator explicitly in the constructor. This could also have been done from Spring-XML. (But I find that making configurable that which we will never configure is usually counter productive)

And the validation method itself:

At this point, the test passes, and we’re happy. But wait, there is something lurking in the shadows….

Testing by “click-like-hell”

At this point in time, things are going a bit too well. This should always be a warning sign that we’re forgetting something. To validate what’s going on, it’s often a good idea to create a main-class to start testing your application. The maven-jetty-plugin can also be used, but the advantage of creating a main-class is that this way, we can debug natively in the IDE. Here is my application starter:

If you run this application, go to http://localhost:9080/category/index.html and click around a little, you will notice something strange: When we enter a duplicate name, even though we get the validation error, the Category is updated. Why does this happen?

Before we fix the problem, let’s update testDuplicateNamesAreIllegal to reproduces it:

The fake DAO is a bit too fake

The problem with using fakes is that they can be too fake. In our case, the fake DAO doesn’t preserve the semantics of the “update” method. We are storing all objects in a table, and when “get” is called, we share this object in the controller and DAO. Even if update() is never called, any changes made to the returned Category will be reflected in the DAO. At this point in time, it makes sense to start writing some tests for the DAO. This is a suspicious situation: We are testing code that was written with the purpose of helping in testing! CategoryDAOTest should better be something we can use again when we replace the fake with a real DAO implementation.

Here is a first test that will fail:

To make it pass, simply make FakeCategoryDAO.get() return a clone of the Category. (Hint: override clone in Category with “public Category clone();” and catch CloneNotSupportedException in this method)

Redirect-on-POST

This will make the integration test pass again, and if you check the web UI, things are handled correctly now. The lesson is worth contemplating though: The fact that with a database, the objects represent a local copy of the database state makes some tests invalid if we don’t watch what we’re doing. To avoid similar problems, it can be useful to implement a Redirect-on-POST policy: Every time a user successfully posts data, instead of displaying a form, the user is redirected to a new screen where the result is displayed via a get-request. This also avoids the problem of retransmission of a form if the user accidentally hits the refresh button. The simplest way I have found to implement Redirect-on-POST in Spring-MVC is by overriding SimpleFormController.onSubmit:

Subcategories and parent categories

Back in the integration test, it is about time we made the Category object a bit more interesting. Creating a hierarchy will introduce new challenges. So, then, let’s see if we can express it as a test:

Besides the extra test data, this is a straightforward test case. You can see that we are starting to introduce more functionality in the Category with subcategories. This means that eventually, the Category class will be relatively complex. In order to support this, now would be a good time to start writing unit tests for the Category. See the companion source code for how I’ve done this. [NB: Reader: would it be better to include this in the text?]

As we bind the domain object directly to the view, the change to get the test to pass is pretty simple: We need to create a subcategories field with a getter in the Category, and use this in the “show.ftl” FreeMarker template. Here is the code for the template:

Similarly, it would be a good idea to have a link back to the parent. Calling “clickLinkWIthText” should be sufficient to verify that the code is working. And the corresponding FreeMarker template code: <p>Parent: <a href="show.html?id=${category.parent.id}" id="parent">${category.parent.name}</a></p>. Now, we only want the Parent to be displayed if this isn’t a top-level category. Do we need a test for this? Maybe. But with FreeMarker, we don’t have to. Running the tests again will produce an error as we get NullPointerExceptions for ${category.parent.id}. To fix this, surround the Parent link with the following #if-directive: <#if category.parent??>. This is a FreeMarker null-test.

Still to come

We have successfully laid the foundations for testing web applications. It’s about time to start tackling some more serious issues. Select-input with Spring-MVC is not quite as trivial or well-documented as it should be. The next thing we will explore is how to create a select-option for a many-to-one relationship. Next, I will look at implementing the DAO in Hibernate. Finally, we will look at the steps to take the code all the way into production.

About Johannes Brodwall

Johannes is Principal Software Engineer in SopraSteria. In his spare time he likes to coach teams and developers on better coding, collaboration, planning and product understanding.
This entry was posted in Java, Software Development. Bookmark the permalink.