Search code examples
springspring-bootjunitjunit4junit5

Unable to fetch properties value using @Value in JUnit 5


Inside my service implementation class I am trying to fetch value from application.properties using @Value. It is working fine. But while writing test case for it, I am getting null.

Inside service impl class

@Value("${transaction.service.process.layer.url}")
private String processLayerUrl;

public ResponseEntity<Object> getTransactionDataListByAccount(Transaction transaction) {
    ResponseEntity<Object> transactionHistoryResponse = restTemplate.postForEntity(processLayerUrl, transaction, Object.class);        
    return new ResponseEntity<>(transactionHistoryResponse.getBody(), HttpStatus.OK);
}

Test file

@RunWith(SpringRunner.class)
@WebMvcTest(value = TransactionServiceImpl.class)
@ActiveProfiles(profiles = "test")
@ContextConfiguration
//@ContextConfiguration(classes = TransactionServiceExperienceApplication.class)
//@SpringBootConfiguration(classes = { TransactionServiceExperienceApplicationTests.class })
//@TestPropertySource(locations="classpath:application-test.properties")
//@SpringBootTest(properties = { "transaction.service.process.layer.url =http://localhost:8087/v1/transaction-process-service" })
//@TestPropertySource(properties = { "transaction.service.process.layer.url = http://localhost:8087/v1/transaction-process-service" })
public class TransactionServiceImplTest {

    @Mock 
    private RestTemplate mockRestTemplate;
    
    @InjectMocks
    private TransactionServiceImpl transactionService;
    
    @Before
    public void setUp() {
        //transactionService = new TransactionServiceImpl("test");
        //ReflectionTestUtils.setField(transactionService, "processLayerUrl", "foo");
    }
    
    @Test 
    public void getTransactionDataListByAccountTest() throws Exception{
        
        Transaction transaction = new Transaction();
        transaction.setPosAccountNumber("40010020070401");
        
        HttpHeaders header = new HttpHeaders();
        header.setContentType(MediaType.APPLICATION_JSON);
        
        ArrayList<Object> mockResponseObj = new ArrayList<Object>();
        //Added data inside this object

        ResponseEntity<Object> responseEntity = new ResponseEntity<>(mockResponseObj, HttpStatus.OK);
        
        when(mockRestTemplate.postForEntity(
                ArgumentMatchers.anyString(), 
                ArgumentMatchers.eq(Transaction.class), 
                ArgumentMatchers.eq(Object.class))).thenReturn(responseEntity);
        
        ResponseEntity<Object> actualResponse = transactionService.getTransactionDataListByAccount(transaction);
        
        System.out.println("--- Response ---");
        System.out.println(actualResponse);
    }
    
    /*
     * @Configuration
     * 
     * @ComponentScan("transaction.service.experience.service") static class
     * someConfig {
     * 
     * // because @PropertySource doesnt work in annotation only land
     * 
     * @Bean PropertySourcesPlaceholderConfigurer propConfig() {
     * PropertySourcesPlaceholderConfigurer ppc = new
     * PropertySourcesPlaceholderConfigurer(); ppc.setLocation(new
     * ClassPathResource("application.properties")); return ppc; } }
     */
}

I have specified application-test.properties file inside src/test/resources

Here is my build.gradle

testImplementation('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
implementation 'junit:junit:4.12'

Due to this restTemplate is throwing NullPointerException instead of returning mock obj.


Solution

  • It happens because you're using Mockito annotations instead ones provided by Spring Boot.

    transactionService should be annotated with @Autowired and mockRestTemplate with @MockBean.

    @MockBean either adds or replaces original bean with a mock in Spring application context and injects it into any beans in this context that need it as a dependency.

    You should not instantiate transactionService manually if the test you're writing is an integration test.

    You should also reconsider what are you testing here - WebMvc test are meant to test controllers, not services. If you want to test the service - consider writing plain unit test without Spring involved. If you want real integration test consider using @SpringBootTest.