I am new to JUnit and Mockito. Here I am trying to mock the rest template call and return a response entity. But it throws me a Null pointer Exception. I am not able to figure out what's wrong with the mocking. Can anyone guide me where I am doing wrong?
JUnit5 Unit test cases
class MyTest {
#InjectMocks
private MyService service;
#Mock
private RestTemplate template;
#BeforeEach
void setUp(){
MockitoAnnotations.initMocks(this);
}
private ResponseEntity<String> generateResponse(String body, HttpStatus http) {
return new ResponseEntity<>(body, http);
}
#Test
public void publishEventsuccessResponseTest() {
when(template.postForEntity(ArgumentMatchers.anyString(), ArgumentMatchers.any(),
ArgumentMatchers.<Class<String>>any())).thenReturn(generateResponse("uuid",
HttpStatus.OK));
String result = service.sentData("data");
Assertions.assertTrue("uuid".equals(result));
}
}
Service
class MyService {
public String sentData(String data) {
String jsonResp = "";
ResponseEntity<String> response;
try {
HttpEntity<String> requestBody = new HttpEntity<String>(data, headers);
response = restTemplate.postForEntity("url", requestBody, String.class);
} catch (Exception ex) {}
System.out.println(response); //value : Null
if (response.getStatusCodeValue() == 200) { // Null pointer exception
jsonResp = response.getBody();
}
return jsonResp;
}
}
Change the #BeforeEach to #Before of jUnit4 and add public void setUp()
Your test should be green!
Related
i am trying to write the mock test case for RestServiceImpl class. Below is the code. And i have a test class shown below RestServiceImplTest. When i run the test class it returning null from line restTemplate.exchange(UrlString, HttpMethod.POST, request, Object.class)
public class RestServiceImpl
private RestTemplate restTemplate;
#Autowired
public RestServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Override
public ResponseEntity<Object> restService(DataRequest dataRequest) throws Exception {
ResponseEntity<Object> response = null;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<DataRequest> request = new HttpEntity<>(dataRequest, headers);
try {
response = restTemplate.exchange(UrlString, HttpMethod.POST, request, Object.class);
} catch (Exception ex) {
throw ex;
}
return response;
}
}
Test class
#RunWith(SpringRunner.class)
public class RestServiceImplTest {
private RestServiceImpl restServiceImpl;
#Mock
private RestTemplate restTemplate;
#Mock
private DataRequest dataRequest;
#Before
public void setUp() {
restServiceImpl = new RestServiceImpl(restTemplate);
dataRequest = new DataRequest();
}
#Test
public void testRestServiceImplwithSuccess() throws Exception {
ResponseEntity<Object> resp = new ResponseEntity<Object>(HttpStatus.OK);
doReturn(resp).when(restTemplate).exchange(any(URI.class), any(HttpMethod.class), any(HttpEntity.class),
any(Class.class));
ResponseEntity<Object> mockResp = restServiceImpl.restService(DataRequest);
}
Can anybody tell me where is it going wrong.
Almost certainly this is happening because your doReturn is not being used due to the parameters not being met (your any() s).
This is the first time I'm trying to write Integration Tests with the spring framework test. The controller returns a proper response in Postman but MvcResult returns "application/json;charset=UTF-8" as a response. I tried different solutions present on Internet but nothing seems to work. Please, help me with this.
I want to extract the key-value pair from the JSON response.
spring test = 5.1.8.RELEASE
Controller
#RestController
public class CustAuController {
#RequestMapping(value = "/retrieve_something", method = RequestMethod.GET)
retrieveSomething(){
}
}
Postman Response
{
"total_count" : 3,
"data" : {
"list" : [
{
...
}
]
}
}
Test Class
#SpringBootApplication
#SpringBootTest(classes = ServiceApplication.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ControllerTest {
#Autowired
WebApplicationContext webApplicationContext;
MockMvc mockMvc;
public static String asJsonString(final Object obj) {
try {
final ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#BeforeAll
void setMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
#DisplayName("Retrieve Something")
#Order(1)
void testRetrieveSomething() throws Exception{
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(
"/retrieve_something"))
.andExpect(status().isOk()).andReturn();
System.out.println(
result.getResponse().getContentType()); // returns application/json;charset=UTF-8
}
}
Change in the last line getContentType() to getContentAsString()
I have what I think is a pretty standard StorageService that exists in a Spring Boot app for a web API.
#Component
#Service
#Slf4j
public class StorageService {
#Autowired
private AmazonS3 s3Client;
#Autowired
private RestTemplate restTemplate;
#Value("${app.aws.s3.bucket}")
private String bucket;
#Async
public boolean fetchAndUpload(List<URI> uris) {
List<CompletableFuture<PutObjectResult>> futures = uris.stream().map(uri ->
fetchAsync(uri).thenApplyAsync((asset) -> put(getName(uri.toString()), asset))
).collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();
return true;
}
#Async
private CompletableFuture<InputStream> fetchAsync(URI uri) {
return CompletableFuture.supplyAsync(() -> {
InputStream resp;
try {
// asdf is null here when running my unit tests
Resource asdf = restTemplate.getForObject(uri, Resource.class);
resp = Objects.requireNonNull(asdf).getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
return resp;
});
}
private PutObjectResult put(String name, InputStream data) {
PutObjectRequest request = new PutObjectRequest(bucket, name, data, new ObjectMetadata());
return s3Client.putObject(request);
}
}
Here is an integration test, which at minimum successfully fetches the images given by the integration test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureWebClient
public class StorageServiceIT {
#Value("${app.aws.s3.access.key}")
private String accessKey;
#Value("${app.aws.s3.secret.key")
private String secretKey;
#Spy
private AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.withRegion(Regions.US_EAST_1)
.build();
#Spy
private RestTemplate restTemplate = new RestTemplateBuilder().build();
#MockBean
private SignService signingService;
#Autowired
private StorageService storageService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void fetchAsync() throws URISyntaxException {
List<URI> uris = List.of(
new URI("https://upload.wikimedia.org/wikipedia/commons/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg"),
new URI("https://upload.wikimedia.org/wikipedia/commons/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg")
);
storageService.fetchAndUpload(uris);
}
}
However, the following unit test does not successfully mock the restTemplate.getForObject call, it constantly returns null, even when setting both arguments to any().
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureWebClient
public class StorageServiceTest {
#MockBean
private AmazonS3 s3Client;
#MockBean
private RestTemplate restTemplate;
#MockBean
private SignService signingService;
#Autowired
// #InjectMocks ???
private StorageService storageService;
#Value("${app.aws.s3.bucket}")
private String bucket;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
List<URI> randomUris(int num) {
final String base = "https://example.com/%s";
return Stream.iterate(0, i -> i)
.limit(num)
.map(o -> URI.create(String.format(base, UUID.randomUUID().toString())))
.collect(Collectors.toList());
}
#Test
public void fetchAsyncTest() {
List<URI> uris = randomUris(2);
uris.forEach(uri -> {
ByteArrayInputStream data = new ByteArrayInputStream(
Integer.toString(uri.hashCode()).getBytes());
PutObjectRequest request = new PutObjectRequest(
bucket, getName(uri.toString()), data, new ObjectMetadata());
Resource getResult = mock(Resource.class);
try {
doReturn(data).when(getResult).getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
doReturn(data).when(restTemplate).getForObject(any(), any());
// none are working
// doReturn(data).when(restTemplate).getForObject(eq(uri), eq(Resource.class));
// doReturn(data).when(restTemplate).getForObject(uri, Resource.class);
// when(restTemplate.getForObject(uri, Resource.class)).thenReturn(data);
// when(restTemplate.getForObject(eq(uri), eq(Resource.class))).thenReturn(data);
// when(restTemplate.getForObject(any(), any())).thenReturn(data);
PutObjectResult res = new PutObjectResult();
doReturn(res).when(s3Client).putObject(eq(request));
// not working here as well, i think
// doReturn(res).when(s3Client).putObject(request);
// doReturn(res).when(s3Client).putObject(any());
// when(s3Client.putObject(eq(request))).thenReturn(res);
// when(s3Client.putObject(request)).thenReturn(res);
// when(s3Client.putObject(any())).thenReturn(res);
});
boolean res = storageService.fetchAndUpload(uris);
}
}
And just in case it's relevant, this is how I'm building the RestTemplate:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
I'm stumped, all advice is appreciated! :|
To follow up on this - my problem was because the method I was attempting to test was marked with Spring Boot's #Async annotation, causing race conditions and some mocks to not be configured properly.
Working on a spring boot based Rest project I have a controller like this
which calls service and service layer call dao layer. Now I am writing unit test code for controllers. when I run this the error says
java.lang.AssertionError: expected:<201> but was:<415>
I don't know where I am doing wrong:
public class CustomerController {
private static final Logger LOGGER = LogManager.getLogger(CustomerController.class);
#Autowired
private CustomerServices customerServices;
#Autowired
private Messages MESSAGES;
#Autowired
private LMSAuthenticationService authServices;
#RequestMapping(value = "/CreateCustomer", method = RequestMethod.POST)
public Status createCustomer(#RequestBody #Valid Customer customer, BindingResult bindingResult) {
LOGGER.info("createCustomer call is initiated");
if (bindingResult.hasErrors()) {
throw new BusinessException(bindingResult);
}
Status status = new Status();
try {
int rows = customerServices.create(customer);
if (rows > 0) {
status.setCode(ErrorCodeConstant.ERROR_CODE_SUCCESS);
status.setMessage(MESSAGES.CUSTOMER_CREATED_SUCCESSFULLY);
} else {
status.setCode(ErrorCodeConstant.ERROR_CODE_FAILED);
status.setMessage(MESSAGES.CUSTOMER_CREATION_FAILED);
}
} catch (Exception e) {
LOGGER.info("Cannot Create the Customer:", e);
status.setCode(ErrorCodeConstant.ERROR_CODE_FAILED);
status.setMessage(MESSAGES.CUSTOMER_CREATION_FAILED);
}
return status;
}
}
The test for the CustomerController.
public class CustomerControllerTest extends ApplicationTest {
private static final Logger LOGGER = LogManager.getLogger(CustomerControllerTest.class);
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#MockBean
private CustomerController customerController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
Status status = new Status(200,"customer created successfully","success");
String customer = "{\"customerFullName\":\"trial8900\",\"customerPhoneNumber\": \"trial8900\", \"customerEmailID\": \"trial8900#g.com\",\"alternateNumber\": \"trial8900\",\"city\": \"trial8900\",\"address\":\"hsr\"}";
#Test
public void testCreateCustomer() throws Exception {
String URL = "http://localhost:8080/lms/customer/CreateCustomer";
Mockito.when(customerController.createCustomer(Mockito.any(Customer.class),(BindingResult) Mockito.any(Object.class))).thenReturn(status);
// execute
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(URL)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.content(TestUtils.convertObjectToJsonBytes(customer))).andReturn();
LOGGER.info(TestUtils.convertObjectToJsonBytes(customer));
// verify
MockHttpServletResponse response = result.getResponse();
LOGGER.info(response);
int status = result.getResponse().getStatus();
LOGGER.info(status);
assertEquals(HttpStatus.CREATED.value(), status);
}
}
HTTP status 415 is "Unsupported Media Type". Your endpoint should be marked with an #Consumes (and possibly also #Produces) annotation specifying what kinds of media types it expects from the client, and what kind of media type it returns to the client.
Since I see your test code exercising your production code with MediaType.APPLICATION_JSON_UTF8, you should probably mark your endpoint as consuming and producing APPLICATION_JSON_UTF8.
Then you also need to make sure that there is nothing terribly wrong going on in your error handling, because in the process of catching the exceptions generated by your production code and generating HTTP responses, your error handling code may be generating something different, e.g. generating an error status response with a payload containing an HTML-formatted error message, which would have a content-type of "text/html", which would not be understood by your test code which expects json.
Use the below base test class for your setUp and converting json to string and string to json
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public abstract class BaseTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, clazz);
}
}
Also verify that your post call has happened or not check the below sample
Mockito.doNothing().when(customerServices).create(Mockito.any(Customer.class));
customerServices.create(customer);
Mockito.verify(customerServices, Mockito.times(1)).create(customer);
RequestBuilder requestBuilder = MockMvcRequestBuilders.post(URI)
.accept(MediaType.APPLICATION_JSON).content(inputInJson)
.contentType(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
assertEquals(HttpStatus.OK.value(), response.getStatus());
Hi I have the following code to test:
#RequestMapping(value = "/displayPages", method = RequestMethod.GET)
public ModelAndView errorPage(ModelMap model, HttpServletRequest request) {
String token = (request != null) ? request.getParameter("tok") : "";
boolean requestP = ESAPI.validator().isValidInput("Request Param", tok, "HTTPParameterValue", 1, false);
if (requestP || token.contains(msg.getMessage("techErr.tok", new Object[]{}, Constants.LOCAL))) {
return new ModelAndView("dispError");
} else {
return new ModelAndView("login");
}
}
Part of my unit test class is shown below:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ESAPI.class})
public class ApplicationConTest {
#InjectMocks
private ApplicationCont appController;
private MockMvc mockMvc;
....
#Before
public void setup() {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpSession session = new MockHttpSession();
request.setSession(session);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
this.mockMvc = MockMvcBuilders.standaloneSetup(appController).build();
}
#Test
public void errorPages_ExpectLogout() throws Exception {
String returnVal = "test";
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getParameter("tok")).thenReturn("dummuy value");
when(msg.getMessage("techErr.tok",new Object[]{},Constants.LOCAL)).thenReturn(returnVal);
//msg.getMessage("techErr.tok", new Object[]{}, Constants.LOCAL)
mockMvc.perform(get("/errorPages"))
.andExpect(view().name("logout")
);
}
When I execute the code the String tok is null and i get a null pointer exception. However as showed above I am returning a dummy value,
any advice why tok is null?
Thanks in advance for any help.