How to mock http header in MockRestServiceServer? - java

I'm using MockRestServiceServer to mock an external webservice xml response.
That already works fine, but how can I also mock the http header inside the response, not only the response body?
#MockBean
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
#Before
public void createServer() throws Exception {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void test() {
String xml = loadFromFile("productsResponse.xml");
mockServer.expect(MockRestRequestMatchers.anything()).andRespond(MockRestResponseCreators.withSuccess(xml, MediaType.APPLICATION_XML));
}

Just follow your withSuccess method with headers method.
mockServer
.expect(...)
.andRespond(withSuccess().headers(...));

#Gorazd's answer is correct. To add more flesh to it:
HttpHeaders headers = new HttpHeaders();
headers.setLocation(new URI(billingConfiguration.getBillingURL()+"/events/123"));
mockServer.expect(ExpectedCount.once(),
requestTo(new URI(billingConfiguration.getBillingURL())))
.andExpect(method(HttpMethod.POST))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON).headers(headers));

The following codes work for me:
HttpHeaders mockResponseHeaders = new HttpHeaders();
mockResponseHeaders.set("Authorization", mockAuthToken);
mockServer
.expect(once(), requestTo(testhUrl))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess().headers(mockResponseHeaders));

Related

Test ExceptionHandler with RestTemplate

I have a method that makes a hit to external API and I have the exception handler is written to handle the errors and send the client-friendly response in case of errors. I have a requirement to test the non 200 OK responses from that external API such as Bad Request, Internal Server Error, and assert that the exception handler method should be invoked to send a client-friendly message. I am able to successfully mock the response of external API as Bad Request but it is not throwing the HttpStatusCodeException which is ideally thrown for 4xx status code and how can I verify method invocation of exception handler
private final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
private final HttpHeaders httpHeaders = new HttpHeaders();
private final NotificationServiceImpl notificationService = new NotificationServiceImpl(restTemplate, httpHeaders, NOTIFICATION_API_URL, PRIMARY_NOTIFIERS, CC_NOTIFIERS, LANG, APPLICATION_NAME);
#Autowired
private ExceptionTranslator exceptionTranslator;
#Test
void testErrorOnSendNotification() {
Map<String, Instant> messages = Map.of("sample message", Instant.now());
ResponseEntity<HttpStatusCodeException> responseEntity =
new ResponseEntity<>(HttpStatus.BAD_REQUEST);
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<HttpStatusCodeException>>any()))
.thenReturn(responseEntity);
// assertThrows(HttpStatusCodeException.class, () -> notificationService.sendNotification(messages));
verify(exceptionTranslator, times(1)).handleExceptions(any(), any());
}
#ExceptionHandler(Exception.class)
public ResponseEntity<Problem> handleExceptions(NativeWebRequest request, Exception error) {
Problem problem =
Problem.builder()
.withStatus(Status.BAD_REQUEST)
.withTitle(error.getMessage())
.withDetail(ExceptionUtils.getRootCauseMessage(error))
.build();
return create(error, problem, request);
}
You are mocking the restTemplate response. The actual #ExceptionHandler is not called at all. You are bypassing that layer.
In your case, in order to verify the ExceptionHandler, your service layer can be mocked, but the actual REST call has to proceed through, and a REAL response has to be triggered, in order for you to verify the Response Status Code + message.
Psuedo Code below:
#Service
class Service{
public void doSomeBusinessLogic() throws SomeException;
}
#RestController
class ControllerUsingService{
#AutoWired
private Service service;
#POST
public Response somePostMethidUsingService() throws SomeException{
service.doSomeBusinessLogic(someString);
}
}
#Test
void testErrorOnSendNotification() {
when(service.doSomeBusinessLogic(anyString()))
.thenThrow(SomeExceptionException.class);
Response receivedResponse = restTemplate.post(request, headers, etc);
//assert receivedResponse status code + message.
}
Hope that makes sense,
For further clarification:
By doing:
ResponseEntity<HttpStatusCodeException> responseEntity =
new ResponseEntity<>(HttpStatus.BAD_REQUEST);
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<HttpStatusCodeException>>any()))
.thenReturn(responseEntity);
You are bypassing service layer and actually stating that whenever I make a request towards /API/xyz, then I should receive a BAD_REQUEST. That means whatever exception handling you have is going to be bypassed.

How to Mock REST API in unit testing?

