Configuring the System Under Test
After we have finished this lesson, we
- Understand why we should configure the system under test by using the standalone configuration.
- Know how we can configure the system under test by using the standalone configuration.
- Know how we can avoid writing duplicate code by using an object mother class.
This lesson assumes that:
Why Should We Use the Standalone Configuration?
When we configure the system under test, we have to create a new MockMvc object that acts as an entry point to our unit tests. We can create this object by using either the standalone configuration or the web application context based configuration.
I think that if we are writing unit tests, we should use the standalone configuration because of the following reasons:
First, We can write nested unit tests for our Spring MVC controllers. If we want to use the web application context based configuration, we have to run our unit tests by using the SpringJUnit4ClassRunner class.
Because JUnit allows us to use only one test runner, we cannot use the web application based configuration AND write tests that use nested configuration.
Second, It is easier to select the components that we want to include in the system under test. If we use the web application context based configuration, we have to either take our tests into account when we create the application context configuration class (or XML file) that configures the application context of our web application or create a second configuration class (or XML file) that is used to configure our unit tests.
The problem is that the first option makes the configuration of our application context more complex than it should be and the second option means that we run our tests by using another configuration that is not necessarily the same configuration that we use when we deploy the application to the production environment.
Third, We can create the required test doubles in the test class that needs them. If we use web application context based configuration, we have to create an application context configuration class (or XML file) that creates our test doubles as Spring beans.
Even though this sounds easy, it becomes quite painful when we want to use more than a few test doubles. Also, we have to remember to reset our test doubles before a test method is invoked because all test methods use the same instance of our test double. This makes our tests more complex and adds unwanted dependencies between our test cases.
Let’s find out how we can configure the system under test by using the standalone configuration.
Using the Standalone Configuration
If we want to configure the system under test by using the standalone configuration, we have to follow these steps:
First, we have to add a MockMvc field into our test class. After we have added this field into the HelloControllerTest class, its source code looks as follows:
1import de.bechte.junit.runners.context.HierarchicalContextRunner;
2import org.junit.experimental.categories.Category;
3import org.junit.runner.RunWith;
4import org.springframework.test.web.servlet.MockMvc;
5
6@RunWith(HierarchicalContextRunner.class)
7@Category(UnitTest.class)
8public class HelloControllerTest {
9
10 private MockMvc mockMvc;
11}
Second, we have to create a setup method that is run before a test method is invoked. After we have added this setup method into our test class, its source code looks as follows:
1import de.bechte.junit.runners.context.HierarchicalContextRunner;
2import org.junit.Before;
3import org.junit.experimental.categories.Category;
4import org.junit.runner.RunWith;
5import org.springframework.test.web.servlet.MockMvc;
6import org.springframework.test.web.servlet.setup.MockMvcBuilders;
7
8@RunWith(HierarchicalContextRunner.class)
9@Category(UnitTest.class)
10public class HelloControllerTest {
11
12 private MockMvc mockMvc;
13
14 @Before
15 public void configureSystemUnderTest() {
16 }
17}
Third, we have to implement our setup method by creating a new MockMvc object. We can do this by invoking the static standaloneSetup() method of the MockMvcBuilders class. This method takes the tested controller objects as method parameters and returns a new StandaloneMockMvcBuilder object.
After we have created a new MockMvc object, the source code of our test class looks as follows:
1import de.bechte.junit.runners.context.HierarchicalContextRunner;
2import org.junit.Before;
3import org.junit.experimental.categories.Category;
4import org.junit.runner.RunWith;
5import org.springframework.test.web.servlet.MockMvc;
6import org.springframework.test.web.servlet.setup.MockMvcBuilders;
7
8@RunWith(HierarchicalContextRunner.class)
9@Category(UnitTest.class)
10public class HelloControllerTest {
11
12 private MockMvc mockMvc;
13
14 @Before
15 public void configureSystemUnderTest() {
16 mockMvc = MockMvcBuilders.standaloneSetup(new HelloController())
17 .build();
18 }
19}
Additional Reading:
Also, we can configure the other components of the system under test by using the API of the StandaloneMockMvcBuilder class.
However, if we want to configure additional components such as a view resolver, an object mapper, or a locale resolver, we cannot create these components in our test classes because this creates duplicate code that makes our tests hard to maintain.
Next, we will find out how we can solve this problem.
Configuring Additional Components
When we want to configure additional components, we typically have three goals:
- We want to include additional components in the system under test.
- We want to avoid writing duplicate code.
- We want to keep our tests as easy to read as possible.
We can achieve these goals by creating a new object mother class and configuring the additional components in the factory methods added into our new object mother class. When we follow this advice, we end up with an object mother class that looks as follows:
1public final class WebTestConfig {
2
3 private WebTestConfig() {}
4
5 //Configure the components in factory methods
6}
After we have added the required factory methods into the WebTestConfig class, we can include these components in the system under test by invoking the methods of the StandaloneMockMvcBuilder class
. We will take a closer look at these methods in the next lessons of this topic.
Next, we will evaluate the pros and cons of this approach.
Pros and Cons
This approach has three benefits:
- We don’t have to write duplicate code because our custom components are configured in one place.
- We can see immediately which components are included in the configuration of the system under test.
- We can document our custom custom components by using the method names of our factory methods. Of course, the names of our factory methods cannot document the configuration of our custom components, but they can give us a general idea what the custom component does.
The only drawback of this approach is that we have to duplicate the configuration that we use when we configure the application context of our application. However, I think that the benefits of this approach outweigh its drawback.
Let’s summarize what we learned from this lesson.
Summary
This lesson has taught us three things:
- The standalone configuration gives us the possibility to write nested unit tests that are easy to read, write, and maintain.
- We can configure the system under test by using the
MockMvcBuildersandStandaloneMockMvcBuilderclasses. - Creating our custom components in an object mother class helps us to write unit tests that are easy read, write, and maintain.