#MOCK in Junit 5 returns NULL when using #Value in constructor - spring-boot

I am writing test cases for a project which uses File inpurstream at multiple places. Due to the issues with PowerMock and Junit5, I ended up using a Constructor with #Value to pass file paths to the class. Sample code will show.
Now When I use the constructor with #Value. The test cases for File inputStream work perfectly. But #MOck for any other class returns NULL.
I comment the constructor and #Mock starts working.
any One faced this before?
Class to be tested:
#Component
public class test {
TestUtils testUtil;
String nodeConfigFilePath;
String genConfigFilePath;
public test (#Value("${node.config.path}") String nodeConfigFilePath,
#Value("${genConfig.path}", #Autowired TestUtils testUtil) String genConfigFilePath) {
this.nodeConfigFilePath=nodeConfigFilePath;
this.genConfigFilePath= genConfigFilePath;
this.testUtil = testUtil;
}
public boolean filterConfig(JSONObject Json) {
if(testUtil.iterateJsonForPatternMatch(Json))
return true;
else
return false;
}
public void loadConfigFromJson() {
ObjectMapper mapper = new ObjectMapper();
TypeReference<Map<String, String>> typeRef = new TypeReference<Map<String, String>>() {
};
try {
InputStream inputStream = new FileInputStream(nodeConfigFilePath);
Map<String, String> nodeConfig = new HashMap<String, String>();
nodeConfig = mapper.readValue(inputStream,typeRef);
<do something>
}
}
catch(Exception e) {
}
}
}
Test Class:
#ExtendWith(MockitoExtension.class)
public class test {
#InjectMocks
test test;
#Mock
TestUtils testUtil;
#Test
public void testFilterConfig_success() throws Exception{
JSONObject jsonObj = new JSONObject();
when(testUtil.iterateJsonForPatternMatch(jsonObj)).thenReturn(true);
assertEquals(true, test.filterConfig(jsonObj));
}
#Test
public void testloadConfigFromJson_success() throws Exception{
test = new NotificationFilter(nodeConfigFile.getAbsolutePath(),genConfigJsonFile.getAbsolutePath(), testUtil);
test.loadConfigFromJson();
}
}
FYI: I changed the method names and created a dummy class for demo purpose.
So If Now I remove the #Value constructor, It starts working and no Null pointer for testUtil. But testloadConfigFromJson_success() starts to fail. And Vice versa. Can anyone explain why this is the case?
UPDATE: moved autoiring to constructor and passed the TestUtil instance via constructor in Test class. It seems to do the trick. I am not sure if its correct

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

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.

Spring Boot Controller Test Failing

I have a RestController that has just one method for http GET and is taking no input arguments. It is calling the service method which takes some arguments. Below is the controller snippet.
#RequestMapping(value = "/leagueResults", method = RequestMethod.GET)
public List<LeagueTableEntry> getResults(){
List<LeagueTableEntry> leagueTableEntryList = new ArrayList<>();
List<Match> listOfMatches = getListOfMatches();
leagueTableEntryList = leagueService.getResults(listOfMatches);
return leagueTableEntryList;
}
Below is my ControllerTest class snippet
#RunWith(SpringRunner.class)
#WebMvcTest(LeagueController.class)
#WebAppConfiguration
public class LeagueControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private LeagueService leagueService ;
private List<LeagueTableEntry> leagueEntryList;
private List<Match> matchList;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
createSampleInputData();//This method populates the instance variable matchList
getLeagueEntryOutput();//This method populates the instance variable leagueEntryList
}
#Test
public void checkNoOfRecordsReturned()throws Exception {
try{
Mockito.when(leagueService.getResults(matchList)).thenReturn(leagueEntryList);
mvc.perform(get("/leagueResults").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(4)));
}
catch(Exception e){
throw new Exception();
}
}
private void getLeagueEntryOutput(){
leagueEntryList = new ArrayList<>();
leagueEntryList.add(new LeagueTableEntry());
leagueEntryList.add(new LeagueTableEntry());
leagueEntryList.add(new LeagueTableEntry());
leagueEntryList.add(new LeagueTableEntry());
}
So, here I am expecting the count of objects in the returned list as 4, but it is coming as 0.So, my test is failing. Can you please let me know what am i missing.
I think you can instead of writing
Mockito.when(leagueService.getResults(matchList)).thenReturn(leagueEntryList);
write
Mockito.when(leagueService.getResults(Mockito.anyList())).thenReturn(leagueEntryList);
Also if this didn't work I would need to get the implementation of
List<Match> listOfMatches = getListOfMatches();

