Avoid making actual Rest call in RestTemplate Junit - spring

Below is my EmployeeService using RestTemplate for which i have written Junit which is working .But the problem is my JUNIT is making an actual call to Rest end point.How can i avoid making an actual rest call?
#Service
public class EmployeeService {
private RestTemplate restTemplate = new RestTemplate();
public Employee getEmployee(String id) {
ResponseEntity resp =
restTemplate.getForEntity("http://localhost:8080/employee/" + id, Employee.class);
return resp.getStatusCode() == HttpStatus.OK ? resp.getBody() : null;
}
}
#RunWith(MockitoJUnitRunner.class)
public class EmployeeServiceTest {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private EmployeeService empService = new EmployeeService();
#Test
public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() {
Employee emp = new Employee(“E001”, "Eric Simmons");
Mockito
.when(restTemplate.getForEntity(
“http://localhost:8080/employee/E001”, Employee.class))
.thenReturn(new ResponseEntity(emp, HttpStatus.OK));
Employee employee = empService.getEmployee(id); **// Actual call happens .How to avoid it.**
Assert.assertEquals(emp, employee);
}
}

You are creating an explicit new RestTemplate();
So, you can not mock it.
An approach would be creating a #Component that performs the actual call.
#Component
public class MyHttpClient {
public ResponseEntity callingMethod(RestTemplate restTemplate, String id) {
restTemplate.getForEntity("http://localhost:8080/employee/" + id, Employee.class);
}
}
So, you call it from the class
#Service
public class EmployeeService {
#Autowired
private myHttpClient MyHttpClient;
private RestTemplate restTemplate = new RestTemplate();
public Employee getEmployee(String id) {
ResponseEntity resp = myHttpClient.callingMethod(restTemplate, id);
...
}
}
From the test you mock the new class and you when it:
#Mock
private MyHttpClientMock myHttpClientMock;
when(myHttpClientMock.callingMethod(Mockito.<RestTemplate> any()).thenReturn(HttpStatus.OK);

you need to change test to mock
public Employee getEmployee(String id)
by doing
doReturn(emp).when(empService).getEmployee(1);//or a wild card

Related

Why Testing Controller not working with #RunWith(SpringRunner.class) & #WebMvcTest annotation?

I am getting the below error when I try to run my controller test. Please can you let me know what I am missing? Why should I add the #ContextConfiguration or #SpringBootTest ?
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
#Controller
public class BasketController {
#Autowired
private BasketService basketService;
#GetMapping(value="/baskets/{basketId}")
public ResponseEntity<BasketDto> getBasket(#PathVariable("basketId") UUID basketId){
Basket basket = basketService.getBasket(basketId);
BasketDto dto = toBasketDto(basket);
ResponseEntity response = ResponseEntity.status(HttpStatus.OK).body(dto);
return response;
}
private BasketDto toBasketDto(Basket basket){
BasketDto dto = new BasketDto(basket.getBasketId(), basket.getItems());
return dto;
}
}
#RunWith(SpringRunner.class)
#WebMvcTest(BasketController.class)
public class BasketControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BasketService basketService;
#Test
public void testGetItemsInBasketSuccessfully() throws Exception {
UUID basketId = UUID.randomUUID();
String URI = "/api/baskets/" + basketId;
Basket mockBasket = new Basket(basketId);
Mockito.when(basketService.getBasket(basketId)).thenReturn(mockBasket);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(URI).accept(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
String actualJson = result.getResponse().getContentAsString();
System.out.println(actualJson);
}
}

How to write Junit test cases for this class

#DeleteMapping(value = "/{id}", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<Void> delete(#PathVariable Long id) {
log.debug("Delete by id Logo : {}", id);
try {
Logo entr = new Logo();
entr.setId(id);
logoRepository.delete(entr);
return ResponseEntity.ok().build();
} catch (Exception x) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
}
For testing controllers we could mock mvc. Inject mock of service layers inside the controller.
Since you have mock for your service you could mock the response that service should return for a transaction using Mockito.when statement.
A sample code will be like.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HelloController.class)
public class HelloWorldTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HelloService service;
#Test
public void testShouldReturnMessage() throws Exception {
when(service.sayHello()).thenReturn("Hello World");
mockMvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.ALL))
.andExpect(MockMvcResultMatchers.status().is(200))
.andExpect(MockMvcResultMatchers.content().string("Hello World"))
.andExpect(MockMvcResultMatchers.header().string("Content-Type", "text/plain;charset=UTF-8"))
.andExpect(MockMvcResultMatchers.header().string("Content-Length", "11"));
}
}
Reference - https://www.nexsoftsys.com/articles/spring-boot-controller-unit-testing.html#:~:text=Unit%20testing%20Spring%20Boot%20controllers%20Technology%20Unit%20testing,is%20to%20validate%20each%20unit%20performs%20as%20designed.

