Jan 9, 2013

REST Client Testing With MockRestServiceServer

Functionally testing a REST Client is simple with the new MockRestServiceServer if you are using Spring’s RestTemplate to power the client. This is a new feature in Spring 3.2.x but was available via the spring-test-mvc project starting with Spring 3.1.x (extra spring-test-mvc.jar required). The documentation is a little light in the spring reference manual so hopefully this example can help you piece it together.

Previously you might have had unit tests that mocked the RestTemplate but didn’t fully test the calls and error handling provided with the framework. Or you created an elaborate fake server environment just to spit back valid and invalid responses. MockRestServiceServer takes the approach of mocking the server and allowing you to specify expected behavior and responses in your junit test class. This allows you to fully test your handling of the RestTemplate client and server exception classes.

This example only shows how the mock server works. In a real environment you’d probably use RestTemplate with Jackson for object to json mapping and possibly Spring @Async for asynchronous calls.

SimpleRestService is a sample REST client that makes a call to a URL and handles successes and errors by returning them in the result string. We’ll use this as an example for our junit test cases:
<pre>
@Service
public class SimpleRestService {
@Autowired
private RestTemplate restTemplate;

public String getMessage() {
String result;
try {
String httpResult = restTemplate.getForObject("http://google.com",
String.class);
result = "Message SUCCESS result: " + httpResult;
} catch (HttpStatusCodeException e) {
result = "Get FAILED with HttpStatusCode: " + e.getStatusCode()
+ "|" + e.getStatusText();
} catch (RuntimeException e) {
result = "Get FAILEDn" + ExceptionUtils.getFullStackTrace(e);
}
return result;
}
}
</pre>

The only real setup you need for testing is to configure your IDE to find the static imports. In Eclipse this is in Java>Editor>Content Assist>Favorites. Add these to go along with the hamcrest CoreMatchers and junit Assert that you probably already have.
If using Spring 3.2.x:
org.springframework.test.web.client.match.MockRestRequestMatchers
org.springframework.test.web.client.response.MockRestResponseCreators
If using Spring 3.1.x, the static import classes are named differently:
org.springframework.test.web.client.match.RequestMatchers
org.springframework.test.web.client.response.ResponseCreators

Each test will chain expect() and respond() methods. MockRestRequestMatchers offers many hamcrest matchers to check your request URL, headers, HTTP method, and even json and xpath matchers to check body content. MockRestResponseCreators allows you to easily build both success and error responses.

Also, each test must call mockServer.verify() after the RestTemplate call is made to run the Mock Server assertions.

Setup the MockRestServiceServer in the setUp method:
<pre>
@Before
public void setUp() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
</pre>

testGetMessage() verifies our URL, GET HttpMethod, and returns a 200 Success with a text message of resultSuccess:
<pre>
@Test
public void testGetMessage() {
mockServer.expect(requestTo("http://google.com"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("resultSuccess", MediaType.TEXT_PLAIN));

String result = simpleRestService.getMessage();

mockServer.verify();
assertThat(result, allOf(containsString("SUCCESS"),
containsString("resultSuccess")));
}
</pre>

testGetMessage_404() shows a response with the specific 404 Not Found client http status code:
<pre>
@Test
public void testGetMessage_404() {
mockServer.expect(requestTo("http://google.com"))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.NOT_FOUND));

String result = simpleRestService.getMessage();

mockServer.verify();
assertThat(result, allOf(containsString("FAILED"),
containsString("404")));
}
</pre>

testGetMessage_500() shows usage of the withServerError() convenience method:
<pre>
@Test
public void testGetMessage_500() {
mockServer.expect(requestTo("http://google.com"))
.andExpect(method(HttpMethod.GET))
.andRespond(withServerError());

String result = simpleRestService.getMessage();

mockServer.verify();
assertThat(result, allOf(containsString("FAILED"),
containsString("500")));
}
</pre>

Additional matcher test examples can be found in the spring-test-mvc section of the spring 3.2.x github repo.

Hopefully the new mock server in Spring helps you as much as it helped me, by cleaning up and reducing the amount of testing code required in both a reusable and standard fashion. The full java code from these examples are on my github page.

About the Author

Object Partners profile.

