You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 01:07:49