Spring Boot JUnit测试异常:无法读取application-test.yml配置值
问题根源
空指针异常的直接原因是getExchangeUrl生成的URL包含null前缀,本质是Constants类中的静态字段EXCHANGE_API_BASE_URL和EXCHANGE_API_API_KEY未被注入值。当前测试用Mockito的@InjectMocks和@Mock搭建环境,未启动Spring上下文,Constants类的@Component注解和@Value注入逻辑完全没执行,静态字段保持初始null值。
解决方案
方案1:用Spring Boot测试框架加载上下文
通过@SpringBootTest让Spring管理组件,确保@Value正常读取配置:
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.beans.factory.annotation.Autowired; @SpringBootTest class RateServiceTest { @MockBean private RateRepository rateRepository; @MockBean private RestTemplate restTemplate; @Autowired private RateService rateService; @Test void testCalculateRate() { // 移除MockitoAnnotations.openMocks(this),SpringBootTest会自动初始化mock // ... 原有测试逻辑保持不变 } }
方案2:手动初始化静态字段(快速临时修复)
在测试方法开始前手动设置Constants的静态字段值:
@Test void testCalculateRate() { // 手动注入配置值 Constants.EXCHANGE_API_BASE_URL = "https://api.apilayer.com/exchangerates_data/"; Constants.EXCHANGE_API_API_KEY = "default-key"; MockitoAnnotations.openMocks(this); // ... 原有测试逻辑 }
方案3:重构Constants类(推荐,符合Spring最佳实践)
避免使用静态字段,改用构造函数注入的非静态类,提升可测试性:
重构Constants类
@Component public class Constants { private final String exchangeApiBaseUrl; private final String exchangeApiApiKey; private final String exchangeCacheName; private final Integer exchangeApiCallLimit; public Constants(@Value("${exchange-api.api-url}") String exchangeApiBaseUrl, @Value("${exchange-api.api-key}") String exchangeApiApiKey, @Value("${exchange-api.cache-name}") String exchangeCacheName, @Value("${exchange-api.api-call-limit}") Integer exchangeApiCallLimit) { this.exchangeApiBaseUrl = exchangeApiBaseUrl; this.exchangeApiApiKey = exchangeApiApiKey; this.exchangeCacheName = exchangeCacheName; this.exchangeApiCallLimit = exchangeApiCallLimit; } // Getter方法 public String getExchangeApiBaseUrl() { return exchangeApiBaseUrl; } public String getExchangeApiApiKey() { return exchangeApiApiKey; } public String getExchangeCacheName() { return exchangeCacheName; } public Integer getExchangeApiCallLimit() { return exchangeApiCallLimit; } }
修改RateService依赖注入Constants
@Service public class RateService { private final Constants constants; private final RestTemplate restTemplate; private final RateRepository rateRepository; // 构造函数注入 public RateService(Constants constants, RestTemplate restTemplate, RateRepository rateRepository) { this.constants = constants; this.restTemplate = restTemplate; this.rateRepository = rateRepository; } // 生成URL时使用Getter方法 private String getExchangeUrl(LocalDate rateDate, EnumCurrency base, List<EnumCurrency> targets) { String symbols = String.join("%2C", targets.stream().map(EnumCurrency::name).toArray(String[]::new)); return constants.getExchangeApiBaseUrl() + rateDate + "?symbols=" + symbols + "&base=" + base; } }
调整测试类
class RateServiceTest extends BaseServiceTest { @Mock private RateRepository rateRepository; @Mock private RestTemplate restTemplate; @Mock private Constants constants; @InjectMocks private RateService rateService; @Test void testCalculateRate() { MockitoAnnotations.openMocks(this); // Mock Constants的返回值 when(constants.getExchangeApiBaseUrl()).thenReturn("https://api.apilayer.com/exchangerates_data/"); when(constants.getExchangeApiApiKey()).thenReturn("default-key"); // ... 原有测试逻辑保持不变 } }
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

