使用@Mock注解时ContentProperties为null,Mockito单元测试求助
我来帮你拆解一下你遇到的问题,并且给出两种针对性的解决方案:
问题根源
你当前的测试类同时混用了Mockito的@RunWith(MockitoJUnitRunner.class)和Spring的@ContextConfiguration注解,这会导致JUnit的Runner机制冲突。MockitoJUnitRunner本来负责初始化@Mock和@InjectMocks标注的字段,但一旦Spring的TestContextRunner接管测试执行,Mockito的注解处理逻辑就可能失效,进而导致@Mock声明的ContentProperties对象为null。
另外,你的PageBuilderService主类里缺少ContentProperties的依赖注入注解,这会导致即使测试中模拟了该对象,服务类也无法获取到它,这个小细节也需要修正。
解决方案一:纯Mockito单元测试(无需Spring上下文)
如果你的测试只是验证PageBuilderService的业务逻辑,不需要加载Spring容器,推荐用纯Mockito的方式:
1. 修改测试类
移除所有Spring相关的注解,只保留Mockito的注解,确保@Mock能被正确初始化:
@RunWith(MockitoJUnitRunner.class) class PageBuilderServiceTest { @Mock private CService cs; @Mock private RService rs; @Mock private ContentProperties contentProperties; // 现在@Mock会被Mockito正确初始化 @InjectMocks private PageBuilderService pageBuilderService ; @ParameterizedTest @EnumSource(HompageEnum.class) @DisplayName("Testing doIt with EnumSource") public void checkRowposition(HompageEnum homepageEnum) { // 打桩模拟行为 Mockito.when(contentProperties.getPageFolder(anyString())).thenReturn("http://google.com"); // 执行被测试方法 Queue<String> result = pageBuilderService.getStaticContent("test-url"); // 添加断言或验证逻辑,比如验证方法调用、结果是否符合预期 Mockito.verify(contentProperties).getPageFolder("test-url"); } }
2. 修正主类的依赖注入
在PageBuilderService中给contentProperties加上@Autowired注解,确保依赖能被注入:
@Service("pgService") public class PageBuilderService { @Autowired private RService rs; @Autowired private CService cs; @Autowired // 补上这个注解,让Spring或Mockito能注入该依赖 private ContentProperties contentProperties; public Queue<String> getPaginatedContent(HomepageRequest homepageRequest) { Queue<String> staticContent = getStaticContent(homepageRequest.getUrl()); return staticContent; } public Queue<String> getStaticContent(String url) { Queue<String> files = new LinkedList<>(); String dir = contentProperties.getPageFolder(url); return null; } }
解决方案二:结合Spring上下文测试(需要加载Spring配置)
如果你的测试必须依赖Spring上下文(比如需要读取配置文件、加载其他Spring组件),那么改用Spring测试框架结合@MockBean注解:
修改测试类
用Spring的测试扩展替代MockitoRunner,并用@MockBean替代@Mock,它会将模拟对象注册到Spring上下文中,确保服务类能获取到:
// JUnit 5 写法,用SpringExtension @ExtendWith(SpringExtension.class) @ActiveProfiles(profiles = "staging") @ContextConfiguration(loader = AnnotationConfigContextLoader.class , classes = {ContentProperties.class , MasterPropertiesFactory.class }) class PageBuilderServiceTest { @MockBean private CService cs; @MockBean private RService rs; @MockBean // 用@MockBean替代@Mock,让Spring上下文管理这个模拟对象 private ContentProperties contentProperties; @Autowired // 从Spring上下文注入服务类,它会自动使用@MockBean的依赖 private PageBuilderService pageBuilderService ; @ParameterizedTest @EnumSource(HompageEnum.class) @DisplayName("Testing doIt with EnumSource") public void checkRowposition(HompageEnum homepageEnum) { Mockito.when(contentProperties.getPageFolder(anyString())).thenReturn("http://google.com"); // 执行测试逻辑 Queue<String> result = pageBuilderService.getStaticContent("test-url"); // 验证或断言 } }
这种方式下,Spring会负责管理所有Bean,@MockBean创建的模拟对象会替换掉上下文中对应的真实Bean,从而实现依赖模拟。
内容的提问来源于stack exchange,提问作者Mohit H

