Mocking a REST call with MockRestServiceServer - java

I'm trying to write a JUnit test case which tests a method in a helper class. The method calls an external application using REST and it's this call that I am trying to mock in the JUnit test.
The helper method makes the REST call using Spring's RestTemplate.
In my test, I create a mock REST server and mock REST template and instanitiate them like this:
#Before
public void setUp() throws Exception {
mockServer = MockRestServiceServer.createServer(helperClass.getRestTemplate());
}
I then seed the mock server so that it should return an appropriate response when the helper method makes the REST call:
// response is some XML in a String
mockServer
.expect(MockRestRequestMatchers.requestTo(new URI(myURL)))
.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
.andRespond(MockRestResponseCreators.withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_XML)
.body(response));
When I run my test, the helper method receives a null response from the REST call it makes and the test fails.
The REST URL that the helper makes has query params and looks like this: "http://server:port/application/resource?queryparam1=value1&queryparam2=value2".
I've tried putting the URL ("http://server:port/application/resource") both with and without the query parameters in the "myURL" variable (to elicit a match so that it returns a response), but can not get the mock server to return anything.
I've tried searching for examples of this kind of code but have yet to find anything which seems to resemble my scenario.
Spring version 4.1.7.
Thanks in advance for any assistance.

When you create an instance of MockRestServiceServer you should use existing instance of RestTemplate that is being used by your production code. So try to inject RestTemplate into your test and use it when invoking MockRestServiceServer.createServer - don't create new RestTemplate in your tests.

Seems that you are trying to test the rest-client, the rest-server should be tested in other place.
You are using RestTemplate -> To call the service. Then, tried to mock RestTemplate and its call's results.
#Mock
RestTemplate restTemplateMock;
and Service Under Test Class
#InjectMocks
Service service;
Let say, Service has a method to be test as
public void filterData() {
MyResponseModel response = restTemplate.getForObject(serviceURL, MyResponseModel.class);
// further processing with response
}
Then, to test filterData method, you need to mock the response from restTemplate call such as
mockResponseModel = createMockResponse();
Mockito.when(restTemplateMock.getForObject(serviceURL, MyResponseModel.class)).thenReturn(mockResponseModel);
service.filterData();
//Other assert/verify,... go here

You can create a new instance of RestTemplate however you have to pass it in your createServer method like this:
private RestTemplate restTemplate = new RestTemplate();
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
client.setRestTemplate(restTemplate);
}

Related

How to write JUnit test for status code of Controller?

I want to test the response of my request.
I have a controller like this,
#RequestMapping("/user")
public class StandardController(#RequestBody Object body) {
#PostMapping(path=/info")
public ResponseEntity<String> getUserInfo(#Validated #RequestBody CustomDTO customDTO) throws customException {
try {
//process something with customDTO
} catch (Exception e) {
//throw exception
}
}
}
Now I have made one of the properties of CustomDTO as #NotNull. When I test the endpoint through Postman I will successfully get 400 as expected if I supply the required field as null value. But how do I test this scenario with Mockito?
If you want to test Spring MVC controllers, don't use unit tests; you need to ensure that everything works correctly including mappings, validation, and serialization. Use MockMvc (in regular, not standalone mode), and if you like you can use Mockito to mock service dependencies.
If you want to test your controller, you have two categories of test:
Unit test by using spring test slice.
Integration test
Read this post also for more understanding.

How do I test business logic in a servlet using Junit?

