Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Value resolving to null when running things from unit test

I have following service class:

@Service
public class BidServiceImpl implements BidService {

    private final String apiUrl;

    private final RestTemplate restTemplate;

    @Autowired
    public BidServiceImpl(RestTemplate restTemplate,
                          @Value("${api.url}") String apiUrl){
        this.apiUrl = apiUrl;
        this.restTemplate = restTemplate;
    }


    public List<Bid> findAll() {
        ResponseEntity<Bid[]> responseEntity = restTemplate.getForEntity(apiUrl, Bid[].class);
        Bid[] bids = responseEntity.getBody();
        return Arrays.asList(bids);
    }
}

and following test:

@RunWith(MockitoJUnitRunner.class)
public class TestBidService {

    @InjectMocks
    private BidServiceImpl bidService;

    @Mock
    RestTemplate restTemplate;

    @Test
    public void testFindAllReturnsListOfBids(){
        List<Bid> b = new ArrayList<>();
        Bid[] arr = new Bid[2];
        arr[1] = new Bid();
        arr[0] = new Bid();
        ResponseEntity<Bid[]> br = new ResponseEntity<>(arr, HttpStatus.OK);
        when(restTemplate.getForEntity("url",Bid[].class)).thenReturn(br);
        List<Bid> bids = bidService.findAll();
        Assert.assertEquals(2,bids.size());
        Assert.assertTrue(bids instanceof List);
    }
}

when I run the unit test, I am getting a NullPointerException on apiUrl. which is injected as @Value in the my service class. The question is why it is not able to get the value from application.properties when I run the api and hit that service method via a controller everything works fine.

like image 772
Toseef Zafar Avatar asked Feb 16 '19 16:02

Toseef Zafar


1 Answers

Because all the @MockitoJUnitRunner, @InjectMocks and @Mock are Mockito stuffs and they do not know anything about Spring. Hence they do not understand what @Value does and will not inject its value. The spring container even do not start in you case.

If you are using spring-boot and want Spring to inject this value , you can consider using spring-boot-starter-test starter and use its @MockBean to configure the Mockito mock :

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestBidService { 

    @Autowired
    private BidServiceImpl bidService;

    @MockBean
    RestTemplate restTemplate;

    @Test
    public void testFindAllReturnsListOfBids(){
       ///
    }
}

But it is an integration test as it will start up the whole spring container , so it is slower than the true unit test.

If you want the test to run as fast as possible , don't rely on Spring to inject that value for you. Simply set up by yourself:

@RunWith(MockitoJUnitRunner.class)
public class TestBidService {

    @Mock
    RestTemplate restTemplate;

    @Test
    public void testFindAllReturnsListOfBids(){
        BidServiceImpl bidService = new BidServiceImpl(restTemplate , "http://127.0.0.1/");
        ////
    }

}
like image 143
Ken Chan Avatar answered Oct 20 '22 22:10

Ken Chan