JUnit Mock outside rest call via restTemplate - java

I am trying to write an Integration test where I have an issue in mocking the rest call which is calling outside server using JUnit.
I have added a #Mock and #InjectMock on service
Service looks like this.
#Service
public class BoundaryDeltaService {
private BoundaryDelta getBoundaryDeltaUsingApp() {
List<BoundaryValueInfo> infoFromSource = Arrays.asList(serviceAdapter.readInfoFromApiUsingApp(boundarySet, app, loginUserInfo));
return getBoundaryDeltacompareCurrentBoundaryValuesWithSource(boundarySet, infoFromSource );
}
}
There is another service with has restTemplate call
#Service
public class ServiceAdapter {
public BoundaryValueInfo[] readInfoFromApiUsingApp(){
String loginToken = systemUserLoginService.getSystemUserTokenManual(app, loginUserInfo);
restTemplate = createRestTemplate(boundarySet);
HttpHeaders headers = new HttpHeaders() {{
String authHeader = "Bearer " + loginToken;
set( "Authorization", authHeader );
}};
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<BoundaryValueInfo[]> answerFromApi = restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request, BoundaryValueInfo[].class);
return getResponseFromApi(answerFromApi);
}
}
And this is the test scenario
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles({"aws", "local"})
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = FlywayConfig.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
public class BoundaryValueDeltaControllerTest {
private static final String API_V1 = "/api/v1/";
#Autowired
TestRestTemplate testRestTemplate;
#Autowired
private BoundaryDeltaService boundaryDeltaService;
#Autowired
private DomainBuilder domainBuilder;
#Autowired
private AppBuilder appBuilder;
#Autowired
private AppAdminBuilder appAdminBuilder;
#Autowired
private BoundaryValueBuilder boundaryValueBuilder;
#Autowired
private BoundarySetBuilder boundarySetBuilder;
#MockBean
private LoginUserProvider loginUserProvider;
#MockBean
private LoginTokenService loginTokenService;
#InjectMocks
private BoundaryServiceAdapter boundaryServiceAdapter;
#Mock
RestTemplate restTemplate;
#LocalServerPort
private int port;
Domain domain;
App app;
BoundarySet boundarySet;
BoundaryValue boundaryValue;
LoginUserInfo loggedInUser;
#Before
public void setUp() {
clear();
domain = domainBuilder.persist();
app = appBuilder.persist(domain);
boundarySet = boundarySetBuilder.persist(domain);
boundaryValue = boundaryValueBuilder.persist(boundarySet);
}
#After
public void tearDown() {
clear();
}
#BeforeClass
public static void setupTestEnv() {
// https://github.com/localstack/localstack/issues/592
}
#Test
public void updateBoundaryValuesFromApi() {
aLoggedInUser(domain.getAuthor().getUsername());
appAdminBuilder.persist(app, domain.getAuthor());
ResponseEntity<BoundaryValueInfo[]> answerFromApi = getBoundaryValueInfos();
HttpHeaders headers = new HttpHeaders() {{
String authHeader = "Bearer 1234";
set( "Authorization", authHeader );
}};
HttpEntity<String> request = new HttpEntity<>(headers);
//when(restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request, BoundaryValueInfo[].class)).thenReturn(answerFromApi);
when(restTemplate.exchange(ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
).thenReturn(answerFromApi);
String url = url(API_V1 + "domains/" + domain.getName() + "/boundarysets/" + boundarySet.getBoundarySetName() + "/app/" + app.getName()+ "/updatefromapi/");
ResponseEntity<String> response = testRestTemplate.exchange(url,HttpMethod.GET, null, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
I am calling controller with api and from there it is going into above services which has rest call but not able to mock the actual call.
Can someone guide me here ?

You are not using the TestRestTemplate for the purpose it is intended for.
TestRestTemplate is not an extension of RestTemplate, but rather an alternative that simplifies integration testing and facilitates authentication during tests. It helps in customization of Apache HTTP client, but also it can be used as a wrapper of RestTemplate
Solution:
Approach1:
1) Directly call the exchange method using RestTemplate object.
ResponseEntity<String> response = restTemplate .exchange(url,HttpMethod.GET, null, String.class);
2) Mock the testRestTemplate and do the mock when on the exchange method call for this object.
when(testRestTemplate.exchange(ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
).thenReturn(answerFromApi);

Related

Integration test not able to mock for methods annotated with #CircuitBreaker

Integration test not able to mock RestTemplate field on methods annotated with #CircuitBreaker.
controller:
#RestController
public class CollegeController {
#Autowired
private CollegeService collegeService;
#RequestMapping(value = "/college/student/{collegeId}")
public ResponseEntity<Map> getCollegeStudent(#PathVariable String collegeId){
ResponseEntity<Map> studentByCollege = collegeService.getStudentByCollege(collegeId);
return studentByCollege;
}
}
Service:
#Service
public class CollegeServiceImpl implements CollegeService {
public static final String COLLEGE_SERVICE = "collegeService";
#Autowired
RestTemplate restTemplate;
int count = 0;
#Override
#Retry(name = COLLEGE_SERVICE/*, fallbackMethod = "fallbackForRetry"*/)
#CircuitBreaker(name = COLLEGE_SERVICE, fallbackMethod = "getAllStudentFallback")
public ResponseEntity<Map> getStudentByCollege(String collegeId) {
ResponseEntity<Map> forEntity = null;
String url = "http://localhost:8080/student/{collegeId}";
System.out.println(" count = " + ++count);
HttpHeaders headers = new HttpHeaders();
HttpEntity request = new HttpEntity(headers);
forEntity = restTemplate.exchange(url, HttpMethod.GET,request, Map.class,collegeId);
//return testCircuitBreakerInFunctionalProgramming().apply(collegeId);
//throw new RuntimeException("abc");
return forEntity;
}
public ResponseEntity<Map> getAllStudentFallback(String collegeId, Throwable t) {
System.out.println("fallback method");
return null;
}
public ResponseEntity<Map> getAllStudentFallback(String collegeId, CallNotPermittedException t) {
System.out.println("fallback call not permitted method");
return null;
}
Integration Test :
#SpringBootTest
#RunWith(SpringRunner.class)
public class CollegeIntegrationTest {
//#InjectMocks
#Autowired
private CollegeController controller;
#InjectMocks
#Autowired
private CollegeServiceImpl service;
#Mock
private RestTemplate restTemplate;
#Before
public void setup() {
MockitoAnnotations.openMocks(this);
}
#Test
public void testCollege() {
ResponseEntity<Map> response = new ResponseEntity<>(HttpStatus.OK);
Mockito.when(restTemplate.exchange(Mockito.anyString(), Mockito.same(HttpMethod.GET),Mockito.<HttpEntity> any(), Mockito.any(Class.class),Mockito.anyString())).thenReturn(response);
controller.getCollegeStudent("102");
}
}
What I am doing:
1 of the micorservice is down and I am implementing CircuitBreaker for that scenario and Its working as expected.
Issue:
When I am not using CircuitBreaker annotation, mock of RestTemplate is available with same id in service class. i.e Mocking working fine as expected.
In case when I use #CircuitBreaker, some mock id is generated in IntegrationTest class but the same is not available on service, so not able to mock restcall.
I am initializing rest template using :
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
In whole mocking is not working as expected while using #CircuitBreaker.
Any suggestion would be appreciated.

How to test if headers are added properly to HTTP request

I have a method like:
#Service
public class ProductFacadeImpl implements ProductFacade {
private NutritionixHeader nutritionHeaderParam;
private RestTemplate restTemplate;
public ProductFacadeImpl(NutritionixHeader nutritionHeaderParam, RestTemplate restTemplate) {
this.nutritionHeaderParam = nutritionHeaderParam;
this.restTemplate = restTemplate;
}
public HttpEntity addUniversalHeaderForRequest() {
HttpHeaders headers = new HttpHeaders();
headers.set("x-app-id", nutritionHeaderParam.getNutritionixAppId());
headers.set("x-app-key", nutritionHeaderParam.getNutritionixAppKey());
return new HttpEntity(headers);
}
}
I want to test if my method is adding properly headers to the HTTP request.
My test looks like:
#ExtendWith(MockitoExtension.class)
#MockitoSettings(strictness = Strictness.LENIENT)
class ProductFacadeImplTest {
#Mock
private NutritionixHeader nutritionixHeader;
#Mock
private RestTemplate restTemplate;
#InjectMocks
private ProductFacadeImpl productFacadeImpl;
#Mock
private UriComponentsBuilder uriComponentsBuilder;
#Mock
private ResponseEntity responseEntity;
#Mock
private HttpHeaders httpHeaders;
#BeforeEach
void setUp() {
productFacadeImpl = new ProductFacadeImpl(nutritionixHeader, restTemplate);
}
#Test
void addUniversalHeaderForRequest() {
//given
given(nutritionixHeader.getNutritionixAppId()).willReturn("id");
given(nutritionixHeader.getNutritionixAppKey()).willReturn("key");
//when
productFacadeImpl.addUniversalHeaderForRequest();
ArgumentCaptor<HttpEntity> httpEntityCapture = ArgumentCaptor.forClass(HttpEntity.class);
ArgumentCaptor<HttpHeaders> httpHeadersCaptor = ArgumentCaptor.forClass(HttpHeaders.class);
verify(httpHeaders).set(eq("x-app-id"), eq("id"));
verify(httpHeaders).set(eq("x-app-key"), eq("key"));
HttpHeaders headers = httpEntityCapture.getValue().getHeaders();
assertEquals("id",
headers.entrySet()
.stream()
.filter(entry -> entry.getKey().equals("x-app-id"))
.map(Entry::getValue)
.flatMap(Collection::stream)
.findFirst()
.orElse(""));
}
}
but when I start it I am receiving an information like:
Wanted but not invoked:
httpHeaders.set("x-app-id", "id");
-> at com.application.nutritionix.service.ProductFacadeImplTest.addUniversalHeaderForRequest(ProductFacadeImplTest.java:196)
Actually, there were zero interactions with this mock.
I know that I suppose to use ArgumentCaptor but I cannot determine how to use httpEntityCapture properly to solve this example.
Thanks for your suggestions on how to reach a goal.

Spring Boot - Mock a POST REST request to an external API

I have a Spring-Boot 1.5.21 application that serves as a REST gateway between an Angular UI and an external API that provides the data (long story - acts as auth between UI and datasource). A request comes to the Spring-Boot application, it calls the data source API with the request payload.
I am new to Unit Testing for Spring-Boot and am trying to write a test for the POST REST method in the Gateway application that creates a new record (create). I've read a couple of tutorials and other websites detailing how to unit test Spring-Boot APIs but nothing that helps me in my situation.
I want to:
Unit test the REST Controller method and check that the #RequestBody is valid
I do not want a record created in the datasource
Controller Method:
#PostMapping(value = "/" + Constants.API_CHANGE_REQUEST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public String submitChangeRequest(#RequestBody ChangeRequestWrapper changeRequestWrapper) {
logger.info("API Request: Posting Change Request: " + changeRequestWrapper.toString());
return restService.makeApiPost(sharedDataService.buildApiUrlPath(Constants.API_CHANGE_REQUEST), changeRequestWrapper);
}
AppConfig:
#PropertySource({"classpath:application.properties"})
#Configuration
public class AppConfig {
#Resource
private Environment env;
#Bean
public RestTemplate restTemplate() {
RestTemplateBuilder builder = new RestTemplateBuilder();
return builder
.setConnectTimeout(Constants.API_TIMEOUT_CONNECT)
.setReadTimeout(Constants.API_TIMEOUT_READ)
.basicAuthorization(env.getProperty("bpm.user"), env.getProperty("bpm.password"))
.build();
}
}
RestServiceImpl:
#Service
public class RestServiceImpl implements RestService {
private static final Logger logger = LoggerFactory.getLogger(RestServiceImpl.class);
#Autowired
private RestTemplate myRestTemplate;
#Value("${bpm.url}")
private String restUrl;
public String getApiUri() {
return restUrl;
}
public String makeApiCall(String payload) /*throws GradeAdminException */{
logger.info("Implementing API call.");
logger.debug("userApi: " + payload);
return myRestTemplate.getForObject(payload, String.class);
}
public String makeApiPost(String endpoint, Object object) {
logger.info("Implementing API post submission");
logger.debug("userApi endpoint: " + endpoint);
return myRestTemplate.postForObject(endpoint, object, String.class);
}
}
SharedDataServiceImpl:
#Service
public class SharedDataServiceImpl implements SharedDataService {
#Autowired
private RestService restService;
#Override
public String buildApiUrlPath(String request) {
return buildApiUrlPath(request, null);
}
#Override
public String buildApiUrlPath(String request, Object parameter) {
String path;
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(restService.getApiUri());
if (parameter != null) {
builder = builder.path(getApiPath(request) + "/{object}");
UriComponents buildPath = builder.buildAndExpand(parameter);
path = buildPath.toUriString();
} else {
builder = builder.path(getApiPath(request));
path = builder.build().toUriString();
}
return path;
}
}
What I've done for the GET methods:
#RunWith(SpringRunner.class)
#WebMvcTest(ClientDataRequestController.class)
#ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigWebContextLoader.class)
public class ClientDataRequestControllerTest {
#Autowired
private MockMvc mvc;
#Before
public void setUp() {
}
#Test
public void test_no_endpoint() throws Exception {
this.mvc.perform(get("/")).andExpect(status().isNotFound()).andReturn();
}
#Test
public void test_controller_no_endpoint() throws Exception {
this.mvc.perform(get("/api/")).andExpect(status().isOk()).andReturn();
}
#Test
public void test_getStudent_valid_parameters() throws Exception {
this.mvc.perform(get("/api/students/?pidm=272746")).andExpect(status().isOk()).andReturn();
}
}
I would greatly appreciate some assistance with this.
Solution:
I've since found this SO answer that has solved my problem.
You could mock the RestServiceImpl. Add a dependency in your test and annotate it with MockBean:
#MockBean
private RemoteService remoteService;
Now you can go ahead and mock the methods:
import org.mockito.BDDMockito;
BDDMockito.given(this.remoteService.makeApiPost()).willReturn("whatever is needed for your test");

TestRestTemplate in junit

ArticleController.java
#GetMapping(path = "articles/{article-id}")
public ResponseEntity<Article> getArticleById( #PathVariable("article-id") Long id) {
Article article = articleService.findById(id);
if (article != null)
return new ResponseEntity<>(article, HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
ArticleControllerTest.java
public class ArticleControllerTest {
#Autowired
private TestRestTemplate template;
#Test
public void testGetArticle(){
// how to implement it
}
private HttpEntity<Object> getHttpEntity(Object body) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return new HttpEntity<Object>(body, headers);
}
}
i searched a lot but find nothing .... how to implement getArticle using the template and this private method ??
Your controller has a dependency to service class. And your method which you want to test is using articleService.findById(id) method. By mocking your service class you can test your controller.
Here is an example of how to use MockMvc:
#RunWith(SpringRunner.class)
#WebMvcTest(ProductController.class)
public class ProductControllerMvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService productService;
#Test
public void it_should_return_product_by_id() throws Exception {
//given
Long productId = 1L;
Product product = ProductBuilder.aProduct().id(productId).title("Nokia").build();
given(productService.findById(productId)).willReturn(product);
//when
mockMvc.perform(get("/product/1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("id").value(productId))
.andExpect(jsonPath("title").value("Nokia"));
}}

What's the correct way to write a Spring Boot Integration Test for REST API

I have a REST API developed using Spring Boot and H2 DB which is working fine without any issues.
http://localhost:8080/api/employees
returns
[
{
"id":1,
"name":"Jack"
},
{
"id":2,
"name":"Jill"
}
]
Now the problem is that I have two different integration tests for testing this REST API. Both of them are working fine but I am not sure which one should I discard. I understand that both of them are more or less achieving the same result. The first one is using MockMvc while the other one is using TestRestTemplate.
Is there any best practice that I should be following and favoring one over the other?
EmployeeRestControllerIT.java
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = TestingSampleApplication.class)
#AutoConfigureMockMvc
public class EmployeeRestControllerIT {
#Autowired
private MockMvc mvc;
#Test
public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception {
mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$[0].name", is("Jack")));
}
}
EmployeeRestControllerIT2.java
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = TestingSampleApplication.class)
public class EmployeeRestControllerIT2 {
#LocalServerPort
private int port;
private final TestRestTemplate restTemplate = new TestRestTemplate();
private final HttpHeaders headers = new HttpHeaders();
private final String expected = "[{\"id\":1,\"name\":\"Jack\"},{\"id\":2,\"name\":\"Jill\"}]";
#Test
public void testRetrieveStudentCourse() throws JSONException {
String url = "http://localhost:" + port + "/api/employees";
HttpEntity<String> entity = new HttpEntity<>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(url, GET, entity, String.class);
JSONAssert.assertEquals(expected, response.getBody(), false);
}
}

Categories

Resources