The Best Way to Configure the Spring MVC Test Framework, Part One

Before we can write unit tests for Spring MVC controllers, we have to configure the system under test (aka the Spring MVC Test framework), and that's why we must understand what's the best way to configure our unit tests.

After we have finished this blog post, we:

  • Can identify the techniques we can use when we want to configure the Spring MVC Test framework.
  • Can choose the best way to configure the Spring MVC Test framework when we are writing unit tests.

Let's begin.

What Options Do We Have?

When we configure the Spring MVC Test framework, we have to create a new MockMvc object which allows us to send HTTP requests to the system under test. We can create this object by using the static factory methods of the MockMvcBuilders class. When we use the MockMvcBuilders class, we can configure the Spring MVC Test framework by using one of these two options:

  • The web application context based configuration loads the Spring application context by using the specified Java configuration classes or XML configuration files, and configures the system under test by using the loaded application context.
  • The standalone configuration allows us to configure the Spring MVC infrastructure programmatically. This option provides the minimum configuration that allows the DispatcherServlet to serve HTTP requests which are processed by Spring MVC controllers. We can naturally customize this configuration by using a fluent API.
If we use Spring Boot, we can also configure the Spring MVC Test framework by using its testing enhancements. I won't cover these enhancements in this blog post because I think that you must understand the basics before you are ready to learn the more advanced stuff.

Additional Reading:

Next, we will take a look at the characteristics of good unit tests.

The Characteristics of Good Unit Tests

Before we can identify the best way to configure the Spring MVC Test framework, we must list our requirements and select the configuration option which fulfills our requirements. When we are writing unit tests, we can identify our requirements by identifying the characteristics of good unit tests.

A good unit test is:

Independent and isolated. A unit test must not depend on the execution order of other unit tests and it must have a clean state that isn't shared with other unit tests. Also, we must isolate the system under test from its external dependencies such as HTTP APIs or databases.

Repeatable. A unit test must be deterministic. This means that if we haven't changed the system under test or the invoked unit test, the result of the unit test must be same every time when we run it.

Not (necessarily) a class test. Some people think that one unit test must test only one method of one class. There are many situations when this approach is useful, but I think that there are also many situations when we must increase the size of the tested unit because this approach helps us to write more meaningful unit tests. For example:

First, if we are writing unit tests for a service which transfers information to an external system by sending HTTP requests with RestTemplate, we should use WireMock because it allows us to verify that the correct HTTP request was sent to the external API when our unit test invoked the tested method.

Naturally, if we want to write so called class tests, we can also replace the RestTemplate object with a mock. If we use this technique, we write shallow tests because we can only verify that the system under test invokes the correct method of the RestTemplate class by using the expected method parameters. This means that we cannot be sure that the system under test sends the correct HTTP request to the external HTTP API.

Second, if we are writing unit tests for a Spring MVC controller, we should use the Spring MVC Test framework because it allows us to write fasts tests for the full Spring MVC runtime behavior. In other words, we can ensure that our HTTP requests are processed by the correct controller method, verify that the method parameters of our controller method are parsed from the incoming HTTP request, ensure that our validation logic is working as expected, and write assertions for the returned HTTP response.

Naturally, we can also write a so called class tests by writing a unit tests which invoke the tested controller method. Even though these tests help us to test some parts of our controller method, they aren't nearly as useful as the tests which use the Spring MVC framework because class tests don't invoke the tested controller method by using its "true" API (HTTP). That's why class tests cannot help us to verify that our Spring MVC controllers are working as expected.

A design tool. It's common knowledge that if we do TDD, unit tests help us write as few lines of production code as possible. Even though this is useful, I think that unit testing has one more important and often overlooked benefit. Unit tests can make it easier to see if the system under test has too many dependencies. If we configure the system under test and its dependencies manually, and we notice that it takes a lot of work, the system under test has too many dependencies or the size of the tested unit is too big.

Fast. A unit test suite is basically our first line of defence and that's why it should be as fast as possible. This is (obviously) important if we are doing TDD because slow tests make our feedback loop longer than it should be. However, this is crucial even if we aren't doing TDD because developers tend to avoid running slow test suites. The problem is that long test runs are basically distractions which make it hard to focus on writing code instead of browsing Reddit, Twitter, or HackerNews.

