Spring JUnit中如何初始化Map<String, Map<String,String>>配置属性?
解决JUnit测试中嵌套Map配置未初始化的空指针问题
问题根源
你的测试代码存在两处关键问题:
- 测试类错误添加了
@ConfigurationProperties(prefix = "someproperties"),该注解仅需作用在配置类(即SomeClient)上。 - 用
@Mock处理urlMap是错误的——Mockito生成的Mock Map调用get()方法默认返回null,这就是空指针的直接原因。我们需要的是真实的Map实例并填充配置数据,而非Mock对象。
方案一:使用Spring Boot Test上下文自动加载配置
如果希望测试时自动读取配置文件中的urlmap数据,可改用Spring Boot测试上下文,自动处理配置注入:
@SpringBootTest(classes = SomeClient.class) @EnableConfigurationProperties(SomeClient.class) @TestPropertySource(locations = "classpath:application-test.properties") public class SomeClientTest { @Autowired private SomeClient someClient; @MockBean private CustomWebClient genericWebClient; // 用@MockBean让Spring管理Mock对象 @Test public void testGetSomething() throws IOException { // someClient的urlmap已自动从application-test.properties加载数据 // 原有WebClient Mock逻辑不变 when(genericWebClient.invoketarget(any(URI.class), any(), any(Class.class), any(HttpMethod.class), any(Some.class), anyBoolean(), any(Smething.class))).thenReturn(somemonoresponse); when(someUtil.typeCastToMonoResponseEntity(any())).thenReturn(somemonoresponse); Mono<ResponseEntity<somemonoresponse>> resp = someClient.getSomething("jhfjhtf6", webExchange); StepVerifier.create(resp) .consumeNextWith(response -> assertNotNull(response)) .verifyComplete(); } }
说明:
@SpringBootTest启动Spring上下文,自动处理@ConfigurationProperties注入,urlmap会读取配置文件中的数据。@MockBean用于替换上下文里的CustomWebClient实例,避免依赖真实的WebClient组件。
方案二:手动初始化urlmap(纯Mockito环境)
若不想启动Spring上下文,继续使用MockitoExtension,可手动给someClient的urlmap赋值:
@ExtendWith(MockitoExtension.class) public class SomeClientTest { private static final String SOME_URL = "/v2/something/{someid}/resource"; @InjectMocks private SomeClient someClient; @Mock private CustomWebClient genericWebClient; @BeforeEach void setUp() { // 手动构建嵌套Map,模拟配置数据 Map<String, Map<String, String>> urlMap = new HashMap<>(); // 填充dev环境配置 Map<String, String> devMap = new HashMap<>(); devMap.put("url1", "http://something1.com"); devMap.put("url2", "http://something2.com"); urlMap.put("dev", devMap); // 填充qa环境配置 Map<String, String> qaMap = new HashMap<>(); qaMap.put("url1", "http://something1.com"); qaMap.put("url2", "http://something2.com"); urlMap.put("qa", qaMap); // 必须添加prod环境配置,避免else分支空指针 Map<String, String> prodMap = new HashMap<>(); prodMap.put("url2", "http://prod-something2.com"); urlMap.put("prod", prodMap); // 赋值给someClient的urlmap someClient.setUrlmap(urlMap); } @Test public void testGetSomething() throws IOException { String someID = "jhfjhtf6"; // 原有WebClient Mock逻辑不变 when(genericWebClient.invoketarget(any(URI.class), any(), any(Class.class), any(HttpMethod.class), any(Some.class), anyBoolean(), any(Smething.class))).thenReturn(somemonoresponse); when(someUtil.typeCastToMonoResponseEntity(any())).thenReturn(somemonoresponse); Mono<ResponseEntity<somemonoresponse>> resp = someClient.getSomething(someID, webExchange); StepVerifier.create(resp) .consumeNextWith(response -> assertNotNull(response)) .verifyComplete(); } }
说明:
- 移除测试类上多余的
@EnableConfigurationProperties、@TestPropertySource、@ConfigurationProperties注解,纯Mockito环境不处理这些Spring配置注解。 - 在
@BeforeEach中构建完整的嵌套Map,确保包含prod条目,避免else分支触发空指针。
方案三:修正Mockito的Mock使用(不推荐)
若坚持要Mock该Map,需为每个get()调用指定返回值,否则默认返回null:
@ExtendWith(MockitoExtension.class) public class SomeClientTest { @InjectMocks private SomeClient someClient; @Mock private CustomWebClient genericWebClient; @Mock private Map<String, Map<String, String>> urlMap; @BeforeEach void setUp() { // 模拟prod分支的返回值 Map<String, String> prodMap = new HashMap<>(); prodMap.put("url2", "http://prod-something2.com"); when(urlMap.get("prod")).thenReturn(prodMap); // 模拟dev分支的返回值 Map<String, String> devMap = new HashMap<>(); devMap.put("url1", "http://something1.com"); when(urlMap.get("dev")).thenReturn(devMap); // 将Mock的Map赋值给someClient someClient.setUrlmap(urlMap); } // 测试方法逻辑不变 }
说明:
- 该方式需为每个可能的
get()调用指定返回值,操作繁琐,不如直接使用真实Map便捷,因此不推荐。
内容的提问来源于stack exchange,提问作者user1354825
相关产品推荐
相关产品推荐