Spring Batch - RepositoryItemReader read param from ExecutionContext

I have Spring Batch job where I am passing some values between two stpes. I set the value in Job Context in Step1 and now trying to read from RepositoryItemReader in Step2. There is #BeforeStep method where I am able to read value set in context. But I am setting up my repository along with method name and args in #PostConstruct annotated method which is executed before #BeforeStep annotated method.
What is the best way to read param in ReposiotryItem from JobExecution Context?
#Component
#JobScope
public class MyItemReader extends RepositoryItemReader<Scan> {
#Autowired
private MyRepository repository;
private Integer lastIdPulled = null;
public MyItemReader() {
super();
}
#BeforeStep
public void initializeValues(StepExecution stepExecution) {
Integer value = stepExecution.getJobExecution().getExecutionContext().getInt("lastIdPulled");
System.out.println(">>>>>>>> last_pulled_id = " + value);
}
#PostConstruct
protected void init() {
final Map<String, Sort.Direction> sorts = new HashMap<>();
sorts.put("id", Direction.ASC);
this.setRepository(this.repository);
this.setSort(sorts);
this.setMethodName("findByGreaterThanId"); // You should sepcify the method which
//spring batch should call in your repository to fetch
// data and the arguments it needs needs to be
//specified with the below method.
List<Object> methodArgs = new ArrayList<Object>();
if(lastIdPulled== null || lastIdPulled<=0 ){
lastScanIdPulled = 0;
}
methodArgs.add(lastIdPulled);
this.setArguments(methodArgs);
}
}
Your reader needs to be #StepScoped instead of #JobScoped. Even though you're accessing the job context, the value is not available in the context until the previous step finishes. If you #StepScope your reader then it won't initialize until the step it is part of starts up and the value is available.
Another option is to construct the reader as a #Bean definition in a #Configuration file but the idea is the same. This uses SpEL for late binding.
#Configuration
public class JobConfig {
// Your item reader will get autowired into this method
// so you don't have to call it
#Bean
public Step myStep(MyItemReader myItemReader) {
//build your step
}
#Bean
#StepScope
public MyItemReader myItemReader(#Value("#{jobExecutionContext[partitionKey]}") Integer lastIdPulled) {
MyItemReader reader = new MyItemReader();
// Perform #PostConstruct tasks
return reader;
}
}
I was able to figure out how to solve your problem, and mine, without having to create a #Bean definition in #Configuration file and without using #PostConstruct.
Instead of using #PostConstruct, just set them in the constructor of the class and in your #BeforeStep set the arguments as shown below:
#Component
#StepScope
public class MyItemReader extends RepositoryItemReader<Scan> {
#Autowired
public MyItemReader(MyRepository myRepository) {
super();
this.setRepository(MyRepository myRepository);
this.setMethodName("findByGreaterThanId");
final Map<String, Sort.Direction> sorts = new HashMap<>();
sorts.put("id", Direction.ASC);
this.setSort(sorts);
}
#BeforeStep
public void initializeValues(StepExecution stepExecution) {
Integer value = stepExecution.getJobExecution().getExecutionContext().getInt("lastIdPulled");
System.out.println(">>>>>>>> last_pulled_id = " + value);
List<Object> methodArgs = new ArrayList<Object>();
if(lastIdPulled== null || lastIdPulled<=0 ){
lastScanIdPulled = 0;
}
methodArgs.add(lastIdPulled);
this.setArguments(methodArgs);
}
}

RestTemplate reads String data with "\" content (Even with StringHttpMessageConverter)