A good unit test has other characteristics as well. I selected these benefits because they help us to select the best way to configure the Spring MVC Test framework.

We have now identified the requirements of our unit tests. Let's move on and find out what's the best way to configure the Spring MVC Test framework when we are writing unit tests.

Choosing the Best Way to Configure the Spring MVC Test Framework

I argue that if we want to write unit tests which fulfill our requirements, we have to configure our unit tests by using the standalone configuration. The standalone (aka programmatic) configuration has the following benefits over the web application context based configuration:

First, because the standalone configuration provides the minimum configuration that can be customized by using a fluent API, it's easy to select the size of the tested unit. This means that:

  • We don't have to write so called class tests if writing them doesn't make any sense. For example, if we are writing unit tests for a controller method that simply returns the information found from the database, we can use a real service class and replace its dependencies with test doubles. If we use this approach, we can write more meaningful unit tests for our controller method.
  • If a test case fails, it is easy to debug the failing test case because we know which components are run when the failing test case invokes the system under test.

Second, we have to create and configure the external dependencies of the system under test (aka test doubles) in our test class before a test method is run. This might sound like a drawback, but it's actually a huge benefit because of these two reasons:

  • Because our test doubles are created before a test method is run, every test method gets "clean" test doubles. In other words, this approach helps us to write deterministic unit tests and ensure that a unit test has a clean state that isn’t shared with other unit tests.
  • We can use our tests as a design tool. As I mentioned earlier, if we notice that configuring these dependencies takes too much work, the system under test has too many dependencies or the size of the tested unit is too big.

Third, our configuration code is fast because it doesn't load the Spring application context and scan Spring beans from the classpath.

The downside of the standalone configuration is that our unit tests might use a different configuration than the application that's deployed to the production environment.

Let's move on and find out what component we should include in the system under test.

What Components Do We Need?

When we use the standalone configuration, we can select the components which we want to include in the system under test by using the API of the StandaloneMockMvcBuilder class. This is both a blessing and a curse. If we leverage the flexibility provided by the standalone configuration, we can face these two problems:

First, if we include a component in the system under test, the person who reads our test code assumes that the component is required by our test cases. If this is not the case, our configuration is misleading and makes our tests hard to read. To make matters worse, if the person who reads our test code is reading it because a test case failed, a misleading configuration can cost this person a lot of time.

Second, because the StandaloneMockMvcBuilder class allows us to configure pretty much every component that's provided by the Spring MVC framework, it can be quite compelling to take advantage of this opportunity. However, if decide to do so, we end up writing tests which aren’t really unit tests.

These tests are typically hard to write because we have to write too many assertions AND these tests are also hard to maintain because we have to synchronize the configuration of our application and the configuration that we use when we run our unit tests.

That's why I think that we should minimize the number of custom components which we include in the system under test. Also, we shouldn’t use the same configuration in every test class because most likely different controllers don’t require the same components.

We can now select the best way to configure the Spring MVC Test framework when we are writing unit tests. Let's summarize what we learned from this blog post.

Summary

This blog post has taught us seven things:

  • We can configure the Spring MVC Test framework by using the standalone configuration or the web application context based configuration.
  • The standalone configuration provides an easy way to select the size of the tested unit.
  • If we use the standalone configuration, it's easy to write deterministic unit tests.
  • The standalone configuration helps us to ensure that a unit test has a clean state that isn't shared with other unit tests.
  • If we use the standalone configuration, we can use our unit tests as a design tool.
  • The standalone configuration allows us to write fast unit tests because it doesn’t load the Spring application context and scan Spring beans from the classpath.
  • We should minimize the number of custom components which we include in the system under test.
4 comments… add one
  • Anonymous Dec 18, 2019 @ 9:08

    Thank for the useful information!

    • Petri Dec 18, 2019 @ 21:25

      You are welcome!

  • vishu Aug 26, 2020 @ 7:20

    Nice blog brother

    • Petri Sep 10, 2020 @ 17:13

      You are welcome!

Leave a Reply