#Mock RestTemplate not returning expected value

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
}
}

How to mock RestTemplate in Springboot app

This question has already been asked. The accepted answer doesn't work for me. Here is my code:-
My service is here:
#Service
public class PlantService {
#Autowired
RestTemplate restTemplate;
static String url = "http://some_url_?Combined_Name=Oak";
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
public String getJson() {
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
return response.getBody();
}
}
My unit test
#RunWith(SpringRunner.class)
class PlantServiceTest {
private PlantService plantService;
#Mock
#Autowired
private RestTemplate restTemplate;
#Before
void setUp() {
MockitoAnnotations.initMocks(this);
plantService = new PlantService();
}
#Test
void testGetJsonString() {
// arrange
String expectedJson = "Some json string";
ResponseEntity mocResponse = mock(ResponseEntity.class);
// act
when(restTemplate.getForEntity("url", String.class)).thenReturn(mocResponse);
String actualJson = plantService.getJson();
// assert
assertSame(expectedJson, actualJson);
}
}
When I debug and step into the actual code. I can see restTemplate is null and throws java.lang.NullPointerException. So how do I unit test this code?
I have tried your code running on my machine.
Please find the test running test class
#RunWith(SpringRunner.class)
class PlantServiceTest {
#InjectMocks
private PlantService plantService;
#Mock
private RestTemplate restTemplate;
String url = "http://some_url_?Combined_Name=Oak";
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
void testGetJsonString() {
// arrange
String expectedJson = "Some json string";
ResponseEntity mocResponse = new ResponseEntity("Some json string", HttpStatus.OK);
// act
when(restTemplate.getForEntity(url, String.class)).thenReturn(mocResponse);
String actualJson = plantService.getJson();
// assert
assertSame(expectedJson, actualJson);
}
}
You can do the following:
Remove #RunWith annotation.
Annontate your test class with #RestClientTest from org.springframework.boot.test.autoconfigure.web.client.RestClientTest.
Use MockRestServiceServer from org.springframework.test.web.client.MockRestServiceServer.
Mock the response of the server when being called in the test method, example:
#RestClientTest
public class MyTest {
#Autowired
private MockRestServiceServer server;
public void test() {
// setup
String expected = "test_value";
server.expect(requestToUriTemplate("/myendpoint"))
.andRespond(withSuccess(myJsonResponse, MediaType.APPLICATION_JSON));
// act
String actual = myClient.fetch(myRequestDto);
// assert
assertThat(actual, equalTo(expected));
server.verify();
}
}
I'm using assertThat from hamcrest, you can use whatever you want to assert the correctness of the result.
You should use constructor injection in your PlantService. For instance:
public class PlantService {
RestTemplate restTemplate;
#Autowired
public PlantService(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
}
And on your test you can just do:
plantService = new PlantService(restTemplate);
^^
yourMock
Your problem is the plantService = new PlantService(); You never inject into this selft created instance.
Solution 1
I usually do it like this:
#InjectMocks
private PlantService plantService = new PlantService();
#Mock
private RestTemplate restTemplate;
Remove the setup method and run with Mockito instead of the SpringRunner.
Solution 2
If you need the SpringRunner you can do the following:
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
plantService = new PlantService();
ReflectionTestUtils.setField(plantService, "restTemplate", restTemplate);
}
As I've worked with JUnit 5 in the last years, I'm not sure about the SpringRunner. In JUnit 5 I can use both extensions (Spring and Mockito at the same time). Maybe this also worked in JUnit 4.

