Why mockServer.verify is needed in MockRestServiceServer - java

When using MockRestServiceServer with andExpect to test
mockServer.expect(requestTo("/hotels/42")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON));
Then test failed if found unexpected behavior,
For example no further requests expected: HTTP if sent to unexpected URL
My Config:
#SpringBootApplication(scanBasePackages = { "..." })
public class MyConfig extends SpringBootServletInitializer {
My Test class
#ContextConfiguration( classes = {MyConfig.class})
#ActiveProfiles("local")
#WebAppConfiguration
public class My Test extends AbstractTestNGSpringContextTests {
#Autowired
#InjectMocks
private ServiceUnderMock serviceUnderMock;
private AutoCloseable closeable;
#BeforeClass
public void initMocks() {
closeable = MockitoAnnotations.openMocks(this);
mockServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
}
#AfterClass
public void releaseMocks() throws Exception {
closeable.close();
}
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
#Test
public void test() {
try {
mockServer.expect(ExpectedCount.min(1),
requestTo(new URI("https://www.google.com")))
.andExpect(method(HttpMethod.GET));
} catch (URISyntaxException e) {
Assert.fail("failed to create mock");
}
serviceUnderMock.doSomething();
}
So why we need to add the mockServer.verify()?
At the end of the test use verify() to ensure all expected requests were actually performed.

The idea of MockRestServiceServer is that it allows you mock the external server such that the RestTemplate does not really need to send the requests to the actual server during the testing. Instead it just sends the requests to this MockRestServiceServer (think that it is a kind of in-memory server) and it will return the configured mocked responses for the corresponding requests.
You have to configure all the expected requests that the MockRestServiceServer will received and its corresponding responds before the test.
So basically there are two things needed to be verified which are :
For every request sent by RestTemplate , there should be a mocked response configured for that request in the MockRestServiceServer
For all the requests that are to be expected to be received on the MockRestServiceServer , the RestTemplate should really send out all of these expected requests.
(1) will be verified automatically whenever the RestTemplate send out a request. The exception no further requests expected: HTTP that you mentioned is because it fails (1) (i.e. forget to stub this request in the MockRestServiceServer)
(2) will not be verified automatically . You have to call MockRestServiceServer.verify() manually in order to verify it.
An example :
mockServer.expect(requestTo(new URI("https://www.yahoo.com")))
.andExpect(method(HttpMethod.GET)).andRespond(withSuccess());
mockServer.expect(requestTo(new URI("https://www.google.com")))
.andExpect(method(HttpMethod.GET)).andRespond(withSuccess());
mockServer.expect(requestTo(new URI("https://www.stackoverflow.com")))
.andExpect(method(HttpMethod.GET)).andRespond(withSuccess());
restTemplate.getForEntity("https://www.yahoo.com", String.class);
restTemplate.getForEntity("https://www.google.com", String.class);
Without mockServer.verify() , the test still passes although RestTemplate does not send the request to https://www.stackoverflow.com which the MockServer is expected to be received.
But with mockServer.verify() , it can check that and hence fails the test.

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 write mockito test for post request

This function is used to update the user details in the database. can someone help me to write test cases for this function.
#RequestMapping(value = "/updateDetails", method = RequestMethod.POST)
public String updateVendorDetails(#Valid #ModelAttribute("users") Users users, BindingResult result,Model model) {
logger.info("{}.{}",new VendorController().getClass().getPackageName(), new VendorController().getClass().getName());
if(result.hasErrors()) {
model.addAttribute("edit","edit");
logger.warn("Function: updateVendorDetails(), Information: Error while updating vendor details");
return register.toString();
}
userDao.updateVendorDetails(users);
logger.info("Function: updateVendorDetails(), Information: Vendor details updated successfully");
return vendor.toString();
}
Update
Code:
mockMvc.perform(post("/updateDetails").accept(MediaType.TEXT_HTML).params(params)).andExpect(status().isOk());
Resulting error:
This says that post method is forbidden and my test fails
This is my Test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class TestVendorPage {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#WithMockUser(roles = "VENDOR")
#Test
public void testIfUpdateEdtailsIsAvailableOnlyForVendor() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("firstName", "vinod");
params.add("lastName", "babu");
params.add("contactNumber", "9952016709");
mockMvc.perform(post("/updateDetails").accept(MediaType.TEXT_HTML).params(params)).andExpect(status().isOk());
}
}
Regarding your update:
Thank you for clarifying your post with a specific error/specific problem.
For that specific error - HTTP 403: Forbidden - this should resolve the problem:
Unit test Springboot MockMvc returns 403 Forbidden
i think probleam is happend in "mockMvc" object is not
autowired.mockMvc object should load from WebApplicationContext in
before program run.
Please - PLEASE - consider looking at one or more of the links I cited above.
baeldung.com: Testing in Spring Boot
spring.io: Testing the Web Layer
mkyong.com: Spring REST Integration Example
I've found all three sites very valuable resources. Time spent with these tutorials will help you a great deal!

com.sun.xml.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:text/plain. Is this an error message instead of a SOAP response?

Hello I am trying to create a dirty test for my soap integration test. I just got SSL working on my spring boot app and I wanted to see if it will hit my soap end point.
When I run man verify on my integration test I get this error:
com.sun.xml.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:text/plain. Is this an error message instead of a SOAP response?
Here is my test code :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {EndPointTestConfiguration.class
})
public class SoapIT {
private static ApplicationContext context;
#BeforeClass
static public void setup(){
SpringApplication springApplication = new SpringApplicationBuilder()
.sources(MockServerApp.class)
.build();
context = springApplication.run();
}
#Autowired
private String studyDetailDemo;
#Test
public void soapTest() throws ClientProtocolException, IOException {
String result = Request.Post("https://127.0.0.1:28443/nulogix/ws/billingtool")
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(studyDetailDemo, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
}
}
I am kind of new to integration testing and have no idea what this error means
Thank you for any help
I think you need to read up a bit on how to do spring testing.
testing-with-mock-environment
#SpringBootTest will automatically scan for spring annotated classes and load up a mockspringcontext for you so you dont need to do all the #BeforeClass things.
If you want to call this "Mock context" you need configure and autowire in a MockMvc, WebTestClient or TestRestTemplate.
On the other hand if you want to start a real server you need to specify #SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) (or a defined port).
You can read all about it in the linked documentation above.
And btw, you can't autowire in a string.
Your code should look something like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class SoapIT {
#LocalServerPort
private int port;
private String studyDetailDemo = "some body text";
#Test
public void soapTest() throws ClientProtocolException, IOException {
String result = Request.Post("https://127.0.0.1:" + port + "/nulogix/ws/billingtool")
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(studyDetailDemo, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
}
}
Havn't tried the code, wrote it on mobile.

How mock REST Request

I am using Mockito in JUnit and I have a method making a request to a microservice using RestTemplate.
private static final String REQUESTOR_API_HOST = "http://localhost:8090/requestor/v1/requestors/";
public TokenRequestorPayload getTokenRequestor(Long id) {
restClient = new RestTemplate();
return restClient.getForObject(REQUESTOR_API_HOST + id, TokenRequestorPayload.class);
}
This method returns a JSON object which will be deserialize in TokenRequestorPayload class.
When I execute unit tests they fail because mock didn't work and I got a org.springframework.web.client.ResourceAccessException. How can I mock my RestTemplate?
Test
RestTemplate restTemplate = Mockito.spy(RestTemplate.class);
Mockito.doReturn(this.tokenRequestorMockJson()).when(restTemplate).getForObject(Mockito.anyString(), Mockito.eq(TokenRequestorPayload.class));
Error
org.springframework.web.client.ResourceAccessException: I/O error on
GET request for "http://localhost:8090/requestor/v1/requestors/1":
Connection refused (Connection refused); nested exception is
java.net.ConnectException: Connection refused (Connection refused)
In your test you are defining behavior for a mock instance of RestTemplate, which is good.
RestTemplate restTemplate = Mockito.spy(RestTemplate.class);
However, the same instance is not used by the Class Under Test which creates a new RestTemplate instance each time and uses that one.
public TokenRequestorPayload getTokenRequestor(Long id, RestTemplate restClient) {
restClient = new RestTemplate(); // it uses this instance, not the mock
return restClient.getForObject(REQUESTOR_API_HOST + id, TokenRequestorPayload.class);
}
So you need to find a way to bring the mock instance to the getTokenRequestor() method.
E.g. this can be accomplished by turning restClient into a method parameter:
public TokenRequestorPayload getTokenRequestor(Long id, RestTemplate restClient) {
return restClient.getForObject(REQUESTOR_API_HOST + id, TokenRequestorPayload.class);
}
and the test might look something like like:
#Test
public void test() {
RestTemplate restTemplateMock = Mockito.spy(RestTemplate.class);
Mockito.doReturn(null).when(restTemplateMock).getForObject(Mockito.anyString(), Mockito.eq(TokenRequestorPayload.class));
// more code
instance.getTokenRequestor(id, restTemplateMock); // passing in the mock
}
Use Spring's mocking support for RestTemplate rather than trying to mock RestTemplate - much nicer:
Create a mock rest service server binding it to your RestTemplate:
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
Record a call to that mock server e.g.
mockServer.expect(requestTo("some url").andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("addSuccessResult", MediaType.TEXT_PLAIN));
Then verify the mocks have been called:
mockServer.verify();
See https://objectpartners.com/2013/01/09/rest-client-testing-with-mockrestserviceserver/

Jersey Test Framework - define default error response for all unknown paths in grizzly

To test our API that connects to the facebook graph API we use a mock server setup based on Jersey Test Framework and grizzly:
#Path("/" + PostRest.RESOURCE)
#Produces("application/json")
public class PostRest {
public static final String RESOURCE = "111_222";
#GET
public Response getPost(#QueryParam("access_token") String access_token) {
if (access_token != VALID_TOKEN) {
return Response.status(400).entity(createErrorJson()).build();
}
return Response.status(200).entity(createSomeJsonString()).build();
}
Now while I can react to an invalid or missing access_token with the correct error response, I also want to test that my API reacts correctly when trying to access an unkown resource at facebook ie an unkown path.
Right now I get a 404 from my grizzly obviously, if I try to access say "/111_2", but facebook seems to catch that error and wrap it inside a Json response, containing the string "false" with status 200.
So... How do I set up the Test Framework to return
Response.status(200).entity("false").build();
every time it is called for an known path?
Basic example:
#ContextConfiguration({ "classpath:context-test.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeTest extends JerseyTest {
#Inject
private SomeConnection connection;
private String unkownId = "something";
public SomeTest() throws Exception {
super("jsonp", "", "com.packagename.something");
}
#Test(expected = NotFoundException.class)
public void testUnkownObjectResponse() throws NotFoundException {
// here it should NOT result in a 404 but a JSON wrapped error response
// which will be handled by the Connection class and
// result in a custom exception
connection.getObject(unkownId);
}
Or maybe I can set up grizzly to behave as desired..?!
Thanks!
Obviously facebook has it own service to intercept errors. Same thing should be done in your code. Just expose you own test service that intercepts all request
#Path("/test/errorTrap")
public class ErrorTrapService{
....
}
This service will produce any response you want. So any un-existing pages like http://mytest/test/errorTrap/111_2 will be intercepted by test service and produce expected response for you

Categories

Resources