Error: java.lang.NullPointerException by RestTemplate exchange method - SpringBoot - java

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");

Related

MockRestServiceServer Returning Null ResponseEntity?

I have the following mocked server:
mockInteractiveServer = MockRestServiceServer.createServer(oauthClientConfig.getRestTemplate());
I use this mocked server in a JUnit test case:
#Test
public void myTestMethod() throws Exception{
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Location", "samplePrefix/sampleTicketId");
mockInteractiveServer
.expect(ExpectedCount.once(), requestTo(new URI("mockURI")))
.andExpect(method(HttpMethod.POST))
.andExpect(content().string("validation content"))
.andRespond(withStatus(HttpStatus.OK)
.headers(responseHeaders));
//calls the actual service, which should call the mocked server
String ticketId = interactiveTicketService.myMethod(new createTicketRequest());
assertThat(ticketId).isEqualTo("sampleTicketId");
}
The actual service, InteractiveTicketService, calls the server here:
public String myMethod(CreateTicketRequest createTicketRequest){
//This builds the model object
TicketRequest request = myBuilder.buildTicketRequest();
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
//createTicketResponse is null
ResponseEntity<String> createTicketResponse = iDOCClient.getRestTemplate()
.postForEntity(iDOCClient.getRequestUrl() + "/interactive-documents", request, String.class);
}
And this is the model class I am passing to the postForEntity() method:
public class MyTicket implements TicketRequest {
private List<Document> documents;
private PublishingRequestWrapper data;
private String assignedTo;
private String requestType;
private String templateGroup;
private List<Object> templates;
//constructors, getters & setters
}
Again, the issue I am facing is that the ResponseEntity is always null.

ResponseEntity is returning null when imposing class object

