We use cookies to provide you with the best experience on our website. By clicking Accept Cookies, you agree to
our Privacy Policy . To change
this, manage your Cookie Preferences.
As many of you know, Spring is deprecating RestTemplate and replacing it with WebClient. Mocking a server with RestTemplate was done with Spring’s MockRestServiceServer, which was able to mock the endpoint your service would normally call. There is a way to almost replicate that functionality using MockWebServer (okhttp).
2. Clarify Integration Test
We will spin up the application with the Spring context, hit the endpoint with a Spring MockMvc client, have it pass through the application, and then mock the server our service calls on the other end to return back the desired HttpStatus and response body.
This will allow us to test things like what our system returns when getting a 500 error from their dependencies and other complex scenarios.
Integration Test Flow
3. MockWebServer Dependencies
To use MockWebServer, you need two dependencies. Shown below as Gradle imports:
Of course, you need a project to import these into.
4. Test Project
We will be using Spring Boot version 2.4.2 with Gradle and Java 8. Our project will include Spring Security with Client Credentials, Actuator, Spring Web, JUnit 5 and Webflux, and some other common dependencies. It is a simple pass-through API that hits a backend and returns the response from it. If you want to skip to the GitHub repo with the build.gradle file and example code, go here.
5. Writing the Test
Because MockWebServer just provides a url that you can hit, we have to figure out a way to insert that url into our application context at runtime. We do that by mocking the WebClient bean we have previously created. We are also choosing to disable the security in this test because we are testing responses and error handling, not security.
Here is the test code testing a 200 Success response:
6. Explaining the Test
This is quite a bit of code for one test, so I will explain each part separately.There are seven class-level annotations.
The @ExtendWith( SpringExtension.class) and @SpringBootTest annotations are required to spin up the Spring Context for your application when using JUnit 5. Furthermore, the @WebAppConfiguration and @AutoConfigureMockMvc( addFilters = false) allow MockMvc to interact with it. The addFilters are set to false to disable security. We are testing the integration of the immediate requirements here, not the security requirements.
The @TestPropertySource(properties = {"spring.main.allow-bean-definition-overriding=true"}) annotation is to allow you to override the WebClient bean.
The annotation above is to insert the inner TestConfig class we defined that overrides the WebClient bean. The TestConfig class must come after your main Application class, or the bean will not be overridden.
Finally, the @ActiveProfiles("local") annotation is optional and only necessary if you are using a Spring Profile that is required for your application context to spin up. In our case, the Spring profile is named “local”.
In the test code, we are using @MockBean for the JwtDecoder because we are using that bean in our WebSecurityConfig and the context does not spin up without it being mocked.
The MockMvc client can be autowired, and is used to call our endpoint.
The MockWebServer is started in the @BeforeAll setup and stopped in the @AfterAll and is set to a field so it can be referenced in the TestConfig class and the tests.
The TestConfig class is just creating a bean that will overwrite the WebClient bean specified in the production code. It creates a real WebClient, and inserts the randomly generated url from the mockServer into it.
Then, before each test, you can insert the response you want the backend server to return.
This helper method allows you to easily insert a mocked response into your mocked server with the status code and JSON body you desire.
To test how your application handles 500 responses, mock the server to return a 500 response and assert on what you want your response to that error to be. For an example of a 500 test case, see my GitHub repo here.