Junit for RESTTemplate Clients - spring

I am trying to write Junit for one REST Webservice Client which uses RestTemplate.
Below is method call to REST Template:
final ResponseEntity<LogDateVO[]> result = restTemplate.postForEntity(url, LogDateInputVO, LogDateVO[].class);
In My Test Class I am trying some thing like below :
mockServer.expect(requestTo("http://abc.def.pvt:80/testOperations/api/traffic/logs/")).andExpect(method(HttpMethod.POST))
.andRespond(MockRestResponseCreators.withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON));
I am unable to get the result back and not able to continue with this approach, i need some help in getting this.
My intention is when my restTemplate is called it should check for above expect and return success with some data.

Assuming That class under test has a method which calls postForEntity using RestTemplate. For more info check this
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
#Service
public class SimpleRestService {
#Autowired
private RestTemplate restTemplate;
public String getMessage() {
String result;
String httpResult = restTemplate.getForObject("http://google.com",
String.class);
result = "Message SUCCESS result: " + httpResult;
return result;
}
}
Your Test class should will be like as below:
#ContextConfiguration(locations = {"classpath:aap.xml"})
public class SimpleRestServiceFunctionalTest extends AbstractJUnit4SpringContextTests {
#Autowired
private SimpleRestService simpleRestService;
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
#Before
public void setUp() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void testGetMessage() {
mockServer.expect(requestTo("http://google.com")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("resultSuccess", MediaType.TEXT_PLAIN));
String result = simpleRestService.getMessage();
mockServer.verify();
assertThat(result, allOf(containsString("SUCCESS"), containsString("resultSuccess")));
}
#Test
public void testGetMessage_500() {
mockServer.expect(requestTo("http://google.com")).andExpect(method(HttpMethod.GET))
.andRespond(withServerError());
String result = simpleRestService.getMessage();
mockServer.verify();
assertThat(result, allOf(containsString("FAILED"), containsString("500")));
}
#Test
public void testGetMessage_404() {
mockServer.expect(requestTo("http://google.com")).andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.NOT_FOUND));
String result = simpleRestService.getMessage();
mockServer.verify();
assertThat(result, allOf(containsString("FAILED"), containsString("404")));
}
}

Related

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

Sprint boot - Auto configure to call a REST service on startup

I have a requirement to create an auto-configuration for service call on spring-boot startup.
i.e., During spring-boot startup, the below service has to be called.
#PostMapping(path = "/addProduct", produces = "application/json", consumes = "application/json")
public #ResponseBody String addProduct(#RequestBody String productStr) {
..<My code>..
}
The add product requires an input like:
{
"product":"test",
"price":"10"
}
This will internally call a database service.
During startup, the json input provided in the console should be fed to this service.
I have no idea on how to achieve this. Verified a couple of Spring documentation. But those does'nt suit the requirement.
Kindly help in explaining a way or providing a right documentation to achieve this.
One way to do this is by implementing ApplicationRunner like this :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
#Component
public class ApplicationInitializer implements ApplicationRunner {
private ProductController productController;
public ApplicationInitializer(ProductController productController) {
this.productController = productController;
}
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
String response = productController.add(product);
System.out.println(response);
}
}
The run method will be invoked at startup with arguments passed in the command line like this : java -jar yourApp.jar --product="{\"name\":\"test\", \"price\":\"15\"}".
And you need a class to map the json to an object like this :
public class Product {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
You can also call your Controller using the RestTemplate (or WebClient) if needed :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class ApplicationInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject("http://localhost:8080/products", product, String.class);
System.out.println(response);
}
}
Such requirement can be achieved by using an init() method annotated with #PostConstruct in a bean.
e.g.
#Component
public class Foo {
#PostConstruct
public void init() {
//Call your service
}
}

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.

Springboot test issue: Mockito is returning empty object

I am trying to build a prototype that is using gradle as build tool and openjdk-11. This prototype will build a rest-api on springboot framework.
My module is working fine with rest-api call and returning expected result. However, as I am now trying to write a test for the rest api, the test is failing as Mockito is returning empty object. Would appreciate any insight on how should I write a test for this rest-api or what to do to fix it.
My controller:
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#Autowired
GreetingService service;
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return service.getGreetings(0L, String.format(template, name));
}
}
The service:
#Service
public class GreetingService {
public Greeting getGreetings() {
return new Greeting(1L, "Hello World");
}
public Greeting getGreetings(Long id, String name) {
return new Greeting(id, name);
}
}
The Model:
#Builder
#Data
#RequiredArgsConstructor
#JsonDeserialize(builder = Greeting.class)
public class Greeting {
#NonNull
private Long id;
#NonNull
private String content;
}
The main class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I executed this by :
gradle bootrun
And then from browser, tried :
http://localhost:8080/greeting
And that returned:
{"id":0,"content":"Hello, World!"}
Again tried:
http://localhost:8080/greeting?name=Patty
and that returned:
{"id":0,"content":"Hello, Patty!"}
Now, I was trying to write test to validate api calls similar to the above calls programatically. So I tried:
#RunWith(MockitoJUnitRunner.class)
public class GreetingControllerTest {
private MockMvc mockMvc;
#Mock
private GreetingService service;
#InjectMocks
private GreetingController controller
#Test
public void testGreeting() throws Exception {
Greeting greeting = new Greeting(0L,"Patty!");
String expectedResponse = "{\"id\":0,\"content\":\"Hello, Patty!\"}";
//JacksonTester.initFields(this, new ObjectMapper());
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.build();
Mockito.when(service.getGreetings(0L,"Patty")).thenReturn(greeting);
MockHttpServletResponse response = mockMvc
.perform(get("/greeting?name=Patty")
.contentType(MediaType.ALL))
.andReturn()
.getResponse();
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
assertThat(response.getContentAsString()).isEqualTo(expectedResponse)
}
}
The error msg is :
org.junit.ComparisonFailure:
Expected :"{"id":0,"content":"Hello, Patty!"}"
Actual :""
Failing from this line:
assertThat(response.getContentAsString()).isEqualTo(expectedResponse)
Thanks in advance.
This helped me understanding: Mockito - thenReturn always returns null object
I changed the Mockito.when part to :
Mockito.when(service.getGreetings(Mockito.anyLong(),Mockito.anyString())).thenReturn(greeting);
and it worked

Using #RestClientTest in spring boot test

I want to write a simple test using #RestClientTest for the component below (NOTE: I can do it without using #RestClientTest and mocking dependent beans which works fine.).
#Slf4j
#Component
#RequiredArgsConstructor
public class NotificationSender {
private final ApplicationSettings settings;
private final RestTemplate restTemplate;
public ResponseEntity<String> sendNotification(UserNotification userNotification)
throws URISyntaxException {
// Some modifications to request message as required
return restTemplate.exchange(new RequestEntity<>(userNotification, HttpMethod.POST, new URI(settings.getNotificationUrl())), String.class);
}
}
And the test;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}
But i get error that No qualifying bean of type 'org.springframework.web.client.RestTemplate' available. Which is right of course as i havn't mocked it or used #ContextConfiguration to load it.
Isn't #RestClientTest configures a RestTemplate? or i have understood it wrong?
Found it! Since i was using a bean that has a RestTemplate injected directly, we have to add #AutoConfigureWebClient(registerRestTemplate = true) to the test which solves this.
This was in the javadoc of #RestClientTest which i seem to have ignored previously.
Test which succeeds;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
#AutoConfigureWebClient(registerRestTemplate = true)
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}

Resources