Spring测试中混合注入真实对象与Mock对象的问题:基于配置属性加载Bean时遭遇NullPointerException
解决Spring测试中SecurityProperties内部Profile对象导致的NullPointerException问题
你遇到的问题核心很明确:用@MockBean标注的SecurityProperties是一个Mockito Mock对象,默认情况下它的所有方法返回值都是null,包括getProfile(),这就导致构造ProfileService时调用securityProperties.getProfile().xxx触发NPE。下面给你几个实用的解决方案,适配不同的测试场景:
方案1:给Mock的SecurityProperties配置Profile的返回值
既然已经用@MockBean创建了SecurityProperties的Mock实例,那我们可以直接用Mockito的API让它的getProfile()返回一个Mock的Profile对象,再给这个Mock Profile设置具体属性的返回值。
示例代码:
@SpringBootTest class ProfileServiceTest { @MockBean private SecurityProperties securityProperties; // 你已经Mock好的其他依赖 @MockBean private DependencyA dependencyA; @MockBean private DependencyB dependencyB; @BeforeEach void setUp() { // 1. Mock内部的Profile对象 SecurityProperties.Profile mockProfile = Mockito.mock(SecurityProperties.Profile.class); // 2. 给Mock Profile设置各个属性的返回值 when(mockProfile.getProtocol()).thenReturn("https"); when(mockProfile.getHost()).thenReturn("test-server"); when(mockProfile.getPort()).thenReturn(9090); when(mockProfile.getEndpoint()).thenReturn("user-profile"); // 3. 让SecurityProperties的getProfile()返回这个Mock的Profile when(securityProperties.getProfile()).thenReturn(mockProfile); } @Test void testBeanLoadingWhenPropertyIsFalse() { // 启动应用时指定属性为false System.setProperty("your.boolean.property", "false"); ApplicationContext context = SpringApplication.run(YourApplication.class); // 验证加载的是ProfileServiceA assertThat(context.getBean(ProfileServiceA.class)).isNotNull(); assertThat(context).doesNotContainBean(ProfileServiceB.class); } }
方案2:注入真实的SecurityProperties实例
如果你的测试不需要Mock Profile的属性,而是想用真实的配置值,那可以直接创建一个真实的SecurityProperties对象并配置好Profile,然后通过测试配置类注入到Spring上下文。
示例代码:
@SpringBootTest class ProfileServiceTest { @MockBean private DependencyA dependencyA; @MockBean private DependencyB dependencyB; // 自定义测试配置类,注入真实的SecurityProperties @TestConfiguration static class TestSecurityConfig { @Bean public SecurityProperties securityProperties() { SecurityProperties properties = new SecurityProperties(); SecurityProperties.Profile realProfile = new SecurityProperties.Profile(); // 设置真实的Profile属性值 realProfile.setProtocol("http"); realProfile.setHost("localhost"); realProfile.setPort(8080); realProfile.setEndpoint("internal-profile"); properties.setProfile(realProfile); return properties; } } @Test void testBeanLoadingWhenPropertyIsTrue() { System.setProperty("your.boolean.property", "true"); ApplicationContext context = SpringApplication.run(YourApplication.class); assertThat(context.getBean(ProfileServiceB.class)).isNotNull(); assertThat(context).doesNotContainBean(ProfileServiceA.class); } }
方案3:用ReflectionTestUtils直接设置Profile字段
如果SecurityProperties没有提供setProfile()方法,或者你不想Mock,也可以用Spring的ReflectionTestUtils直接给它的profile字段赋值,绕过getter/setter的限制。
示例代码:
@SpringBootTest class ProfileServiceTest { @MockBean private SecurityProperties securityProperties; @MockBean private DependencyA dependencyA; @BeforeEach void setUp() { // 创建真实的Profile对象并配置属性 SecurityProperties.Profile realProfile = new SecurityProperties.Profile(); realProfile.setProtocol("http"); realProfile.setHost("test-local"); // 用反射直接设置SecurityProperties的profile字段 ReflectionTestUtils.setField(securityProperties, "profile", realProfile); } // 测试方法... }
各方案适用场景
- 方案1:适合需要灵活控制Profile属性返回值的场景,比如测试不同URL组合对Bean的影响。
- 方案2:适合需要真实配置逻辑的测试场景,不需要Mock Profile的任何行为。
- 方案3:适合
SecurityProperties没有公开的setter方法,无法通过常规方式设置Profile的情况。
内容的提问来源于stack exchange,提问作者OmriYaHoo
相关产品推荐
相关产品推荐