I am working with Spring Framework 4.3.6.RELEASE
For testing purposes through RestTemplate how a consumer, if the HTTP DELETE method fails because the entity does not exist, I need return an error message in raw format or String. It because 'normally' DELETE does not require mandatorily neither Accept nor Content-Type. I don't want complicate the user setting the Accept either for XML or JSON
My #Test method fails with the following error
java.lang.AssertionError:
Expected: is "The entity does not exist"
but: was "\"The entity does not exist\""
Observe the \" part
In the server side, I have declared:
#Bean
public StringHttpMessageConverter stringHttpMessageConverter(){
StringHttpMessageConverter converter = new StringHttpMessageConverter();
converter.setDefaultCharset(StandardCharsets.UTF_8);
converter.setSupportedMediaTypes(Arrays.asList(new MediaType(MediaType.TEXT_PLAIN, StandardCharsets.UTF_8)));
return converter;
}
And is registered:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(httpMessageConverterConfig.mappingJackson2HttpMessageConverter());
converters.add(httpMessageConverterConfig.mappingJackson2XmlHttpMessageConverter());
converters.add(httpMessageConverterConfig.stringHttpMessageConverter());
}
With a #ControllerAdvice, working together with a method annotated with #ExceptionHandler, a ResponseEntity<Object> object is created with the following approach in some place to establish the Content-Type
if(httpServletRequest.getMethod().equals(HttpMethod.DELETE.toString())){
headers.setContentType(new MediaType(MediaType.TEXT_PLAIN, StandardCharsets.UTF_8));
}
When I print the error message in the server side it prints without \" part
The RestTemplate for testing purposes is created with:
RestTemplate restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
restTemplate.setMessageConverters(customConverters);
restTemplate.setErrorHandler(restTemplateResponseErrorHandler);
Therefore why the \" part arrives to RestTemplate?
How it can be fix it.? What missing configuration is need it?
Alpha
Test Class declaration
#Transactional
#WebAppConfiguration
#RunWith(Parameterized.class)
#ActiveProfiles(resolver=TestActiveProfilesResolver.class)
#ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
#TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public class PersonaDeleteOneRestControllerClientBadRequestTest {
Some variables in that class:
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private RestTemplateClientSupport restTemplateClientSupport;
private MockMvc mockMvc;
private RestTemplate restTemplate;
private static RestTemplateResponseErrorHandler restTemplateResponseErrorHandler;
Some creations:
#BeforeClass
public static void setUp_(){
restTemplateResponseErrorHandler = new RestTemplateResponseErrorHandler();
}
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
restTemplate = restTemplateClientSupport.deleteOneBadRequest(mockMvc);
restTemplate.setErrorHandler(restTemplateResponseErrorHandler);
}
The RestTemplate part is:
#Component
public class RestTemplateClientSupport {
private static final Logger logger = LoggerFactory.getLogger(RestTemplateClientSupport.class.getSimpleName());
private final List<HttpMessageConverter<?>> customConverters;
#Autowired
public RestTemplateClientSupport(List<HttpMessageConverter<?>> customConverters){
this.customConverters = customConverters;
}
private RestTemplate createRestTemplateWithListHttpMessageConverter(MockMvc mockMvc){
RestTemplate restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
restTemplate.getMessageConverters().removeIf(converter -> {
if(converter.getClass().getSimpleName().equals(MappingJackson2XmlHttpMessageConverter.class.getSimpleName())){
logger.info("Removing: {}" , converter.getClass().getSimpleName());
return true;
}
else{
return false;
}
});
restTemplate.getMessageConverters().removeIf(converter -> {
if(converter.getClass().getSimpleName().equals(MappingJackson2HttpMessageConverter.class.getSimpleName())){
logger.info("Removing: {}" , converter.getClass().getSimpleName());
return true;
}
else{
return false;
}
});
restTemplate.getMessageConverters().removeIf(converter -> {
if(converter.getClass().getSimpleName().equals(StringHttpMessageConverter.class.getSimpleName())){
logger.info("Removing: {}" , converter.getClass().getSimpleName());
return true;
}
else{
return false;
}
});
restTemplate.getMessageConverters().addAll(customConverters);
return restTemplate;
Note: I am using restTemplate.getMessageConverters().removeIf(Predicate<>.. because I need remove these 3 default converters and add my own one created.

Resources