
Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:
Once the early-adopter seats are all used, the price will go up and stay at $33/year.
Last updated: May 11, 2024
We frequently find ourselves with applications that perform some sort of web request. When it comes to testing this behavior, we have a few options with Spring apps.
In this quick tutorial, we’ll look at just a couple of ways of mocking such calls performed only through a RestTemplate.
We’ll start by testing with Mockito, a popular mocking library. Then we’ll use Spring Test, which provides us with a mechanism to create a mock server to define the server interactions.
We can use Mockito to mock the RestTemplate altogether. With this approach, testing our service would be as simple as any other test involving mocking.
Let’s assume we have a simple EmployeeService class, which fetches employee details over HTTP using the getForEntity() method:
@Service
public class EmployeeService {
@Autowired
private RestTemplate restTemplate;
public Employee getEmployeeWithGetForEntity(String id) {
ResponseEntity resp =
restTemplate.getForEntity("http://localhost:8080/employee/" + id, Employee.class);
return resp.getStatusCode() == HttpStatus.OK ? resp.getBody() : null;
}
}
Now let’s implement a test for the previous code:
@ExtendWith(MockitoExtension.class)
public class EmployeeServiceTest {
@Mock
private RestTemplate restTemplate;
@InjectMocks
private EmployeeService empService = new EmployeeService();
@Test
public void givenMockingIsDoneByMockito_whenGetForEntityIsCalled_thenReturnMockedObject() {
Employee emp = new Employee(“E001”, "Eric Simmons");
Mockito
.when(restTemplate.getForEntity(
"http://localhost:8080/employee/E001", Employee.class))
.thenReturn(new ResponseEntity(emp, HttpStatus.OK));
Employee employee = empService.getEmployeeWithGetForEntity(id);
Assertions.assertEquals(emp, employee);
}
}
In the above JUnit test class, we first asked Mockito to create a dummy RestTemplate instance using the @Mock annotation.
Then we annotated the EmployeeService instance with @InjectMocks to inject the dummy instance into it.
Finally, in the test method, we defined the behavior of our mock using Mockito’s when/then support.
exchange() method in Spring RestTemplate is a versatile way to execute HTTP requests. We can use the GET, POST, PUT, and DELETE methods with the exchange() method.
Let’s assume we have a simple EmployeeService class, which fetches employee details through HTTP using the exchange() method:
public Employee getEmployeeWithRestTemplateExchange(String id) {
ResponseEntity<Employee> resp = restTemplate.exchange("http://localhost:8080/employee/" + id,
HttpMethod.GET,
null,
Employee.class
);
return resp.getStatusCode() == HttpStatus.OK ? resp.getBody() : null;
}
Now let’s write a test for the above code:
@ExtendWith(MockitoExtension.class)
public class EmployeeServiceUnitTest {
@Mock
private RestTemplate restTemplate;
@InjectMocks
private EmployeeService empService = new EmployeeService();
@Test
public void givenMockingIsDoneByMockito_whenExchangeIsCalled_thenReturnMockedObject(){
Employee emp = new Employee("E001", "Eric Simmons");
Mockito.when(restTemplate.exchange("http://localhost:8080/employee/E001",
HttpMethod.GET,
null,
Employee.class)
).thenReturn(new ResponseEntity(emp, HttpStatus.OK));
Employee employee = empService.getEmployeeWithRestTemplateExchange("E001");
assertEquals(emp, employee);
}
}
In the above test, we first created a mocked RestTemplate instance using @Mock annotation. Later we injected the EmployeeService instance which we used to invoke our logic. Finally, we mocked the exchange() method with the necessary parameters and returned the employee instance.
The Spring Test module includes a mock server named MockRestServiceServer. With this approach, we configure the server to return a particular object when a specific request is dispatched through our RestTemplate instance. In addition, we can verify() on that server instance whether or not all expectations have been met.
MockRestServiceServer actually works by intercepting the HTTP API calls using a MockClientHttpRequestFactory. Based on our configuration, it creates a list of expected requests and corresponding responses. When the RestTemplate instance calls the API, it looks up the request in its list of expectations, and returns the corresponding response.
Thus, it eliminates the need of running an HTTP server in any other port for sending mock responses.
Let’s create a simple test for the same getEmployeeWithGetForEntity() example using MockRestServiceServer:
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringTestConfig.class)
public class EmployeeServiceMockRestServiceServerUnitTest {
@Autowired
private EmployeeService empService;
@Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
private ObjectMapper mapper = new ObjectMapper();
@BeforeEach
public void init() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
@Test
public void givenMockingIsDoneByMockRestServiceServer_whenGetIsCalled_thenReturnsMockedObject()() {
Employee emp = new Employee("E001", "Eric Simmons");
mockServer.expect(ExpectedCount.once(),
requestTo(new URI("http://localhost:8080/employee/E001")))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(mapper.writeValueAsString(emp))
);
Employee employee = empService.getEmployeeWithGetForEntity(id);
mockServer.verify();
Assertions.assertEquals(emp, employee);
}
}
In the previous snippet, we used static methods from in MockRestRequestMatchers and MockRestResponseCreators to define the expectation and response for the REST call in a clear and readable way:
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
We should keep in mind that the RestTemplate in the test class should be the same instance used in the EmployeeService class. To ensure this, we defined a RestTemplate bean in the spring config and auto-wired the instance in both test and implementation:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
We can use a similar approach mentioned as before to test for the getEmployeeWithRestTemplateExchange() example using MockRestServiceServer:
@Test
public void givenMockingIsDoneByMockRestServiceServer_whenExchangeIsCalled_thenReturnMockedObject() throws Exception {
Employee emp = new Employee("E001", "Eric Simmons");
mockServer.expect(ExpectedCount.once(), requestTo(new URI("http://localhost:8080/employee/E001")))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(mapper.writeValueAsString(emp)));
Employee employee = empService.getEmployeeWithRestTemplateExchange("E001");
mockServer.verify();
Assertions.assertEquals(emp, employee);
}
As we can see in the above test, we’re using static methods from the MockRestRequestMatchers and MockRestResponseCreators to define the expectation and response for REST calls with the exchange() method.
Using a MockRestServiceServer is very useful when we write our integration tests and only need to mock external HTTP calls.
In this brief article, we discussed a few effective options for mocking the external REST API calls over HTTP while writing unit tests.