I'm having a servlet that does some pre-condition checks before invoking a DAO method, like following:
private void processRequest(HttpServletRequest request, HttpServletResponse response){
if(a condition is met)
myDAOFunction();
else
redirect();
}
How should I construct my unit test to verify whether with a certain request, the servlet invokes my function, and with other requests that does not meet the condition then it will redirect the page?
I have tried this solution: Since my DAO function would make some changes in the database if it were called, and through that I can test if the servlet handles the requests and responses correctly. But I figure that is not quite an elegant solution.
So what you need to verify if the servlet can interact with the DAO related codes correctly. If your design already separate and encapsulate all the codes related to interacting with DB in a DAO service class , you can easily test it by mocking this DAO service class using Mockito and then verify if the expected methods on the mock DAO service are invoked with the expected parameters. If not , please refractor your codes such that it will have this separate DAO service class.
For mocking MockHttpServletRequest and MockHttpServletResponse , spring-test already provides some utilities to create them which are useful for testing the Servlet stuff. Although they are primarily designed to work with the codes written by spring-mvc , it should also be used for the codes that are not written by spring and should be more convenient to use when compared Mockito.
Assuming your servlet is called FooBarServlet, the test case may look like :
#ExtendWith(MockitoExtension.class)
public class FooBarServletTest {
#Mock
DaoService daoService;
#Test
void testSaveToDatabase(){
FooBarServlet sut = new FooBarServlet(daoService);
MockHttpServletRequest request = MockMvcRequestBuilders.get("/foobar")
......
.buildRequest(new MockServletContext());
MockHttpServletResponse response = new MockHttpServletResponse();
sut.processRequest(request, response);
verify(daoService).save("xxxxxx");
}
#Test
void testRedirect(){
FooBarServlet sut = new FooBarServlet(daoService);
MockHttpServletRequest request = MockMvcRequestBuilders.get("/foobar")
......
.buildRequest(new MockServletContext());
MockHttpServletResponse response = new MockHttpServletResponse();
sut.processRequest(request, response);
verify(daoService,never()).save(any());
}
}

Testing JSON mapping for a Spring Boot RestTemplate client

I have a REST API outside of my control (supplied by a different, distant team) which I need to consume from a Spring Boot application.
Currently I would like to write a test for that the request (not response) resulting from my RestTemplate invocation corresponds exactly to what is expected at the remote end. I have a sample JSON snippet that I would like to replicate from my code - given the same parameters as in the sample I should get an equivalent JSON snippet in the request body which I would then like to analyze to be certain.
My idea so far is to get RestTemplate to use a server under my control which then captures the JSON request. Apparently MockRestServiceServer is a good choice for this.
Is this the right approach? How do I configure MockRestServiceServer to allow me to do this?
If you're only interested in verifying the JSON mapping, you can always use Jackson's ObjectMapper directly and verify if the object structures match by using a library like JSONassert to verify if the serialized string matches your expected result. For example:
#Autowired
private ObjectMapper objectMapper;
private Resource expectedResult = new ClassPathResource("expected.json");
#Test
public void jsonMatches() {
Foo requestBody = new Foo();
String json = objectMapper.writeValueAsString(requestBody);
String expectedJson = Files
.lines(expectedResult.getFile())
.collect(Collectors.joining());
JSONAssert.assertEquals(expectedJson, json, JSONCompareMode.LENIENT);
}
This test purely uses ObjectMapper to verify the JSON mapping and nothing else, so you could even do this without actually having to bootstrap Spring boot within your test (which could be faster). The downside of this is that if you're using a different framework than Jackson, or if RestTemplate changes its implementation, that this test could become obsolete.
Alternatively, if you're interesting in verifying that the complete request matches (both URL, request method, request body and so on), you can use MockRestServiceServer as you mentioned. This can be done by adding the #SpringBootTest annotation to your test, autowiring RestTemplate and the service that invokes RestTemplate for example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class FooServiceTests {
#Autowired
private RestTemplate restTemplate;
#Autowired
private FooService fooService; // Your service
private MockRestServiceServer server;
#Before
public void setUp() {
server = MockRestServiceServer.bindTo(restTemplate).build();
}
}
You can then set up your tests by using:
#Test
public void postUsesRestTemplate() throws IOException, URISyntaxException {
Path resource = Paths.get(getClass().getClassLoader().getResource("expected-foo.json").toURI());
String expectedJson = Files.lines(resource).collect(Collectors.joining());
server.expect(once(), requestTo("http://example.org/api/foo"))
.andExpect(method(HttpMethod.POST))
.andExpect(MockRestRequestMatchers.content().json(expectedJson))
.andRespond(withSuccess());
// Invoke your service here
fooService.post();
server.verify();
}
As per the documentation, you could match requests using json paths on Mock. For example;
RestTemplate restTemplate = new RestTemplate()
MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
server.expect(ExpectedCount.once(), requestTo(path))
.andExpect(method(HttpMethod.POST))
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].someField").value("some value"))
Note: I haven't tested this.
But I have achieved what you are looking for using Wire Mock many times. That's again a much better option than MockRestServiceServer. Why do I say so?
wide adoption and support
more elegant and extensive request & response matching
highly configurable
record and playback
configurable security/auth
you could even dockerise this
Have a look at http://wiremock.org/docs/request-matching/
I think your approach using a stub server (you could use WireMock for this) is fine if you want to check once, manually.
Alternatively you could add a request logger to your RestTemplate which logs each request. That would make it easier to check if the sent request is correct any time if problems arise.

