Upcoming courses: | ||
---|---|---|
8th, 10th, 15th and 17th of February 2022 | Twice a week - 2 Tuesdays and 2 Thursdays | Register Now! |
We’ve been through a lot. We’ve covered all kinds of flows to be tested on the server side. We’ve seen how to check flows from the controller down to the database, combined with injecting different mocks and services. We’ve got one more thing: What happens if my controller (or any other component) makes an external API call. I may want to mock that call. Does String help?
Here’s our client. It calls the ItemController from last time, through its API:
public class Client {
@Autowired RestTemplate template;
public String getAllItems() {
try {
ResponseEntity<String> result =
template.getForEntity("/item/" , String.class);
return result.getBody();
}
catch (HttpClientErrorException e) {
if (e.getStatusCode()== HttpStatus.BAD_REQUEST)
throw new ItemNotFoundException();
}
return "";
}
}
Code language: JavaScript (javascript)
We need to supply the RestTemplate through configuration . Just create one, it’s created in the context of the WebApplication, and therefore the web container that Spring runs. The RestTemplate is very helpful in posting and getting, and making HTTP API calls. If something bad happens, it throws an exception we can check and do something about it.
So if we want to check the logic of our client in an integration test, we would want to mock the API call. We can, as we’ve seen before, inject a mock RestTemplate, and tell it what to return. That’s kids stuff. But then everybody will make their own mock, and that’s a waste.
So, of course, we get a Spring solution. It’s got the sexy name MockRestServiceServer. Yeah, I know, but it does what it says on the label.
In order to create one, we need to remember that the RestTemplate we use is connected to the test server. when we run the integration test. We need to have a shared RestTemplate between our client-under-test, and the integration test itself, and Spring happily injects one for us. It’s still a regular RestTemplate:
@Configuration
public class TestClientConfiguration {
@Bean
public Client client() {
return new Client();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Code language: PHP (php)
Once we’ve got a configuration, we can use it in our integration test class:
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes= {TestClientConfiguration.class})
public class ClientTests {
@Autowired RestTemplate template;
@Autowired Client client;
MockRestServiceServer mockServer;
@Before
public void setup() {
mockServer = MockRestServiceServer.createServer(template);
}
...
}
Code language: JavaScript (javascript)
Then we build our MockRestServiceServer manually, using our RestTemplate. Or if we’re lazy, Spring gives as an annotation to inject the MockRestServiceServer automatically, so we don’t have to build it ourselves (or the RestTemplate):
@RunWith(SpringRunner.class)
@AutoConfigureMockRestServiceServer
@SpringBootTest
@ContextConfiguration(classes= {TestClientConfiguration.class})
public class ClientTests {
@Autowired MockRestServiceServer mockServer;
...
}
Code language: JavaScript (javascript)
Now that we got mock what are we gonna do with it? For the first integration test, let’s configure it to return a value that doesn’t trigger an exception.
@Test
public void get_ServerCalledCorrectly() {
mockServer
.expect(once(), requestTo("/item/"))
.andRespond(
withSuccess("", MediaType.TEXT_PLAIN));
client.getAllItems();
mockServer.verify();
}
Code language: JavaScript (javascript)
Just like good ol’ EasyMock, but at the API level, we can set expectations, behavior and verify those callse.
For the second integration test, we’ll ask the mock to return a bad request answer that will result in throwing the exception:
@Test (expected = ItemNotFoundException.class)
public void add_ThrowsOnError() {
mockServer.expect(once(), requestTo("/item/"))
.andRespond(withBadRequest());
client.getAllItems();
}
Code language: JavaScript (javascript)
That’s pretty nifty. We can control dependent services in our integration tests.
We can have the client call the the server, if it makes sense. For example, if we can set up the database and configure it, we can check the integration between the services.
Remember, the longer the sequence, the bigger the setup, and the chance of the flow breaking on something else rises too. There are cases though when mocking is necessary, if we don’t control the server, of if the server returns a different result every time. Think an API that returns the global time or weather. These are hardly repeatable. And controlling the weather is even harder.
0 Comments