I'm doing simple unit test by using mockMvc with springboot. SampleCtrl.java is origin source code and SampleCtrlTest.java is test source code. There are 3 mocks injected to SampleCtrl and they are working well except SampleService class. As you can see, SampleService wrapped with try-catch block. So, I have no idea how to call the method in mock class to get return or throw exception in test source code.
I removed try-catch block in origin code, also given() method in test code. It worked well. That's why I think the try-catch block is undoubted reason to make error.
SampleCtrl.java
#PostMapping(path = "/save", consumes = "application/json")
#ResponseBody
public ResponseEntity<Map<String, Object>> setEmp(#RequestBody
List<EmpSaveVo> vos) {
result.clear();
try {
msg = service.setEmp(vos);
} catch (Exception e) {
msg = e.getMessage();
}
result = messageTrans.getMapLang(msg);
return messageReturn.getRestResp(result, msg);
}
SampleCtrlTest.java
private MockMvc mockMvc;
#Mock
private SampleService service;
#Mock
private MessageTrans messageTrans;
#Mock
private MessageReturn messageReturn;
#InjectMocks
private SampleCtrl sampleCtrl;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(sampleCtrl).build();
}
#Test
public void givenEmployeeDataWhenPostEmpSave() throws Exception {
List<EmpSaveVo> empSaveVos = new ArrayList<>();
EmpSaveVo empSaveVo = new EmpSaveVo();
empSaveVo.setEmployeeId(100L);
empSaveVo.set_status((long) Status.Modified.getStatus());
empSaveVos.add(empSaveVo);
Gson gson = new Gson();
String element = gson.toJson(empSaveVos);
String msg = "Test";
Map<String, Object> result = new HashMap<>();
result.put("message", msg);
given(service.setEmp(empSaveVos)).willThrow(new Exception());
given(messageTrans.getMapLang(msg)).willReturn(result);
given(messageReturn.getRestResp(any(), anyString()))
.willReturn(new ResponseEntity<Map<String, Object>>(result, HttpStatus.OK));
mockMvc.perform(post("/api/emp/save")
.content(element)
.contentType(MediaType.APPLICATION_JSON)).andDo(print())
.andExpect(status().isOk());
}
Consequently, I want to see this console log.
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json;charset=UTF-8"]
Content type = application/json;charset=UTF-8
Body = {"message":"Test"}
Forwarded URL = null
Redirected URL = null
Cookies = []
Related
Can someone please, tell me how you can write a test. I now have to test sending a request from one server to another using RestTemplate.
class ServiceTest {
#Mock
private RestTemplate restTemplate = new RestTemplate();
#InjectMocks
private RConsumerService refundConsumerService = new RConsumerService(new RestTemplateBuilder());
#Test
public void sendRequestToBillingService(){
ChargeResponse chargeResponse = new ChargeResponse();
chargeResponse.setInstanceKey("testInstanceKey");
KafkaMessage kafkaMessage = new KafkaMessage();
kafkaMessage.setApplication_id(1L);
kafkaMessage.setCompany_id(1111);
TransactionRequestContext reqContext = refundConsumerService.createTxnRequestContext(kafkaMessage);
Mockito.when(restTemplate.postForEntity(Mockito.any()
, refundConsumerService.buildChargeRequest(reqContext), ChargeResponse.class))
.thenReturn(new ResponseEntity<>(chargeResponse, HttpStatus.OK));
refundConsumerService.refund(kafkaMessage);
assertEquals(chargeResponse.getInstanceKey(), "testInstanceKey");
}
}
How do I write the condition correctly in
Mockito.when(restTemplate.postForEntity(Mockito.any()
, refundConsumerService.buildChargeRequest(reqContext), ChargeResponse.class))
.thenReturn(new ResponseEntity<>(chargeResponse, HttpStatus.OK));
Now I am getting this exception java.lang.IllegalArgumentException: URI is required
As you are using #Mock and #InjectMocks, you don't need to create new instance of those objects. Mockito will inject it for you. I guess you have this exception because of this parameter : Mockito.any() in your Mockito.when(). It have to be of a Uri type.
Your code will looks like this :
#ExtendWith(MockitoExtension.class)
class ServiceTest {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private RConsumerService refundConsumerService;
#Test
public void sendRequestToBillingService() {
ChargeResponse chargeResponse = new ChargeResponse();
chargeResponse.setInstanceKey("testInstanceKey");
KafkaMessage kafkaMessage = new KafkaMessage();
kafkaMessage.setApplication_id(1L);
kafkaMessage.setCompany_id(1111);
TransactionRequestContext reqContext = refundConsumerService.createTxnRequestContext(kafkaMessage);
URI mockUri = URI.create("http://localhost/mockUri");
Mockito.when(restTemplate.postForEntity(mockUri
, refundConsumerService.buildChargeRequest(reqContext), ChargeResponse.class))
.thenReturn(new ResponseEntity<>(chargeResponse, HttpStatus.OK));
refundConsumerService.refund(kafkaMessage);
assertEquals(chargeResponse.getInstanceKey(), "testInstanceKey");
}
}
I am writing a test to test the POST method for failure case in the controller.
It returns a 415, I am expecting 500. I have mocked the response using mockito.
ControllerTest
#Test
#DisplayName("POST /customers - Failure")
void createProductShouldFail() throws Exception {
// Setup mocked service
when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}");
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
}
Controller
#PostMapping(path = "/customers")
public ResponseEntity<Customer> saveCustomer(#RequestBody Customer customer){
try {
// Create the new product
Customer savedCustomer = customerService.save(customer);
// Build a created response
return ResponseEntity
.created(new URI("/customers/" + savedCustomer.getId()))
.body(savedCustomer);
} catch (URISyntaxException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
Error:
HTTP Method = POST
Request URI = /customers
Parameters = {}
Headers = [Accept:"application/json", Content-Length:"15"]
Body = {"name":"John"}
Session Attrs = {}
Handler:
Type = com.prabhakar.customer.controller.CustomerController
Method = com.prabhakar.customer.controller.CustomerController#saveCustomer(Customer)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 415
Error message = null
Headers = [Accept:"application/json, application/*+json"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
org.opentest4j.AssertionFailedError:
Expected :500
Actual :415
But 415-Unsupported Media Type client error response code.
I have used the same payload for this method,it works.
#Test
#DisplayName("POST /customers - Success")
void createProductShouldSucceed() throws Exception {
// Setup mocked service
Customer mockCustomer = new Customer(1L, "John");
when(customerService.save(any(Customer.class))).thenReturn(mockCustomer);
this.mockMvc.perform(post("/customers")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}"))
// Validate the response code and content type
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
//Validate returned json fields
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.name").value("John"));
}
Update I have added
#RestController
#EnableWebMvc
this throws an error as mocked But the code breaks near mockmvc.perform.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError
How can I verify if this is working.
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
There are two thing you must have in account to solve the problem:
First, Instead of use .accept(MediaType.APPLICATION_JSON) you must use .contentType(MediaType.APPLICATION_JSON).
Second, the other thing you must have in account is, if you are not handling the exception (using a controller advice or other way) you must do it, because when you execute the firts step you will receive the following error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError
The workaround that I took was use #ExceptionHandler in the CustomerController to test your code (this isn't the best place to do this, depending what you are doing. Instead use a #ControllerAdvice. You can find some examples here https://www.baeldung.com/exception-handling-for-rest-with-spring).
Below the complete code that are used to recreate your case.
Customer.class
public class Customer {
private Long id;
private String name;
public Customer(Long id, String name) {
this.id = id;
this.name = name;
}
// + getter and setter
}
CustomerController.class
#RestController
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
#PostMapping(path = "/customers")
public ResponseEntity<Customer> saveCustomer(#RequestBody Customer customer) {
try {
// Create the new product
Customer savedCustomer = customerService.save(customer);
// Build a created response
return ResponseEntity
.created(new URI("/customers/" + savedCustomer.getId()))
.body(savedCustomer);
} catch (URISyntaxException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
// Code used to avoid the error explained in the second step
#ExceptionHandler
public ResponseEntity<?> handlingInternalServerError(HttpServerErrorException.InternalServerError ex) {
// code to be executed when the exception is thrown (logs, ...)
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CustomerService.class
#Service
public class CustomerService {
public Customer save(Customer customer) {
return customer;
}
}
CustomerControllerTest.class
#SpringBootTest
#AutoConfigureMockMvc
class CustomerControllerTest {
#MockBean
private CustomerService customerService;
#Autowired
private MockMvc mockMvc;
#Test
#DisplayName("POST /customers - Failure")
void saveCustomer() throws Exception {
Customer customerMock = new Customer(1L, "John");
// Setup mocked service
when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
RequestBuilder requestBuilder = post("/customers")
.content("{\"name\":\"John\"}")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
}
}
NOTE: This test was executed using Java 8 and JUnit5
Other NOTE based on your comment:
Ok. #prabhakar-maity, my recommendation based in your case is to use a #ExceptionHandler or #ControllerAdvice instead of try...catch. For example, you have 6 methods in your controller or several controllers and want to handle the same exception (Internal Server Error) and return the same info, so you’ll have to implement a try..catch in each method, while using #ControllerAdive (multiple controllers) or #ExceptionHandler (one controller) you implement your logic in one place
Check this question for more info LINK
You can reference Spring MVC Test Framework - Unsupported Media Type
You may be missing #EnableWebMvc annotation in your controller.
EDIT - for Comment:
Instead of
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}");
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(),
response.getStatus());
Try:
mockMvc.perform(requestBuilder)
.andExpect(status().isInternalServerError());
I am working on Spring Boot project with microservice architecture. I have a service that is talking to another service via RestTemplate. HttpDataClient.java class is sending dataId to external service and should receive something in response. For my test I should test that RestTemplate and check if I am getting good response.
Here is class that I need to test:
public class HttpDataClient implements DataClient{
private final static Logger LOGGER = LoggerFactory.getLogger(HttpDataClient.class);
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();
public HttpDataClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Override
public DataResponse getData(String dataId) {
try{
JsonNode node = restTemplate.exchange(
String.format("/data/{0}", dataId),
HttpMethod.POST,
new HttpEntity<>(buildRequest(dataId), headers()),
JsonNode.class
).getBody();
return dataResponse(node);
}catch (HttpStatusCodeException e) {
String msg = String.format(
"Error getting data for dataId: {0}",
dataId,
e.getStatusCode(),
e.getResponseBodyAsString());
LOGGER.error(msg);
return dataResponse.failed();
}
}
private MultiValueMap<String, String> headers() {
final LinkedMultiValueMap<String, String> mv = new LinkedMultiValueMap<>();
mv.set(HttpHeaders.CONTENT_TYPE, "application/json");
return mv;
}
private DataResponse dataResponse(JsonNode node) {
return DataResponse.dataResponse(
asString(node, "dataId"),
asString(node, "author"),
asString(node, "authorDataId"),
asString(node, "serverSideDataId")
);
}
private JsonNode buildRequest(String dataId) {
ObjectNode root = objectMapper.createObjectNode();
root.put("dataId", dataId);
return root;
}
}
Test class looks like this:
#RunWith(MockitoJUnitRunner.class)
public class HttpDataServiceTest {
#Mock
RestTemplate restTemplate;
#InjectMocks
private HttpDataService httpDataService;
#Test
public void getData() {
httpDataService.getData("gameIdTest");
Mockito
.when(restTemplate.exchange(
ArgumentMatchers.eq("/game/IdTest"),
ArgumentMatchers.eq(HttpMethod.POST),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<DataResponse>>any()))
.thenReturn(new ResponseEntity<>(HttpStatus.ACCEPTED));
}
}
When I run the test I am getting a NullPointerException
java.lang.NullPointerException at com.example.gamedata.HttpDataService.getData(HttpDataService.java:37) at com.example.data.HttpDataServiceTest.getData(HttpDataServiceTest.java:36) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
What am I missing here?
At least these are wrong:
you need to do Mockito.when() stuff before you call the actual method. Not after.
/game/idTest/ is different from /data/{0}, they won't match but they need to in order for this to work
DataResponse is not JsonNode, they should match, too
in your when() call you would actually need to return something sensible to be received in HTTP body, just "Accepted" is not enough and it leaves the response body empty
you would need to provide a reasonable json node as response
So your test method contents should be something like
// create response object
ObjectNode responseNode = JsonNodeFactory.instance.objectNode();
responseNode.put("dataId", "");
responseNode.put("author", "");
responseNode.put("authorDataId", "");
responseNode.put("serverSideDataId", "");
// prepare your mock for the call
Mockito
.when(restTemplate.exchange(
ArgumentMatchers.eq("/data/gameIdTest"),
ArgumentMatchers.eq(HttpMethod.POST),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<JsonNode>>any()))
.thenReturn(new ResponseEntity<>(responseNode, HttpStatus.OK));
// do the call
httpDataService.getData("gameIdTest");
im working in a spring boot projet where i create an api that return a file from classpath and all works fine , this is my api :
#Value(value = "classpath:pdf/notice_file.pdf")
private Resource myPdfResource;
#GetMapping("/getLocalDocument/{typeDoc}")
public ResponseEntity<byte[]> getLocalDocument(#PathVariable String typeDoc)
{
byte[] contents = new byte[0];
HttpStatus status = HttpStatus.OK;
HttpHeaders headers = new HttpHeaders();
final InputStream in;
String filename="";
try {
headers.setContentType(MediaType.APPLICATION_PDF);
if ("NOTICE".eqauls(typeDoc)) {
in = myPdfResource.getInputStream();
contents = IOUtils.toByteArray(in);
filename = "notice_1.pdf";
}
headers.setContentDispositionFormData(filename, filename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
return new ResponseEntity<>(contents, headers, status);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
all the tests methods are ok but i'm getting an error with the method getLocalDocumentTest , and this is my unit test code :
#RunWith(SpringRunner.class)
#PrepareForTest(IOUtils.class)
public class ApiTest{
#Mock
private Resource myPdfResource;
#InjectMocks
private Api api;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(api).build();
}
#Test
public void getLocalDocumentTest() throws Exception {
String typeDoc= "NOTICE";
byte[] contents = new byte[0];
InputStream stubInputStream = IOUtils.toInputStream("some test data for my input stream", "UTF-8");;
String URI = "/v1/getLocalDocument/"+typeDoc;
when(myPdfResource.getInputStream()).thenReturn(stubInputStream);
PowerMockito.mockStatic(IOUtils.class);
PowerMockito.when(IOUtils.toByteArray(any(InputStream.class))).thenReturn(contents);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(URI);
MvcResult mvcResult = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
assertEquals(HttpStatus.OK.value(), response.getStatus());
}
}
when i run the test i'm getting the following error :
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
do you have any idea please why i'm getting this error , im new in mockito framework
Thanks.
I am new to JUNIT and using RestTemplate to call my service, I'm getting 200 response for the same. But, I can't test the class using JUnit. Tried different approaches and getting 400 and 404. I want to post the request body (json) and test the status. Please let me know if there is any issue.
/**
* Rest client implementation
**/
public class CreateEmailDelegate implements CDM {
#Autowired
private RestTemplate restTemplate;
private String url = "http://example.com/communications/emails";
public ResponseEntity<CDResponse> createEmail(CDMEmailRequest cDRequest) throws UnavailableServiceException, InvalidInputException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("SR_API_Key", SR_API_KEY);
httpHeaders.set("consumerIdentification", CONSUMER_IDENTIFICATION);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity< CDMEmailRequest > cDRequestEntity = new HttpEntity<>( cDRequest, httpHeaders);
ResponseEntity< CDResponse > cDResponse = null;
try {
cDResponse = restTemplate.postForEntity(url, cDRequestEntity, CDResponse.class);
} catch (Exception e) {
LOGGER.error(e.getMessage());
throw e;
}
return cDResponse;
}
}
My Test class which return 404 status instead of 200
#RunWith(SpringJUnit4ClassRunner.class)
public class CreateEmailCommunicationDelegateTest {
#Before
public void setup() {
httpHeaders = new HttpHeaders();
httpHeaders.set("SR_API_Key", SR_API_KEY);
httpHeaders.set("consumerIdentification", CONSUMER_IDENTIFICATION);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
this.mockMvc = builder.build();
}
public void testResponse() throws Exception, HttpClientErrorException, JsonProcessingException {
String url = "http://example.com/CommunicationDeliveryManagement-Service-1.0.0/communications/emails";
CDMEmailRequest anObject = new CDMEmailRequest();
ResultMatcher ok = MockMvcResultMatchers.status().isOk();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String requestJson = ow.writeValueAsString(anObject);
System.out.println(requestJson);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(url).contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson);
this.mockMvc.perform(builder).andExpect(ok).andDo(MockMvcResultHandlers.print());
}
}
My Test class using TestRestTemplate instead MockMvc returns 400
#RunWith(SpringJUnit4ClassRunner.class)
public class CreateEmailCommunicationDelegateTest {
#Before
public void setup() {
httpHeaders = new HttpHeaders();
// rest headers as above
}
#Test
public void testResponse() throws Exception, HttpClientErrorException, JsonProcessingException {
String url = "http://example.com/CommunicationDeliveryManagement-Service-1.0.0/communications/emails";
String username = "";
String password = "";
HttpEntity<CDMEmailRequest>
cDEntity = new HttpEntity<>(httpHeaders);
restTemplate = new TestRestTemplate(username, password);
responseEntity =
restTemplate.exchange(url, HttpMethod.POST, cDEntity,
CDResponse.class);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK,
responseEntity.getStatusCode());
}
}
I think you're trying to implement an integration test instead of an unit test, there is quite difference. MockMvc should be used to implement unit tests and TestRestTemplate for integration tests. You can't neither use it for testing a Client implementation.
See Unit and Integration Tests in Spring Boot
If you are working with Spring Boot you could achieve your goal using another approach see this question Spring boot testing of a rest client using #RestClientTest.