I am new to Spring boot and sorry in case it's very basic but I am posting as I have tried other ways and checked similar threads as well.
If I use below code it's returning correct response
ResponseEntity<String> responseEntityString = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
Output
[{"Id":"123aa","TenId":5198,"Name":"test","Description":"test11","Tags":[]}]
Now I have created workspace class like below (getter/setter/arg constructor and no-arg construcntor is also there)
public class Workspace {
private String Id;
private String TenId;
private String Name;
private String Description;
private List<String> Tags;
}
Now I execute the below code -
ResponseEntity<List<Workspace>> response = restTemplate.exchange(
url,
HttpMethod.GET,
requestEntity,
new ParameterizedTypeReference<List<Workspace>>(){});
List<Workspace> employees = response.getBody();
employees.stream().forEach(entry -> System.out.println(entry.getId() + ": " + entry.getName()));
It's returning
null: null
Below is returning true
System.out.println("Value "+ response.hasBody());
Below is returning - New Values [com.pratik.model.Workspace#3cbf1ba4]
New Values [com.pratik.model.Workspace#3cbf1ba4]
So please advise what needs to change to get the values
================================================================
Initialized resttemplate bean like below
public class app1 {
static RestTemplate restTemplate = new RestTemplate();
static String url = url;
public static void main(String[] args) {
SpringApplication.run(app1.class, args);
getCallSample();
}
===============================================================
Update on the latest code
ResponseEntity<Workspace[]> responseNew = restTemplate
.exchange(
url,
HttpMethod.GET,
requestEntity,
Workspace[].class);
Workspace [] employees1 = responseNew.getBody();
List<Workspace> list = Arrays.asList(employees1);
list.stream().forEach(entry -> System.out.println(entry.getId() + ": " + entry.getName()));
Still the response is
null: null
===============================================================
Another update
When tried with String.class it's returning
[{"Id":"abc","TenId":11,"Name":"tt1 Workspace","Description":"testtenant Workspace (System Generated)","Tags":[]}]
But when using workspace class - it's returning -
[Id=null, TenId=null, Name=null, Description=null, Tags=null, getId()=null, getTenId()=null, getName()=null, getDescription()=null, getTags()=null]
So is using Workspace[].class would be the right method ?
Replace your static RestTemplate restTemplate = new RestTemplate(); variable for a real bean:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
#Configuration
public class app1 {
//remove this variable
//static RestTemplate restTemplate = new RestTemplate();
static String url = "your_url";
public static void main(String[] args) {
SpringApplication.run(app1.class, args);
//getCallSample();
}
//create a proper RestTemplate bean
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
//add a converter so you can unmarshall the json content
MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
//this is an example to set an ObjectMapper instance
//you can define a bean to configure the ObjectMapper
//with specific details like avoid unmarshalling unknown fields
converter.setObjectMapper(new ObjectMapper());
restTemplate.getMessageConverters().add(converter);
return restTemplate;
}
}
Now, in the method you're using the rest template. Get it from Spring's application context rather than using your own static bean. Example:
#Component
public class WorkspaceService {
private final RestTemplate restTemplate;
public WorkspaceService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public List<Workspace> getWorkspaces() {
ResponseEntity<List<Workspace>> response = restTemplate.exchange(
url,
HttpMethod.GET,
requestEntity,
new ParameterizedTypeReference<List<Workspace>>(){});
List<Workspace> employees = response.getBody();
employees.stream().forEach(entry -> System.out.println(entry.getId() + ": " + entry.getName()));
return employees;
}
}
Now you can use this bean in your components. For example, if you want to use it in main class:
#Configuration
public class app1 {
static String url = "your_url";
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(app1.class, args);
WorkspaceService ws = ctx.getBean(WorkspaceService.class);
ws.getWorkspaces();
}
//create a proper bean
#Bean
public RestTemplate restTemplate() {
/* code from above... */
}
}
The issue is resolved by changing the Pojo class (used this to get the class https://json2csharp.com/code-converters/json-to-pojo) to
public class Root{
#JsonProperty("Id")
public String id;
#JsonProperty("TenantId")
public int tenantId;
#JsonProperty("Name")
public String name;
#JsonProperty("Description")
public String description;
#JsonProperty("Tags")
public ArrayList<Object> tags;
}
and the code is used
ResponseEntity<Workspace[]> responseNew = restTemplate
.exchange(
url,
HttpMethod.GET,
requestEntity,
Workspace[].class);
Workspace [] employees1 = responseNew.getBody();
list.stream().forEach(entry -> System.out.println(entry.getDescription()+": "+ entry.getId() + ": " + entry.getName()));
Thanks for the responses , got to learn a lot from the answers

RestTemplate Junit: Cannot invoke "org.springframework.http.ResponseEntity.getBody()" because "response" is null

I have a spring boot application that makes an API hit of other internal projects using the rest template which works fine and I am writing unit test cases for it but test case if failing due to Unexpected exception thrown: java.lang.NullPointerException: Cannot invoke "org.springframework.http.ResponseEntity.getBody()" because "response" is null
Service
#Service
#Slf4j
public class DynamoDBServiceImpl implements DynamoDBService {
private final RestTemplate restTemplate;
private final HttpHeaders httpHeaders;
private final String jwtHeader;
private final String apiKeyHeader;
private final String dynamodbSysApiKey;
private final String fetchAdminGroupUrl;
public DynamoDBServiceImpl(
RestTemplate restTemplate,
HttpHeaders httpHeaders,
#Value("${header-name.jwt}") String jwtHeader,
#Value("${header-name.api-key}") String apiKeyHeader,
#Value("${dynamodb-sys-api-key}") String dynamodbSysApiKey,
#Value("${dynamodb-fetch-admin-group-url}") String fetchAdminGroupUrl) {
this.restTemplate = restTemplate;
this.httpHeaders = httpHeaders;
this.jwtHeader = jwtHeader;
this.apiKeyHeader = apiKeyHeader;
this.dynamodbSysApiKey = dynamodbSysApiKey;
this.fetchAdminGroupUrl = fetchAdminGroupUrl;
}
#Override
public List<AdminGroupDTO> getAllAdminGroups() {
log.debug("Request to get admin group details with url : {}", fetchAdminGroupUrl);
httpHeaders.set("Accept", MediaType.APPLICATION_JSON_VALUE);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setBearerAuth(CommonUtils.getHeaderFromCurrentHttpRequest(jwtHeader));
httpHeaders.set(apiKeyHeader, dynamodbSysApiKey);
HttpEntity<AdminGroupDTO> request = new HttpEntity<>(httpHeaders);
ResponseEntity<List<AdminGroupDTO>> response =
restTemplate.exchange(fetchAdminGroupUrl, HttpMethod.GET, request, new ParameterizedTypeReference<List<AdminGroupDTO>>() {});
return response.getBody();
}
}
Test
#SpringBootTest(classes = Application.class)
public class DynamoDBServiceTest {
private final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
private final HttpHeaders httpHeaders = new HttpHeaders();
private final DynamoDBServiceImpl dynamoDBService =
new DynamoDBServiceImpl(
restTemplate, httpHeaders, "Authorization" , "Api-Key", "fake", "https://fake.com");
#Test
void testGetAllAdminGroups() {
List<AdminGroupDTO> adminGroupDTOList = new ArrayList<>();
AdminGroupDTO adminGroupDTO = new AdminGroupDTO();
adminGroupDTO.setAdminGroupId(1L);
adminGroupDTO.setAdminGroupName("fake");
adminGroupDTO.setCountryName("fake");
adminGroupDTOList.add(adminGroupDTO);
ResponseEntity<List<AdminGroupDTO>> responseEntity = new ResponseEntity<>(adminGroupDTOList, HttpStatus.OK);
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<List<AdminGroupDTO>>>any()))
.thenReturn(responseEntity);
assertDoesNotThrow(dynamoDBService::getAllAdminGroups);
}
}
Replace this line of code :
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<List<AdminGroupDTO>>>any()))
.thenReturn(responseEntity);
With this :
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.eq(new ParameterizedTypeReference<List<AdminGroupDTO>>() {}))
)
.thenReturn(responseEntity);
And excepption should dissapear.
For futher details you can check this post.

How to test restclient using RestTemplate and JUnit?

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.

How to test injected mock with try-catch

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 = []

Categories