One thought on “REST Client Testing With MockRestServiceServer

  1. Hanan says:

    Can you please attach what you in
    /testconfig/applicationContext-test.xml
    configuration file.

  2. Jeff Sheets says:

    No problem. I just added the applicationContext-test.xml to the github repo. I also modified the test class to use the non-transactional AbstractJUnit4SpringContextTests so it is easier to setup.

    Take a look. Hope it helps!

  3. Bernhard says:

    Hi,

    Thanks for your helpful example! One comment:

    I noticed that you are injecting the RestTemplate in your test. There are several issues with storing RestTemplates as instance variables (e.g. the template uses the BasicClientConnectionManager per default (for which the docs say “Even though this class is thread-safe it ought to be used by one execution thread only.”) which can lead to IllegalStateException due to connection reallocation attempts by different threads).

    Hence you probably only want to inject your HttpRequestFactory and create the RestTemplate on the fly when needed using new().

    Just my 2 cents 😉

    1. Jeff Sheets says:

      Bernhard, thanks for the feedback!

      You raise an interesting question with valid points, but I’m not sure that autowiring is an issue with RestTemplate.

      It seems that even Spring’s official blog posts on RestTemplate are autowiring it in and labeling it as being Thread-safe:
      http://blog.springsource.org/2009/03/27/rest-in-spring-3-resttemplate/

      You may want to file a bug with the Spring team to clarify this, since they seem to list RestTemplate as being Thread-safe. And unfortunately I couldn’t find any forum postings related to RestTemplate and autowiring.

      Thanks for the discussion!
      — Jeff

  4. Hanan says:

    Can you please help me with an example of a mocking a request with header parameters.

  5. Jeff Sheets says:

    Hanan, try adding something like this to the mockServer expect line:

    .andExpect(header(“Content-Type”, “application/json”))

    1. Hanan says:

      Thank you for your help; I really appreciate your article
      Another help please
      I don’t know how to add query parameters to get request
      and how could i do post request.

  6. Jeff Sheets says:

    Hanan,
    If you have a REST Client that does a post, then to test that you would change the mockServer to expect a HttpMethod.POST. To check query parameters, one way is to use a different hamcrest matcher on the requestTo() like requestTo(containsString(“param1=myval”). For a POST you might need to check the body with content(), jsonPath(), or xpath()

    There are many usage samples of request matchers here:
    https://github.com/SpringSource/spring-framework/tree/master/spring-test-mvc/src/test/java/org/springframework/test/web/client/samples/matchers

  7. Hanan says:

    But requestTo(containsString(“param1=myval”)
    compare the matcher with request.getURI().toString(), I want to include this parameters in the request body not in the url.

    1. Jeff Sheets says:

      Hi Hanan,
      Please see my full reply via email, but essentially yes you’ll need to check the body with something like this:
      .andExpect(jsonPath(“$.person[0].name”).value(“Hanan”))

  8. Vijay says:

    Hi

    I want to write the test to check rest readTimeOut, Can you please help me to write the test please

  9. Jeff Sheets says:

    Vijay,

    I think you would need to implement a custom ResponseCreator for this purpose that has a sleep() function in it for a specified amount of time, though I haven’t tried it.

  10. Vijay says:

    Thanks Jeff,

    Still am not able to test the readTimeout even after implementing custom ResponseCreator. I am put thread sleep(timeinmilisecond) in createResponse method where timeinmilisecond > readtime I configured in context file and still not getting expected result. But thanks for replay.

  11. Jeff Sheets says:

    Vijay,

    Unfortunately the way that MockRestServiceServer works is that the createServer() method changes the RestTemplate to use a RequestMatcherClientHttpRequestFactory instead of any configured requestFactory that you may have used. So even if you set a readTimeout or connectTimeout on SimpleClientHttpRequestFactory, the RestTemplate won’t use it since it has been changed to use a different requestFactory.

    You’ll need to just throw a SocketTimeoutException from the response to test how your code handles the exception (since I think this is what the Java Connection object will throw on a timeout).

    Try implementing a new TimeoutResponseCreator and finish your mockServer call with .andRespond(withTimeout()):

    public class TimeoutResponseCreator implements ResponseCreator {

    @Override
    public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
    if (true) {
    throw new SocketTimeoutException(“Testing timeout exception”);
    }
    return null;
    }

    public static TimeoutResponseCreator withTimeout() {
    return new TimeoutResponseCreator();
    }
    }

  12. Marco says:

    Hi Jeff,

    thanks for the examples. Can you update the link to the additional matchers? I was in need for the jsonPath matcher and I found this page useful, but the link (second last paragraph before our responses) now is https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/client/samples/matchers.

    Thanks,

    Marco

  13. Shailesh says:

    Any chance of creating similar testing for AsyncRestTemplate?

  14. Bob F. says:

    When I try to execute the example I get this error. java.lang.AssertionError: Further request(s) expected
    0 out of 1 were executed

    What am I missing?

  15. Jeff Sheets says:

    Bob F., I’m not sure. Sounds like something didn’t work in the setup. You should be able to clone the repo, run mvn eclipse:eclipse, import it and run the tests.
    https://github.com/jeffsheets/MockRestServiceServerExample

    1. Gabby says:

      I tried running it with no luck,I am getting the same error as Bob F. please help

  16. matt says:

    If you don’t mind, how would I specify a service method in SimpleRestService that was a POST? Is GET the default? I’ve tried using @RequestMapping(method = RequestMethod.POST) but it didn’t work. Thanks for your efforts!

  17. Jeff Sheets says:

    matt, try changing the method to use postForObject instead of getForObject on this line:
    restTemplate.getForObject(

  18. Goran Petrov says:

    Hi, I am wondering if it is possible to execute some kind of a callback when the request from the rest template is received?

  19. Jeff Sheets says:

    Hi Goran,

    Take a look at the new AsyncRestTemplate. It allows you to register a callback.

  20. Sam JYot says:

    HI , I am having the confusion in the scenario where , i am having a dynamic string created inside the service method , and i need to test that the service method returns a string of Any value . In short how do we test only the return type in andRespond(??); .
    Thanks .

  21. Ran says:

    Great article but unfortunately my code below isn’t working. The verify() call fails as the request doesn’t go to my mock server.

    @Autowired
    private SlackWebhooks service;

    private MockRestServiceServer server;

    @Before
    public void setUp() {
    RestTemplate restTemplate = new RestTemplate();
    server = MockRestServiceServer.createServer(restTemplate);
    }

    @Test
    public void invokeIncomingWebhook_When_Invoked_Should_PostCorrectRichMessage() {
    this.server.expect(requestTo(“https://hooks.slack.com/services/asdasd”)).
    andExpect(method(HttpMethod.POST)).
    andRespond(withServerError());

    ResponseEntity response = this.service.invokeSlackWebhook();

    server.verify();

    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    Can you please help me out here?

    1. Jeff Sheets says:

      @Ran make sure to inject/autowire the RestTemplate to the test so that it uses the same one that the SlackWebhooks service is using. I think the issue is that this test is using a different RestTemplate so the mock service doesn’t get setup correctly.

      Here’s a test class for comparison: https://github.com/jeffsheets/MockRestServiceServerExample/blob/master/src/test/java/com/jeffsheets/rest/SimpleRestServiceFunctionalTest.java

  22. Lubomír Zrnečko says:

    Hi, Jeff, just want to thank you for your article, it was helpful.
    Also let me say that I really admire your infinite patience with responding to all of the requests for help in comments.

    1. Jeff Sheets says:

      Glad you found it useful, thanks for the nice comments!

  23. Kevin says:

    I know this is a really old article, but seeing that you continued responding for years, I thought I’d give it a shot. I’m trying to use MockRestServiceServer to mock multiple different api endpoints that are all called behind the scenes in the single test I am trying to write. I’m trying to write a Controller level test that calls out to a rest api multiple times, hitting different api endpoints each time with different query params. I don’t want to .expect each one since there are many and it depends on the data, so I tried to use two .expect calls using a Matcher passed to the requestTo method. But it apparently only allows a single matcher.

    I need to mock out multiple endpoints with different outcomes. What am I missing with this mock server?

    1. Jeff Sheets says:

      Hi Kevin!

      If you want to respond differently to each request, you’ll need separate expect lines for each one. But if you just want to respond the same each time you might be able to make the requestTo matcher very generic or with a regex to match on all of them.

      There’s also the ability to tell it to expect any number of requests. See this comment for more details:
      https://jira.spring.io/browse/SPR-11365?focusedCommentId=127425&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-127425

      Or this one on stackoverflow: https://stackoverflow.com/a/41720142/1469525

      Good luck, hope this helps!
      — Jeff

      1. Kevin says:

        I have it working where I literally write a new expect line for each api endpoint because I can’t seem to use the requestTo matcher with a Matcher like contains() or startsWith because for some reason, if fails when I have multiple expect lines that have different startsWith matcher. It basically insinuates that you can only have a single Matcher.

        I can’t seem to mix the one generic matcher based .expect call with the specific ones I want to vary for my test. My api endpoint that I want to create a controller level test for calls another backend service for translations and timezone formatting. I want to ignore the translation calls and have them just return a badRequest, while the timezone formats are the ones I want to isolate and test. So take this generic expectation:

        this.server.expect(ExpectedCount.manyTimes(), MockRestRequestMatchers.requestTo(contains(“http://translationservice”)))
        .andRespond(withBadRequest());

        and then I have a specific one for the piece I’m trying to verify:

        this.server.expect(ExpectedCount.manyTimes(), MockRestRequestMatchers.requestTo(“http://translationservice/format” +
        “?value=2017-04-10T18%253A30%253A00Z&tz=America/Denver”))
        .andRespond(MockRestResponseCreators.withSuccess(translationResponseDay, MediaType.APPLICATION_JSON));

        It doesn’t seem to let me mix one expection using a Matcher and one using a specific requestTo. It just doesn’t work. I get an AssertionError where it’s expecting a blank uri and getting a specific one. As if the generic matcher for the translationservice is not matching the specific api calls to the translationservice.

        Does that make sense? I can make it work if I specify all the very specific calls to the translation service with all the query parameters and the EXACT same response, but it’s very cluttered and seems like there should be a better way.

        1. Jeff Sheets says:

          Hmmm, yeah that looks okay at a quick glance. I’m not sure what is going on in this case. You might have some better luck posting this one as a question on stackoverflow too. My guess is it doesn’t like that `http://translationservice/format` will match on both requests

          1. Kevin says:

            Missed that. I have tried /translationservice/translation separate from /translationservice/format but I will try that again. If it doesn’t work, will try Stack Overflow. Thanks Jeff! And way to support a super old blog post!

      2. Kevin says:

        Thanks, BTW!

        1. Jeff Sheets says:

          Yeah, good luck! Unfortunately I don’t have an active project to test this out on at the moment. Hopefully you are getting close to the answer though!

  24. Tom Ang says:

    Jeff, I found your posting is very useful. But, at the stage where I am at, I am still confuse between the mockserver and verifying the return from the service called.

    Let’s take a look at your sample code the testGetMessage() :
    1. mockServer.expect(requestTo(“http://google.com”))…
    2. String result = simpleRestService.getMessage();
    3. mockServer.verify();
    4. assertThat(result, allOf(containsString(“SUCCESS”),containsString(“resultSuccess”)));

    My questions is if we remove line 1 & 3, what happen then ? Shouldn’t give the same purpose ? What is the relationship between line 1 and 2 ?

    Thanks for your help.

    1. Jeff Sheets says:

      Hi Tom,

      mockServer is basically the fake server that Spring wires up so your test can hit against that instead of a real server, if that makes sense?

      So if you remove lines 1 & 3, you’ll get some sort of error from Spring because mockServer won’t be expecting the call in #2 so it won’t know what to respond with. You’ll either get an error, or result in line 2 will be null.

      Line 1 is just telling the server to expect a restTemplate call to the http://google.com URL, with the .andRespond(…) telling the fake server what to respond with.

      Line 2 will make a call from restTemplate to the http://google.com url (but it will be intercepted by the mock server and never go out to the real internet)

      Hope this helps!
      — Jeff

  25. Javier says:

    Can you help with an example when the interface it’s being implemented by 2 or more classes?

    public interface Service {
    void getCustomer();
    }

    @Primary
    @Service
    public class ServiceImpl implements Service {
    @Override
    public void getCustomer() {}
    }

    @Component(“ServiceV2”)
    public class ServiceV2Impl extends ServiceImpl {
    @Override
    public void getCustomer() {}
    }

    The test:

    @RestClientTest(ServiceImpl.class)
    @ActiveProfiles(“test”)
    @ExtendWith(SpringExtension.class)
    @SpringBootTest(classes = {CommonRestClientApplication.class, RestClientConfig.class})
    public class ServiceImplTest {
    @Autowired private SupplierService client;
    @Autowired private MockRestServiceServer server;

    @Test
    void getSupplier() throws IOException {
    this.server
    .expect(requestTo(“http://localhost:80/service/v1/customer/15”))
    .andRespond(withSuccess(data, MediaType.APPLICATION_JSON));

    }
    }

    The server it’s being initialized with 2 expectationManagers. So the error is:

    Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has been bound to more than one RestTemplate.

    Any suggestion is welcome. Thanks

Leave a Reply to Hanan Cancel reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]