I wanted to mock the RestTemplate's result, but with my code below, it always went to do the Http. Can someone please advise? I think I have parts that I did wrongly, please help.
UserServiceTest.java
#RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceTest(){
#InjectMock
#Autowired
UserService userservice;
#Mock
RestTemplate restTemplate;
#Value(${aa.bb.cc})
private String getfromapplicationpropertiesVal;
#Test
public void test1(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String jsonBody = null;
HttpEntity<String> entity = new HttpEntity<>(jsonBody, headers);
String textContent = "result from junit";
ResponseEntity<String> response = new ResponseEntity<>(textContent, HttpStatus.OK);
String url = "http://localhost:8080/test/test.txt";
doReturn(response).when(restTemplate).exchange(
eq(url),
any(HttpMethod.class),
any(HttpEntity.class),
any(Class.class)
);
userservice.test();
}
}
UserService.java
#Service
public class UserService{
#Autowired
HttpHelperService httpHelperService;
public void test(){
String url = "http://localhost:8080/test/test.txt";
String response = httpHelperService.cal(url, HttpMethod.GET);
System.out.println(response);
}
}
HttpHelperService.java
#Service
public class HttpHelperService{
#Autowired
private RestTemplate restTemplate;
public String cal(String url, HttpMethod httpMethod){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String jsonBody = null;
HttpEntity<String> entity = new HttpEntity<>(jsonBody, headers);
String response = restTemplate.exchange(url, httpMethod, entity, String.class); //This part kept calling http when run the #Test
}
}
RestTemplateConfig
#Configuration
public class RestTemplateConfig{
#Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
Recommendation in these type of scenario is to use MockRestServiceServer & not mocking restTemplate.
You might do something like this:
#Mock
RestTemplate restTemplate;
#Autowired
MockRestServiceServer mockServer;
#BeforeEach
public void init(){
mockServer = MockRestServiceServer.createServer(restTemplate); //initialization
}
And then invoke mock for your service something like :
mockServer.expect(requestTo(url))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("successOperation"));
-Alternatively, you can just mock your test() method:
when(mockedHttpHelperService).cal(anyString(),HttpMethod.GET).thenReturn("success");
Related
I am not able to run the restTemplate whenever I run the unit test. Is there anything I need to set in the Unit Test class?
http://localhost:8082/test is another program running, whenever I run the unit test, it will get stuck at the restTemplate.postForEntity and keep rolling. Can anyone advise?
I don't wish to mock http://localhost:8082/test
Below is my source code.
UserService.java
#Service
public class UserService{
public void processUserInfo(User user){
HttpHelperService service = new HttpHelperService();
service.callOthers();
}
}
HttpHelperService.java
#Service
public class HttpHelperService{
#Autowired
RestTemplate restTemplate;
String url = "http://localhost:8082/test"
public void callOthers(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new MultiValueMap<String, Object>();
body.add("param1", "test");
body.add("param2", "test1234");
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
restTemplate.postForEntity(url, requestEntity, String.class); //when run the unit test (AppTest), it will get stuck here.
}
}
My unit test below
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class AppTest{
#Autowired
UserService userService;
#Test
public void test1(){
userService.processUserInfo();
}
}
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 have tried many ways to mock the restTemplate exchange, but the mock is not happening, the actual exchange keeps on calling and gives me url not valid exception.
My CallRestService method is below
public class Util{
public static ResponseEntity<String> callRestService(JSONObject reqJsonObj,HttpHeaders headers, String url, HttpMethod method, boolean isAuto){
ResponseEntity<String> re=null;
try{
HttpEntity<String> entity=null;
entity=new HttpEntity<>(String.valueOf(reqJsonObj),headers);
RestTemplate restTemplate= new RestTemplate();
re=restTemplate.exchange(url,method,entity,String.class);
}catch(Exception e){
System.out.println(e);
}
}
}
And My Mock is below:
public class UtilTest{
#InjectMocks
Util util;
#Mock
RestTemplate restTemplate;
ResponseEntity res=mock(ResponseEntity.class);
#Test
public void test(){
//ResponseEntity<String> entity=new ResponseEntity<String>("anySt",HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(HttpEntity.class),
ArgumentMatchers.<Class<String>>any())
).thenReturn(res);
Util.callRestService(json,headers,url,HttpMethod.POST,false);
}
}
I also tried to return the commented response Entity. but always got exception in exchange.
What i understand about mocking is the actual exchange method will not be called, then how am i getting resttemplate exchange exception.
if any inputs required pls comment.
Thanks for the support.
UPDATE:
I tried with changing the static method to non static but kept the test case as is. But i get the same error
The problem is resolved when I update the method to non static and move the Resttemplate declaration outside the method.
Updated code is below, if anyone found it useful.
Method changes :
public class Util{
private RestTemplate restTemplate= new RestTemplate();
public ResponseEntity<String> callRestService(JSONObject reqJsonObj,HttpHeaders headers, String url, HttpMethod method, boolean isAuto){
ResponseEntity<String> re=null;
try{
HttpEntity<String> entity=null;
entity=new HttpEntity<>(String.valueOf(reqJsonObj),headers);
re=restTemplate.exchange(url,method,entity,String.class);
}catch(Exception e){
System.out.println(e);
}
}
}
And the mock with verify :
public class UtilTest{
#InjectMocks
Util util;
#Mock
RestTemplate restTemplate;
#Test
public void test(){
ResponseEntity<String> entity=new ResponseEntity<String>("anySt",HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(HttpEntity.class),
ArgumentMatchers.<Class<String>>any())
).thenReturn(entity);
Util.callRestService(json,headers,url,HttpMethod.POST,false);
Mockito.verify(restTemplate,times(1)).exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(HttpEntity.class),
ArgumentMatchers.<Class<String>>any())
)
}
}
I tried to write test cases for the rest calls in my service which is calling the 3rd party api.
#RunWith(MockitoJUnitRunner.class)
public class ForceServiceTest {
private ForceService forceService;
#Mock
private ForceServiceConfig config;
#Mock
private RestTemplate restTemplate;
#Before
public void setup() {
forceService = new ForceService(config);
}
#Test
public void apiCall_valid() throws JSONException {
HttpHeaders headers = new HttpHeaders();
headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
headers.set(ACCEPT, APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(
"id=null",
headers);
config.authTokenUrl = "https://ex...com/..";
Mockito.when(restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity, Access.class)).thenReturn(null);
// Mockito.when(any()).thenReturn(null);
forceService.apiCall();
}
}
#Component
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate = new RestTemplate();
public ForceService(ForceServiceConfig config) {
this.config = config;
}
private String apiCall() {
HttpHeaders headers = new HttpHeaders();
headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
headers.set(ACCEPT, APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(
"&id=" + config.id,
headers);
ResponseEntity<Access> response = restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity,
Access.class);
return response.getBody().token_type + " " + response.getBody().access_token;
}
}
I get the following error:
org.springframework.web.client.HttpClientErrorException: 404 Not Found
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
It is calling the api in test class, which I do not want to happen.
I need to mock the resttemplate call of 3rd party api. How can I do it without being actually calling the api?
This is the problem
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate = new RestTemplate(); // HERE
you are creating new REAL rest template. What you probably want is to
Use mock that you created in wrapping test class
Use real rest template and SPY on it.
A don't know is that your actualy structure (1 file 2 classes) but it is safe to assume it is not and in any case you can simply pass RestTemplate as ctor argument. So
#Component
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate;
public ForceService(ForceServiceConfig config,RestTemplate restTemplate) {
this.restTemplate=restTemplate;
this.config = config;
}
and
#Before
public void setup() {
forceService = new ForceService(config,restTemplate);
}
Now if you want to RestTemplate to be just a stub that does literally nothing and return null on any calls if not instructed otherwiser - leave it as #Mock.
If you want however, to allow it to work normally and only intercept some specific method calls and stub responses, use spy.
#Mock
private RestTemplate restTemplate;
private RestTemplate restTemplate=Mockito.mock(RestTemplate.class)
or
private RestTemplate restTemplate=Mockito.spy(new RestTemplate());
My Service class is below, followed by its test -
#Service
public class MyServiceImpl implements MyService {
#Autowired
private RestTemplate restTemplate;
#Override
public StudentInfo getStudentInfo(String name) {
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity entity = new HttpEntity(headers);
StudentInfo student = null;
URI uri = new URI("http:\\someurl.com");
ResponseEntity<String> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, entity,
String.class);
if (responseEntity.getStatusCode().equals(HttpStatus.NO_CONTENT)) {
throw new Exception("Student absent");
}else {
ObjectMapper mapper = new ObjectMapper();
StudentInfo student = mapper.readValue(responseEntity.getBody(), StudentInfo.class);
}
return student;
}
}
Test class: In my test class below, I see ResponseEntity object as null while debugging which causes a NPE.
#RunWith(MockitoJUnitRunner.class)
public class MyServiceImplTest {
#InjectMocks
private MyService service = new MyServiceImpl();
#Mock
private RestTemplate restTemplate;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testStudentGetterResponse() {
ResponseEntity<String> mockEntity = Mockito.spy(new ResponseEntity({"id" : 1, "name" : "Rutzen"}, HttpStatus.OK));
doReturn(mockEntity).when(restTemplate).exchange(any(URI.class), any(HttpMethod.class), any(ResponseEntity.class),
any(Class.class));
StudentInfo info = service.getStudentInfo("testuser");
Assert.assertNotNull(info);
}
}
When I debug the test, I get a null value for responseEntity at the following line in the main service class -
ResponseEntity<String> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, entity,
String.class);
This instruction ...
doReturn(mockEntity).when(restTemplate).exchange(
any(URI.class),
any(HttpMethod.class),
any(ResponseEntity.class),
any(Class.class)
);
... should be replaced with:
doReturn(mockEntity).when(restTemplate).exchange(
any(URI.class),
any(HttpMethod.class),
any(HttpEntity.class),
any(Class.class)
);
Because getStudentInfo() creates an instance of HttpEntity (not ResponseEntity) which is then passed to the restTemplate.exchange() invocation.
As the accepted answer is correct. I'm adding something to the already accepted answer.
It seems little bit strange but i fixed the issue by seeing the accepted answer and the comment is added by the user who raised the question.
Replace this
doReturn(mockEntity).when(restTemplate).exchange(
any(URI.class),
any(HttpMethod.class),
any(ResponseEntity.class),
any(Class.class)
);
With,
doReturn(mockEntity).when(restTemplate).exchange(
any(URI.class),
any(HttpMethod.class),
any(HttpEntity.class),
any(Class.class)
);
And if you are still getting the error then don't use multiple lines. Use only one line and replace it like below.
doReturn(mockEntity).when(restTemplate).exchange(any(URI.class), any(HttpMethod.class), any(HttpEntity.class), any(Class.class)
);
ResponseEntity<String> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, entity,
String.class);
I will not work in case of String[]
like
ResponseEntity<String[]> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, entity,
String[].class);