@Value 从单元测试运行时解析为 null

Tos*_*far 5 junit spring mockito

我有以下服务类:

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

和以下测试:

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行单元测试,我得到一个NullPointerExceptionapiUrl。这是@Value在我的服务类中注入的。问题是为什么application.properties当我运行 api 并通过控制器点击该服务方法时它无法获取值,一切正常。

Ken*_*han 6

因为所有的@MockitoJUnitRunner,@InjectMocks@Mock都是 Mockito 的东西,他们对 Spring 一无所知。因此,他们不明白什么@Value会注入什么价值,什么不会注入它的价值。在您的情况下,弹簧容器甚至无法启动。

如果你使用 spring-boot 并希望 Spring 注入这个值,你可以考虑使用spring-boot-starter-teststarter 并使用它@MockBean来配置 Mockito 模拟:

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

    @Autowired
    private BidServiceImpl bidService;

    @MockBean
    RestTemplate restTemplate;

    @Test
    public void testFindAllReturnsListOfBids(){
       ///
    }
}
Run Code Online (Sandbox Code Playgroud)

但它是一个集成测试,因为它会启动整个 spring 容器,所以它比真正的单元测试慢。

如果您希望测试尽可能快地运行,请不要依赖 Spring 为您注入该值。自己简单设置一下:

@RunWith(MockitoJUnitRunner.class)
public class TestBidService {

    @Mock
    RestTemplate restTemplate;

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

}
Run Code Online (Sandbox Code Playgroud)