How to test if headers are added properly to HTTP request - java

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.

Related

How to pass #MockBean to an internal function call from a JUnit?

I need to write unit tests for a Spring Controller class.
The setup is like this:
#RestController
#RequestMapping("/")
public class MyCustomController {
#Autowired
private MagicWriter magicWriter;
#Autowired
private MagicUpdater magicUpdater;
#RequestMapping(path = "/", method = RequestMethod.POST)
public String postMagicMethod(#RequestParam(name = "SomeParam") String param1) {
var magicHandler = new MagicHandler(magicWriter, magicUpdater);
return magicHandler.doSomeMagic();
}
}
From my JUnit test, I need to use #MockBean for magicWriter and magicUpdater class.
So far I could not find anything constructive.
Here is my Unit test
#SpringJUnitConfig
#WebMvcTest(value= MyCustomController.class)
public class MyCustomControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MagicWriter magicWriter;
#MockBean
private MagicUpdater magicUpdater;
#Autowired
private WebApplicationContext webApplicationContext;
#Configuration
static class Config {
#Bean
MyCustomController dispatchController() {
return new MyCustomController();
}
}
#Test
void basicTest() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
HttpHeaders headers = new HttpHeaders();
// Added some http headers
String uri = "/";
RequestBuilder request = MockMvcRequestBuilders.request(HttpMethod.POST, uri, headers);
MvcResult result = mockMvc.perform(request).andReturn();
assertThat(result.getResponse().getContentAsString()).isEqualTo(expected);
}
}
Convert your #Autowired parameters to be constructor based and not field-based.
#RestController
#RequestMapping("/")
public class MyCustomController {
private MagicWriter magicWriter;
private MagicUpdater magicUpdater;
#Autowired
public MyCustomController(MagicWriter magicWriter, MagicUpdater magicUpdater) {
this.magicWriter = magicWriter;
this.magicUpdater = magicUpdater;
}
// ... rest of your code
}
Then in your test, you just new an instance of this class with your mocks passed in. You're already resigned to using mock beans, so you don't need to whole Spring Context to come along.
// Unit test code example
MyCustomController testObject;
MagicWriter magicWriterMock;
magicUpdater magicUpdaterMock;
#BeforeEach
void setUp() throws Exception {
magicWriterMock = mock(MagicWriter.class);
magicUpdaterMock = mock(MagicUpdater.class);
testObject = new MyCustomController(magicWriterMock, magicUpdaterMock);
}

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.

JUnit Mock outside rest call via restTemplate

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);

Mocked method returning NullPointerException

I want to test a method which will add headers to my GET request in order to establish a connection with public API.
My service with business logic looks like:
#Service
public class RestTemplateFacade {
private NutritionixHeader nutritionHeaderParam;
private RestTemplate restTemplate;
public RestTemplateFacade(
NutritionixHeader nutritionHeaderParam,
RestTemplate restTemplate) {
this.nutritionHeaderParam = nutritionHeaderParam;
this.restTemplate = restTemplate;
}
public ResponseEntity<Products> addHeaderToRequest(String queryParam) {
HttpHeaders headers = new HttpHeaders();
headers.set("x-app-id", nutritionHeaderParam.getNutritionixAppId());
headers.set("x-app-key", nutritionHeaderParam.getNutritionixAppKey());
UriComponentsBuilder uriBuilder = UriComponentsBuilder
.fromHttpUrl("https://trackapi.nutritionix.com/v2/search/instant")
.queryParam("query", queryParam);
HttpEntity httpEntity = new HttpEntity(headers);
return
restTemplate
.exchange(
uriBuilder.toUriString(),
HttpMethod.GET,
httpEntity,
Products.class);
}
}
NutritionixHeader.class looks like:
#PropertySource("classpath:nutritionix.properties")
#Configuration
#Getter
#NoArgsConstructor
public class NutritionixHeader {
#Value("${nutritionix-app-id}")
private String NutritionixAppId;
#Value("${nutritionix-app-key}")
private String NutritionixAppKey;
}
My test class looks like:
#ExtendWith(MockitoExtension.class)
#MockitoSettings(strictness = Strictness.LENIENT)
class RestTemplateFacadeTest {
#Mock
private NutritionixHeader nutritionixHeader;
#Mock
private RestTemplate restTemplate;
#InjectMocks
private RestTemplateFacade restTemplateFacade;
#Mock
private UriComponentsBuilder uriComponentsBuilder;
#BeforeEach
void setUp() {
restTemplateFacade = new RestTemplateFacade(nutritionixHeader, restTemplate);
}
#Test
void addHeaderToRequest() {
//given
var query = "query";
given(nutritionixHeader.getNutritionixAppId()).willReturn("x-app-id");
given(nutritionixHeader.getNutritionixAppKey()).willReturn("x-app-key");
//when
ResponseEntity<Products> productsResponse = restTemplateFacade.addHeaderToRequest(query);
HttpHeaders headers = productsResponse.getHeaders();
//then
assertEquals("x-app-id",
headers.entrySet()
.stream()
.filter(entry -> entry.getKey().equals("x-app-id"))
.map(Entry::getValue)
.flatMap(Collection::stream)
.findFirst()
.orElse(""));
then(nutritionixHeader).should(times(1)).getNutritionixAppId();
}
Problem occurs in the line:
HttpHeaders headers = productsResponse.getHeaders();
with NullPointerException.
All I need is the ability to test if my method has correctly added headers to my GET query.
I am a little bit confused because it seems that I mocked everything properly but still receiving null pointer. Maybe my NPE is related to the fact there is no mock for my RestTemplate object. If it's true I will be grateful for suggestions on how to fix this problem.
You need use ArgumentCaptor to catch HttpEntity and then get the headers. Have a look at the code:
#Test
void addHeaderToRequest() {
//given
var query = "query";
given(nutritionixHeader.getNutritionixAppId()).willReturn("x-app-id");
given(nutritionixHeader.getNutritionixAppKey()).willReturn("x-app-key");
//when
restTemplateFacade.addHeaderToRequest(query);
//then
ArgumentCaptor<HttpEntity> httpEntityCapture = ArgumentCaptor.forClass(HttpEntity.class);
String url = "https://trackapi.nutritionix.com/v2/search/instant?query=query";
verify(restTemplate).exchange(eq(url), eq(HttpMethod.GET), httpEntityCapture.capture(), eq(Products.class));
HttpHeaders headers = httpEntityCapture.getValue().getHeaders();
assertEquals("x-app-id",
headers.entrySet()
.stream()
.filter(entry -> entry.getKey().equals("x-app-id"))
.map(Map.Entry::getValue)
.flatMap(Collection::stream)
.findFirst()
.orElse(""));
then(nutritionixHeader).should(times(1)).getNutritionixAppId();
}

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"));
}}

Categories

Resources