how to create RestController in test directory for SpringBoot Application

Im currently writing integration test for SpringBoot Application .
It's functionality is to receive/send request from outside and forward/receive them to another application(APP_2). So there are two systems which needs to be mocked outside System and APP_2 .
HomeController
#Controller
public class HomeController {
#Autowired
ForwardController forwardController;
#RequestMapping("/")
public #ResponseBody
String greeting() {
return forwardController.processGET().toString();
}
}
ForwardController
#Service
public class ForwardController {
#Autowired
private RestTemplate restTemplate;
#Autowired
private Environment environment;
private ResponseEntity sendRequest(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Object... uriVariables) {
return restTemplate.exchange( url, method, requestEntity, responseType,uriVariables);
}
public ResponseEntity processGET()
{
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<?> entity = new HttpEntity<>(headers);
String app_2_url = environment.getProperty(Constants.APP_2_URL);
ResponseEntity<String> response = sendRequest(app_2_url,HttpMethod.GET,entity,String.class);
return response;
}
}
APP_2_CONTROLLER
#Controller
public class App_2_Controller {
#RequestMapping("/app2Stub")
public #ResponseBody
String greeting() {
return "Hello End of world";
}
}
Test Class which simulates the external request behavior to the system:
HTTP_request_Test
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = Application.class)
public class HttpRequestTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private Environment environment;
#Test
public void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello End of world");
}
}
Here in this test class I'm overriding the properties by having two property file. So when we run test the request would be sent to App_2_Controller ( Mock in my project ) rather than the real App .
QUESTION :
Is there any way to have the APP_2_CONTROLLER inside the test folder ? This is because I don't want to expose the unwanted test endpoint in my Actual application .
Here in the above project , Im changing the URL with properties. Is there a better way to put a controller for the same URL. For simplicity sake lets assume, app_2 url is app.com:9000/serve
Spring already comes with a MockRestServiceServer, that makes this a lot easier so that you don't have to create your own dummy controllers (App_2_Controller). So in your case, you can remove that controller, and write a test like this for ForwardController:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class ForwardControllerTest {
#Autowired
private RestTemplate restTemplate;
#Autowired
private ForwardController forwardController; // Your service
private MockRestServiceServer server;
#Before
public void setUp() {
server = MockRestServiceServer.bindTo(restTemplate).build();
}
#Test
public void processGet_returnsResponseFromAPI() {
server.expect(once(), requestTo("http://app.com:9000/serve"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("Hello End of world", MediaType.TEXT_PLAIN));
assertThat(forwardController.processGET().getBody()).isEqualTo("Hello End of world"));
}
}
Additionally, you can create a separate test for your actual controller (ForwardController is just a service), mock ForwardController and use MockMvc:
#RunWith(SpringRunner.class)
#WebMvcTest
public class HomeControllerTest {
#Autowired
private HomeController homeController;
#Autowired
private MockMvc mockMvc;
#MockBean
private ForwardController forwardController;
#Test
public void greeting_usesForwardController() {
when(forwardController.expectGET()).thenReturn("Hello End of world");
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello End of world")));
}
}
In this case, you'll end up with two tests:
One test to verify that RestTemplate is used to capture the proper response from your external REST API.
Another test to verify that HomeController just forwards whatever ForwardController responds.

Resources