Mocking RestTemplate.exchange() is not working. The Response of restTemplate.exchange() mocking gives null value at BDS Adapter class. My test case is failing with null pointer exception in BDSAdapter class. (response.getStatusCodeValue() gives null pointer exception..Mockito hints
Unused... -> at com..policydetails_adapters.BDSAdapterTest.getInsuranceHoldings(BDSAdapterTest.java:56)
[MockitoHint] ...args ok? -> at com.policydetails_adapters.BDSAdapter.fetchInsuranceDetails(BDSAdapter.java:77)
Below are my classes.
Test Class:
#RunWith(MockitoJUnitRunner.class)
public class BDSAdapterTest {
#InjectMocks
private BDSAdapter bdsAdapter;
#Mock
private BDSFetchInsuranceDetailsRequest bdsFetchInsuranceDetailsRequest;
#Mock
private RestTemplate restTemplate;
#Mock
Environment env;
#Test
public void getInsuranceHoldings() throws InsuranceHoldingsException {
Mockito.when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<BDSFetchInsuranceDetailsResponse>>any()))
.thenReturn(sampleBDSCustomerInsuranceHoldings());
BDSFetchInsuranceDetailsResponse bdsFetchInsuranceDetailsResponse = bdsAdapter.fetchInsuranceDetails("MBSG", "S6564318I", "1234", "007");
assertNotNull("response is not null", bdsFetchInsuranceDetailsResponse);
}
public static ResponseEntity<BDSFetchInsuranceDetailsResponse> sampleBDSCustomerInsuranceHoldings() {
BDSFetchInsuranceDetailsResponse bdsResponse = new BDSFetchInsuranceDetailsResponse();
Header header = new Header();
header.setChannelId("MBSG");
header.setMsgId("4aBE50ZrQtjVuXfTyALJ");
bdsResponse.setHeader(header);
ResponseEntity<BDSFetchInsuranceDetailsResponse> response = new ResponseEntity<BDSFetchInsuranceDetailsResponse>(bdsResponse, HttpStatus.ACCEPTED);
return response;
}
}
My Actual class
#Component
public class BDSAdapter {
#Autowired
RestTemplate restTemplate;
#Autowired
BDSFetchInsuranceDetailsRequest bdsFetchInsuranceDetailsRequest;
#Autowired
Environment env;
public BDSFetchInsuranceDetailsResponse fetchInsuranceDetails(String channelId, String customerId,
String insurerCode, String policyNumber) throws InsuranceHoldingsException {
BDSFetchInsuranceDetailsResponse bdsFetchInsuranceDetailsResponse = null;
try {
logger.info("Inside BDSAdapter::fetchInsuranceDetails");
Header header = new Header();
header.setMsgId(RandomStringUtils.randomAlphanumeric(20));
header.setChannelId(channelId);
bdsFetchInsuranceDetailsRequest.setHeader(header);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<BDSFetchInsuranceDetailsRequest> requestEntity = new HttpEntity<>(bdsFetchInsuranceDetailsRequest, requestHeaders);
ResponseEntity<BDSFetchInsuranceDetailsResponse> response = restTemplate.exchange(env.getProperty("bds_fetch_insurance_details_url"),HttpMethod.POST, requestEntity, BDSFetchInsuranceDetailsResponse.class);
if(response.getStatusCodeValue() == 204) {
throw new InsuranceHoldingsException(response.getStatusCode().toString(), "No Content");
}
bdsFetchInsuranceDetailsResponse = response.getBody();
} catch (Exception e) {
e.printStackTrace();
}
}
return bdsFetchInsuranceDetailsResponse;
}
}
Perhaps, because mock of BDSFetchInsuranceDetailsResponse will return not a class of BDSFetchInsuranceDetailsResponse but some mockclass
Arugments inside restTemplate.exchange() method should match. In this code env.getProperty("bds_fetch_insurance_details_url") returns null which does not match with String. So it gives null response.
Added the below statment before When(restTemplate.exchange(-,-,-,-).thenReturn(myResponse). It is working
`Mockito.when(env.getProperty("bds_fetch_insurance_details_url")).thenReturn("anyString")`;
Related
I am creating unit test for my service class:
#Service
#Slf4j
public class SsaOpeningService {
#Autowired
private MockDataInitializer mockDataInitializer;
#Qualifier("restHttpsTemplateClient")
#Autowired
private RestTemplate restTemplate;
#Value("${acc-opening-casa.open-ssa}")
private String openSSAaccountUrl;
public CompletableFuture<AccountData> openSsa(
ApplicationDto items,
HttpHeaders headers,
BigInteger cifNo) {
log.info("Initializing headers");
HeaderRequest headerRequest = new HeaderRequest();
HttpHeaders header = headerRequest.initHeader(headers);
CurrentAcctReqBody request = CurrentAcctReqBody.builder()
.cifNo(cifNo)
.currencyType(SGD_CURRENCY)
.acName1(items.getApplicationData().getPersonalDetail().getName())
.productType(SSA_PRODUCT_CODE)
.noOfAccountHolders(BigInteger.ONE)
.accountType(ACC_TYPE)
.transactionRefNo(mockDataInitializer.randomIntInString(9))
.build();
log.info("Setting up entity for calling SSA opening.....");
HttpEntity<CurrentAcctReqBody> entity = new HttpEntity<>(request, header);
CurrentAcctResBody result = null;
try {
result = restTemplate
.postForObject(openSSAaccountUrl, entity, CurrentAcctResBody.class);
} catch (Exception e) {
log.info(e.getMessage());
}
System.out.println(14527);
System.out.println(result);
if(result !=null && result.getError()==null) {
AccountData accountData = AccountData.builder().build();
BeanUtils.copyProperties(result.getRbkAccountDetail(), accountData);
return CompletableFuture.completedFuture(accountData);
}
return null;
}
}
My test class:
#SpringBootTest
#RunWith(SpringRunner.class)
class SsaOpeningServiceTest {
#InjectMocks private SsaOpeningService ssaOpeningService;
#Autowired private MockDataInitializer dataInitializer;
#Mock private MockDataInitializer mockDataInitializer;
private static HttpHeaders headers = new HttpHeaders();
private static HeaderRequest ekycHeaderRequest = new HeaderRequest();
#BeforeAll
public static void init() {
headers = ekycHeaderRequest.initHeader();
}
#Qualifier("restHttpsTemplateClient")
#Mock private RestTemplate restTemplate;
#Test
void createSsa() throws IOException {
CurrentAcctResBody result = JSONUtils
.getObjectFromJson(DCResourceLoader.getResourceAsString("casa/ssa-res.json"), CurrentAcctResBody.class);
ApplicationDto items = ApplicationDto.builder().build();
Application application = dataInitializer.initialize();
BeanUtils.copyProperties(application, items);
when(restTemplate.postForObject(
any(String.class),
eq(HttpEntity.class),
eq(CurrentAcctResBody.class)))
.thenReturn(result);
System.out.println(1452);
System.out.println(result);
when(mockDataInitializer.randomIntInString(any(Integer.class)))
.thenReturn(dataInitializer.randomIntInString(9));
assertThat(ssaOpeningService.openSsa(items, headers, any(BigInteger.class))).isNull();
}
}
I have mocked my RestTemplate to return me the result I want. Problem is, it is not giving me the expected result. I have printed the result in both test class and service class. But in the service class it is always giving me null. I tried to give the most generic parameter when mocking but still doesnt work. The rest is working fine when running unit test EXCEPT for this part. Need assist on this. Thanks all!
#RunWith(MockitoJUnitRunner.class)
class SsaOpeningServiceTest {
#InjectMocks
private SsaOpeningService ssaOpeningService;
#Mock
private MockDataInitializer mockDataInitializer;
#Mock
private RestTemplate restTemplate;
#Test
void createSsa() throws IOException {
// some code
Mockito.when(restTemplate.postForObject(
nullable(String.class),
any(HttpEntity.class),
eq(CurrentAcctResBody.class)))
.thenReturn(result);
// some code
}
}
I am using the SpringRunner to run the Junit mockito test case , below is the class , i was trying to write the test case , but getting null object
public class AccountManager {
public String getToken() throws Exception {
#Autowired
RestClient restClient;
String auth = apiUserPrefix + apiUserName + BatchJobConstants.COLON + apiUserPassword;
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(StandardCharsets.UTF_8));
String authHeader = BatchJobConstants.BASIC_SPACE + new String(encodedAuth);
String token= null;
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
data.add("grant_type", "client_credential");
String accManagerUrl = accManagerHost+":"+accManagerPort+"/"+accManagerResPath;
RestResponseObject responseObject = null;
try {
responseObject = restClient.execute(accManagerUrl, HttpMethod.POST, data, String.class, authHeader);
if (responseObject != null && responseObject.getPayload() != null && responseObject.getStatusCode() == HttpStatus.OK) {
JsonElement responseJson = (JsonElement) responseObject.getPayload();
if (responseJson.isJsonObject()) {
token= responseJson.getAsJsonObject().get(BatchJobConstants.ACCESS_TOKEN).getAsString();
}catch(RunTimeException e) {
//e
}
return token;
}
//Junit test case
#RunWith(SpringRunner.class)
public class AccountManagerTest {
#InjectMocks
AccountManager accountManager;
#Mock
RestClient restClient;
#Test
public void getTokenAccMgrSucess() throws Exception {
RestResponseObject restResponseObject = Mockito.mock(RestResponseObject.class);
Mockito.when(restClient.execute(Mockito.anyString(), Mockito.any(HttpMethod.class),
Mockito.anyString(), Mockito.eq(String.class), Mockito.anyString())).thenReturn(restResponseObject);
String token = accountManagerTokenProvider.getToken();
Assert.assertEquals("Token value {} ", null, token);
}
}
But still the below code return null value even after mocking this, can you please help how to mock this.
responseObject = restClient.execute(accManagerUrl, HttpMethod.POST, data, String.class, authHeader);
Note: Only Mockito needs to use no powermockito
For Autowired fields you not only have to mock it but should bind the mocked class to the spring context. You have two options :
1. Mark the mocked class as primary bean
#Configuration
public class TestConfiguration {
#Bean
#Primary
public RestClient restClient() {
return Mockito.mock(RestClient.class);
}
}
2.Use #MockBean annotation
#MockBean
RestClient restClient;
More on this :
https://www.baeldung.com/injecting-mocks-in-spring
https://www.baeldung.com/java-spring-mockito-mock-mockbean
Finally worked with mockito only just user any() instead of anyString(), since the Object is not matching with string only
Mockito.when(restClient.execute(Mockito.any(), Mockito.any(HttpMethod.class),
Mockito.any(), Mockito.eq(String.class), Mockito.any())).thenReturn(restResponseObject);
I'm sending a response to another web service to create an user. If the user already exists it sends back the 409 response. I'm using RestTemplate like so:
#PostMapping("/todos/{toDoNoteId}/users")
public ResponseEntity <String> postUser(#RequestBody User user, #PathVariable int toDoNoteId, UriComponentsBuilder builder)throws HttpMessageNotReadableException, ParseException{
RestTemplate restTemplate = new RestTemplate();
final String uri = "http://friend:5000/users";
try {
ResponseEntity<String> result = restTemplate.postForEntity(uri, user, String.class);
return result;
}
catch (HttpClientErrorException ex) {
return ResponseEntity.status(ex.getRawStatusCode()).headers(ex.getResponseHeaders())
.body(ex.getResponseBodyAsString());
}
}
While catching an exception somewhat works (in the catch block i can access the status code and body), is there a way to access it without exceptions something similar like this:
#PostMapping("/todos/{toDoNoteId}/users")
public ResponseEntity <String> postUser(#RequestBody User user, #PathVariable int toDoNoteId, UriComponentsBuilder builder)throws HttpMessageNotReadableException, ParseException{
RestTemplate restTemplate = new RestTemplate();
final String uri = "http://friend:5000/users";
ResponseEntity<String> result = restTemplate.postForEntity(uri, user, String.class);
if(result.getStatusCode()=="409"){
// do something
}
else{
// do something else
}
return result;
}
Have you been check the ExceptionHandler? When exception throws, ExceptionHandler handles it.
For example:
#ControllerAdvice()
public class CustomExceptionHandler {
private static final Logger logger = LogManager.getLogger("CustomExceptionHandler");
#ExceptionHandler(YourException.class)
public ResponseEntity handleYourException(HttpServletRequest request, YourException ex) {
return ResponseEntity.ok("");
}
#ExceptionHandler(Exception.class)
public ResponseEntity handleException(HttpServletRequest request, Exception ex) {
logExp("Exception", request, ex);
//return new ResponseEntity<>();
return null;
}
}
You can create your own custom resttemplate and define exception handler. Here is a code snippet.
#Configuration
public class CustomRestTemplate extends RestTemplate {
#Autowired
private CustomErrorHandler customErrorHandler;
#PostConstruct
public void init() {
this.setErrorHandler(customErrorHandler);
}
}
#Component
public class CustomErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
if(response.getStatusCode() != "409"){
return true;
}else {
return false;
}
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
String responseBody = response.getBody();//Pls read from InputStream and create write into String
JSONObject jsonObj = new JSONObject(result);
JSONArray jsonArray = new JSONArray();
jsonObj.put("status", response.getStatusCode());
jsonObj.put("body", responseBody );
jsonArray.put(jsonObj);
responseString = jsonArray.get(0).toString();
throw new MyException(responseString );
}
}
class MyException throw RuntimeException {
public MyException (String message) {
super(message);
}
}
So, your class will changed to
#PostMapping("/todos/{toDoNoteId}/users")
public ResponseEntity <String> postUser(#RequestBody User user, #PathVariable int toDoNoteId, UriComponentsBuilder builder)throws HttpMessageNotReadableException, ParseException{
CustomRestTemplate restTemplate = new CustomRestTemplate ();
final String uri = "http://friend:5000/users";
ResponseEntity<String> result = restTemplate.postForEntity(uri, user, String.class);
return result
}
My service class code is as below:
public class MyServiceImpl implements MegatillAccessService {
#Autowired
RestTemplate restTemplate;
#Value("${api.key}")
private String apiKey;
#Value("${customers.url}")
private String postUrl;
#Override
public String pushCustomerData(List<Customer> listOfcustomers, String storeId) throws MyServiceException {
Set<Customer> setOfCustomers = new HashSet<>(listOfcustomers);
int noOfCustomersLoadedSuccessfully =0;
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("apiKey", apiKey);
headers.add("Content-Type", "application/json");
headers.add("storeId", storeId);
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
for(Customer customer: setOfCustomers){
HttpEntity<Customer> request = new HttpEntity<Customer>(customer, headers);
CustomerDataDto customerDataDto = null;
try {
customerDataDto = restTemplate.exchange(postUrl, HttpMethod.POST, request, CustomerDataDto.class).getBody();
}
catch (HttpClientErrorException ex) {
if (ex.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
log.error("The customers service is not available to load data: "+ ex.getResponseBodyAsString(), ex);
throw new MyServiceException("The customers service is not available to load data",new RuntimeException(ex));
}
else{
log.warn("Error for customer with alias: "+customer.getAlias() +" with message: "+ ex.getResponseBodyAsString(), ex);
if(!ex.getResponseBodyAsString().contains("already found for this shop")){
throw new MyServiceException("An error occurred while calling the customers service with status code "+ex.getStatusCode(),new RuntimeException(ex));
}
}
}
catch(Exception e){
throw new MyServiceException("An error occurred while calling the customers service: ",new RuntimeException(e));
}
if(null != customerDataDto) {
noOfCustomersLoadedSuccessfully++;
log.debug("--------Data posted successfully for: ---------"+customerDataDto.getAlias());
}
}
String messageToReturn = "No. of unique customers from source: "+setOfCustomers.size()+". No. of customers loaded to destination without error: "+noOfCustomersLoadedSuccessfully;
return messageToReturn;
}
}
My Test class is as below:
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class MyServiceTest {
#InjectMocks
private MyService myService = new MyServiceImpl();
#Mock
RestTemplate restTemplate;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
initliaizeModel();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
}
#Test
public void pushAllRecords(){
Mockito.when(restTemplate.exchange(Matchers.anyString(), Matchers.any(HttpMethod.class), Matchers.<HttpEntity<?>> any(), Matchers.<Class<CustomerDataDto>> any()).getBody()).thenReturn(customerDataDto);
/*Mockito.when(restTemplate.exchange(Mockito.anyString(),
Mockito.<HttpMethod> eq(HttpMethod.POST),
Matchers.<HttpEntity<?>> any(),
Mockito.<Class<CustomerDataDto>> any()).getBody()).thenReturn(customerDataDto);*/
String resultReturned = myService.pushCustomerData(customers,"1235");
assertEquals(resultReturned, "No. of unique customers from source: 2. No. of customers loaded to destination without error: 2");
}
}
While running the test, I am getting NullPointerException at the line where I am giving the Mockito.when and thenReturn condition. I tried many combinations but it is still giving NPE. I can't even reach the method invocation.Can you please let me know where am I going wrong?
You get NullPointerException because you are doing too much in your Mockito.when. Your code inside when (shorter version):
restTemplate.exchange(args).getBody()
You are trying to mock getBody() but it is called on exchange(args). And what does exchange(args) returns? Mockito doesn't know what it should return and you didn't specify that so by default it returns null.
That's why you get NPE.
To fix this you can either do mocking step by step, ie.
ResponseEntity re = Mockito.when(exchange.getBody()).thenReturn(customerDataDto);
Mockito.when(restTemplate.exchange()).thenReturn(re);
or specify mock to return deep stubs, like that (if you want to use annotations):
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
RestTemplate restTemplate;
I have a problem with test of my method which use RestOperation exchange method. When i'm trying to mock response i get an error:
ResponseEntity cannot be returned by toString()
toString() should return String
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Below is my class, which i want to test
#Component
public class AuthGateway {
#Autowired
AuthorizedHttpEntityFactory authorizedHttpEntityFactory;
#Autowired
RestOperations restOperations;
#Value("${authServer.host}:${authServer.port}/${authServer.validateToken.path}")
private String authPath;
#Value("${authServer.host}:${authServer.port}/basic/check")
private String basicAuthPath;
#Value("${authServer.tokenName}")
private String tokenName;
#Value("${authServer.host}:${authServer.port}/user")
private String userProfileUrl;
#Value("${authServer.host}:${authServer.port}/homeowner")
private String homeownerUrl;
public UnpackedToken authenticate(String token) throws ResourceAccessException, AuthException {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add(tokenName, token);
HttpEntity httpEntity = authorizedHttpEntityFactory.getAuthorizedHttpEntity(formData);
Map map = null;
try {
ResponseEntity<Map> entity = restOperations.exchange(authPath, HttpMethod.POST,
httpEntity, Map.class);
map = entity.getBody();
} catch (RestClientException e) {
processError(e);
}
#SuppressWarnings("unchecked")
Map<String, Object> result = map;
return new UnpackedToken(result);
}
and Test class
#RunWith(MockitoJUnitRunner.class)
public class AuthGatewayTest {
private ResponseEntity<Map> entity;
#Mock
private RestOperations restOperations;
#Mock
private LinkedMultiValueMap linkedMultiValueMap;
#Mock
private AuthorizedHttpEntityFactory authorizedHttpEntityFactory;
#Autowired
#InjectMocks
private AuthGateway authGateway;
private String token;
private Integer userId = 1;
private String role = "ROLE_PRO";
private UnpackedToken unpackedToken;
private Map<String, Object> map;
private RestClientException restClientException;
private AuthException authException;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
restClientException = new RestClientException("Test exception");
authException = new AuthException("Test exception");
token = "token-token";
map = new HashMap<>();
map.put("UserId", userId);
map.put("authorities", Collections.singletonList(role));
entity = new ResponseEntity<>(map, HttpStatus.OK);
unpackedToken = new UnpackedToken(map);
}
#Test
public void testAuthenticateSuccessfully() throws Exception {
HttpEntity httpEntity = new HttpEntity("body");
Mockito.when(authorizedHttpEntityFactory.getAuthorizedHttpEntity(any(Map.class))).thenReturn(httpEntity);
Mockito.when(restOperations.exchange(
Mockito.anyString(), Mockito.<HttpMethod>any(), Mockito.<HttpEntity<?>>any(), Mockito.<Class<Map>>any())).
thenReturn(entity);
Mockito.doNothing().when(linkedMultiValueMap).add(any(), any());
assertEquals(this.unpackedToken, authGateway.authenticate(token));
}
What is wrong with this mock?
Weird, when i change mock line into:
Mockito.when(restOperations.exchange(
Mockito.anyString(), Mockito.<HttpMethod>any(), Mockito.<HttpEntity<?>>any(), Mockito.<Class<Map>>any())).
thenReturn(new ResponseEntity<>(map, HttpStatus.OK));
then it starts working properly...