Unit test for Spring controller have RequestPart String param

I must write unit test using junit for a spring web project.
A controller have parameter like
#RequestMapping(value = "/tasks/{id}/", method = RequestMethod.PUT)
public ResponseEntity<String> unlock(
#PathVariable String id, #RequestPart String param)
I dont know what exactly param receive. I usually use #RequestPart MultipartFile.
In the test, Inject your class.
Add MockMvc (is a part of Spring Test an let you call your WS).
Prepare the mock, ie: when(myClassInjected.myMethod()).thenReturn(the_result);
Perform a call with MockMvc. (you can follow the tutorial at the end of the post)
Verify the call, ie: verify(myClassInjected, times(1)).myMethod();
Check this tutorial to get more information.

RestTemplate for local file testing

Since I want to use the RestTemplate from Spring I want to use the same class as well for Unit-Testing. The idea would be to download a JSON-File and save it locally for the purpose of testing. Therefore I would like to change the URI from a HTTP to a File address. When it as File-address I get an Excpetion
Exception in thread "main" java.lang.IllegalArgumentException: Object of class [sun.net.www.protocol.file.FileURLConnection] must be an instance of class java.net.HttpURLConnection
urlGETList = "http://api.geonames.org/countryInfoJSON?username=volodiaL";
RestTemplate restTemplate = new RestTemplate();
CountryInfoResponse results = restTemplate.getForObject(urlGETList, CountryInfoResponse.class);
Any ideas how I can use the same classes for Unit-Testing?
I think you could look into Wiremock.
Wiremock allows subbing of requests. The advantage is that you really test the complete stack and your tests make real requests against a server responding with mock responses. These mock response bodies can be files (there are other possibilities as well.)
In your unit test you set up wiremock server like this:
#Rule
public WireMockRule wireMockRule = new WireMockRule(port);
Then you can setup a stub with a file response like this:
public void givenResponse(int statusCode, MediaType contentType, String bodyPath) {
String responseBody;
try (InputStream data = new ClassPathResource(bodyPath).getInputStream()) {
responseBody = copyToString(data, UTF_8);
}
stubFor(any(urlPathEqualTo(getWireMockUri().getPath()))
.willReturn(aResponse()
.withStatus(statusCode)
.withHeader("Content-Type", contentType.toString())
.withHeader("Content-Length", String.valueOf(responseBody.length()))
.withBody(responseBody)
));
}
You could also put the complete stub into a stub file like described here
Afterwards you can check if a certain request has been made:
verify(postRequestedFor(urlEqualTo("/form"))
.withHeader("Content-Type", containing(MULTIPART_FORM_DATA_VALUE)));
You can find out more about verification here
You would use the RestTemplate to make requests. You just need to have host and port configurable so you can use localhost and the wiremock port in your tests.

Categories

Resources