I am using RestTemplate exchange HttpMethod.POST method to POST to an endpoint. In my test file I am testing for success of the POST method. However with my current tests I am getting 401 Unauthorized error when POST request is made. I need help to Mock the API while making POST request in test file
Here is my main file
#Component
public class DataTestRepo {
private final RestTemplate restTemplate;
private final String url;
private final AllBuilder headersBuilder;
public DataTestRepo(
#Qualifier(Oauth.BEAN_NAME) AllBuilder headersBuilder,
RestTemplate restTemplate, String url) {
this.headersBuilder = headersBuilder;
this.restTemplate = restTemplate;
this.url = url;
}
public ResponseEntity<String> postJson(Set<String> results) {
ResponseEntity<String> result = null;
try {
JSONObject jsonObject = new JSONObject(body);
HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
restTemplate.getMessageConverters().add(stringConvertor);
result = restTemplate.exchange(url, HttpMethod.POST,
new HttpEntity<>(request, getHttpHeaders()), String.class);
}
return result;
}
}
Here is my test file
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource
public class DataTestRepoTest {
private static final String url = "http://localhost:8080/data/name";
#Mock
private DataTestRepo DataTestRepo;
RestTemplate restTemplate = new RestTemplate();
#Test
public void restTemplateHttpPost_success() throws URISyntaxException {
URI uri = new URI(url);
Set<String> mockData = Stream.of("A","B").collect(Collectors.toSet());
Map<String, String> body = new HashMap<>();
body.put("Name", "Aws");
JSONObject jsonObject = new JSONObject(body);
HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), null);
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.POST,
new HttpEntity<>(request, DataTestRepo.getHttpHeaders()), String.class);
Assert.assertEquals(201, result.getStatusCodeValue());
}
}
You are testing the logic inside DataTestRepo class, so you should not mock it.
RestTemplate is a dependency inside DataTestRepo, so this is exactly what you need to mock.
In general it should look like this inside your test:
#InjectMocks
private DataTestRepo DataTestRepo;
#Mock
RestTemplate restTemplate;
Also, you will have to provide a return value for your mocked dependency, like this:
Mockito.when(restTemplate.exchange(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(new ResponseEntity<>(yourExpectedDataHere, HttpStatus.OK));
enter code here
This is just a simple example. A good practice would be to check that the arguments passed to your mock equal to the expected ones. One way would be to replace ArgumentMatchers.any() with the real expected data. Another is to verify it separately, like this:
Mockito.verify(restTemplate, Mockito.times(1)).exchange(ArgumentsMatchers.eq(yourExpectedDataHere), ArgumentsMatchers.eq(yourExpectedDataHere), ArgumentsMatchers.eq(yourExpectedDataHere), ArgumentsMatchers.eq(yourExpectedDataHere));
This is a great read on this topic: https://reflectoring.io/spring-boot-web-controller-test/

How to mock rest template exchange

I have a method in which is it using RestTemplate. I using the following code to make a call:
final ResponseEntity<RESTResponse> responseEntity = restTemplate.exchange(uri,
HttpMethod.POST,
httpEntityWithHeaders,
RESTResponse.class);
httpEntityWithHeads is of type HttpEntity<String>. I am writing a test and trying to mock RestTemplate so that when it calls the exchange method, it will throw an exception.
I am trying to mock it like this:
when(restTemplate.exchange(
ArgumentMatchers.contains(randomHost),
ArgumentMatchers.eq(HttpMethod.POST),
ArgumentMatchers.<HttpEntity<List<String>>>any(),
ArgumentMatchers.<ParameterizedTypeReference<List<RESTResponse>>>any())
).thenThrow(new ResourceAccessException("Random exception message."));
But when running the test, it doesn't throw the exception, it just continues.
Any suggestions?
As you said httpEntityWithHeads is of type HttpEntity<String>, so you have to stub in way that matches to HttpEntity<String>
when(restTemplate.exchange(
ArgumentMatchers.contains(randomHost),
ArgumentMatchers.eq(HttpMethod.POST),
ArgumentMatchers.<HttpEntity<String>>any(),
ArgumentMatchers.<ParameterizedTypeReference<List<RESTResponse>>>any())
).thenThrow(new ResourceAccessException("Random exception message."));
To me seems that your last parameter is not a list it is a class, and that is why the stub is failing, I tried the following and it is working.
#Test(expected = IllegalArgumentException.class)
public void test() {
RestTemplate restTemplate = mock(RestTemplate.class);
when(restTemplate.exchange(anyString(), ArgumentMatchers.eq(HttpMethod.POST),
any(HttpEntity.class),
any(Class.class))).thenThrow(new IllegalArgumentException("a"));
Rest rest = new Rest(restTemplate);
rest.call();
}
public void call(){
HttpEntity<Object> httpEntityWithHeaders= new HttpEntity<>(null);
final ResponseEntity<Object> responseEntity = restTemplate.exchange("a",
HttpMethod.POST,
httpEntityWithHeaders,
Object.class);
}

TestRestTemplate is returning 415 Unsupported Media Type for POST call on IntelliJ

I am writing functional tests for my Spring Boot 2 application.
I used TestRestTemplate for functional testing. It was working fine for GET APIs. But now for testing POST API, it is returning 415 Unsupported Media Type. I am passing Content-Type and Accept header with value application/json.
This is happening only when running the test on IntelliJ 19. It is running fine on commandline. I am using Gradle build tool.
#RestController
#RequestMapping("Path/rest")
public class Controller {
#PostMapping(value = "/api", produces = "application/json", consumes = "application/json")
public ResponseEntity<Response> getResponse(
#RequestBody Request request,
#RequestHeader(name = "Transaction-GUID", required = false) String transactionGUIDheader, HttpServletRequest request)
throws InvalidInputException, ExternalServiceGenericException {
Response response = service.getResponse(request, transactionGUID);
return new ResponseEntity<>(response, HttpStatus.OK);
}
Functional Test:
#ActiveProfiles(FUNCTIONAL_TEST)
#SpringBootTest (webEnvironmentSpringBootTest.WebEnvironment.DEFINED_PORT)
#ExtendWith({SpringExtension.class})
#AutoConfigureWireMock(port = 9000)
public class ControllerFunctionalTest {
#Autowired
private ObjectMapper objectMapper;
#Autowired
protected TestRestTemplate testRestTemplate;
#BeforeEach
public void setup() {
WireMock.reset();
}
#Test
public void testControllerPost(){
HttpEntity<Reqeust> request = createRq(String str1, String str2);
callWiremock();
String url = "http://localhost:8080/Path/rest/api";
ResponseEntity<Response> response =
testRestTemplate.postForObject(url, request, Response.class);
assertNotNull(response);
}
Just put content type in header (in http Entity).
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
HttpEntity httpEntity = new HttpEntity(headers);

How to write mockito junit for Resttemplate postForObject method

I am trying to post list of messages to the rest api. How to write mockito junit for the method postJSONData below:
public class PostDataService{
#Autowired
RestTemplate restTemplate;
#Autowired
private Environment env;
private HttpEntity<String> httpEntity;
private HttpHeaders httpHeaders;
private String resourceURL = null;
public PostDataService(){
httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Type", "application/json");
}
public void postJSONData(List<String> data){
try
{
resourceURL = env.getProperty("baseURL") + env.getProperty("resourcePath");
httpEntity = new HttpEntity<String>(data.toString(), httpHeaders);
String response = restTemplate.postForObject(resourceURL, httpEntity, String.class);
}
catch (RestClientException e) {
LOGGER.info("ErrorMessage::" + e.getMessage());
LOGGER.info("ErrorCause::" + e.getCause());
}
}
}
Please help me how to write.
You can use Mockito to:
Create an instance of postData with mocked RestTemplate and Environment
Set expectations on these which allow the ``postJSONData` call to complete
Verify that the mocked RestTemplate is invoked correctly
The postJSONData method does not use the restTemplate.postForObject() response so the best you can do in terms of testing this method is to verify that restTemplate.postForObject() is invoked with the correct parameters.
Here's an example:
#RunWith(MockitoJUnitRunner.class)
public class PostDataTest {
#Mock
private RestTemplate restTemplate;
#Mock
private Environment env;
#InjectMocks
private PostData postData;
#Test
public void test_postJSONData() {
String baseUrl = "theBaseUrl";
String resourcePath = "aResourcePath";
Mockito.when(env.getProperty("baseURL")).thenReturn(baseUrl);
Mockito.when(env.getProperty("resourcePath")).thenReturn(resourcePath);
List<String> payload = new ArrayList<>();
postData.postJSONData(payload);
// it's unclear from your posted code what goes into the HttpEntity so
// this approach is lenient about its expectation
Mockito.verify(restTemplate).postForObject(
Mockito.eq(baseUrl + resourcePath),
Mockito.any(HttpEntity.class),
Mockito.eq(String.class)
);
// assuming that the HttpEntity is constructed from the payload passed
// into postJSONData then this approach is more specific
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
Mockito.verify(restTemplate).postForObject(
Mockito.eq(baseUrl + resourcePath),
Mockito.eq(new HttpEntity<>(payload.toString(), headers)),
Mockito.eq(String.class)
);
}
}
On a side note; postData is an unusual name for a class and the postJSONData method provided in your OP doesn't compile; it references meterReadings rather than data.
You can use wiremock to mock the server. It's a mocking framework specifically for this job.
Add following dependency to your pom.xml:
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.12.0</version>
</dependency>
Add following rule to your test:
#Rule
public WireMockRule wireMockRule = new WireMockRule(); // default port is 8080
Then you should define your baseUrl and resourcePath properties in application.properties (or elsewhere). Remember, the server will be running on localhost.
After that you should mock HTTP response for resourcePath:
stubFor(get(urlEqualTo(resourcePath))
.withHeader("Accept", equalTo("application/json"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(content)));
Then you can execute postJSONData method:
postData.postJSONData();
And finally, you can verify if a request to the server was correct.
verify(postRequestedFor(urlMatching(resourcePath))
.withRequestBody(matching(expectedBody))
.withHeader("Content-Type", matching("application/json")));
Just mock postForObject correctly:
#ExtendWith(MockitoExtension.class)
public class YourServiceTest {
#Mock
RestTemplate template;
#InjectMocks
private final YourService srv = new YourService();
#Test
public void yourTest() {
when(template.postForObject(anyString(),any(Object.class),eq(String.class)))
.thenReturn("xxxxxxxxxxx");
assertEquals("xxxxxxxxxxx", srv.yourMethod());
}
}

Categories

Resources