Testing Code With REST Calls Made Easy
Need to test Spring code that makes remote REST calls? Here's an alternative to mocking frameworks that can speed up test development.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
These days, our code frequently has to reach out to a remote server as part of its job, usually consuming an API or some other service.
Frequently, our code communicates with these remote services in a RESTful manner.
Writing code in Spring is no different.
Nothing novel about any of this so far.
What do we when we have to create automated tests (unit, integration, or otherwise) that include these service consumers?
A Typical Solution
Odds are, in this case, if you're using Spring, you're making use of its RestTemplate
class, which makes remote calls a snap (documentation here).
Now, perhaps the most obvious answer to the above question is to reach for the time-tested option of mocks, and often, this is enough. With a myriad of options in the Java world alone (Mockito, EasyMock, and JMockit are but a few popular mocking frameworks), it's difficult to go astray.
In the world of Spring, sometimes Dependency Injection can get in the way, especially in some more complex cases where inserting mocks alongside properly managed and wired beans can be a headache.
So is there an alternative?
MockRestServiceServer to the Rescue!
A couple of major versions ago, Spring introduced MockRestServiceServer
to help with these very situations.
Instead of injecting mocks and having to manage them, MockRestServiceServer
allows you to configure a mock REST service that can intercept outgoing HTTP requests via a series of Matchers
and will instead return some static fixture or response. The best part? It's super simple to use.
Let's take a look at a quick example:
public class MyServiceTest {
// the service to test
@Autowired
private MyService myService;
// note that this RestTemplate should be the one used by MyService above
@Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
@Before
public void setUp() {
this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
}
@Test
public void testMyService() {
// this can be anything, even loaded from a file
String serviceResponseBody = "{'field1': 'abcdef', 'field2': 1234 }";
String url = "https://api.awesome-service.com/api/v1/resource/list";
this.mockServer.expect(requestTo(url))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(serviceResponseBody, MediaType.APPLICATION_JSON));
// call the service to be tested that calls out to the URL in question
this.myService.fetchResources();
}
}
Note that we can perform almost any type of test here, e.g. we could also try calling out to an exposed endpoint that our application has exposed that includes the call to fetchResources().
The code above should be fairly self-explanatory: We set up the mock REST server, configure it, and then call the service under test, and watch the magic! It also fits in nicely with any testing framework you want to use (in this case, JUnit).
The .andRespond(...) portion of the configuration is what tells the mock server what to hand back to the calling code, and takes care of creating proper ResponseEntity
objects.
MockRestServiceServer
implements a builder pattern to make configuration, usage, and readability extremely easy, as you can see.
While the mock REST service configuration above is extremely basic, it does the trick.
For additional information on this feature, be sure to check out its Javadoc, including the various kinds of matches that are available to make your mock REST server even more flexible.
Conclusion
If you're using Spring and need to test code that makes remote HTTP calls using the RestTemplate
class, then the solution described above is one worth checking out, even in simpler cases of automated testing.
While mocking has its place and isn't going anywhere anytime soon, sometimes taking advantage of a platform's alternatives has its benefits.
Opinions expressed by DZone contributors are their own.
Comments