如何测试含多依赖的Spring组件?解决JUnit测试NPE问题
问题分析
你遇到的NullPointerException核心原因是:测试过程中CacheService的实例没有被Spring容器管理,或者手动实例化时未注入其内部的genericBean和dao依赖。如果直接通过new CacheService()创建对象,Spring不会自动处理字段上的@Autowired注入,导致这些依赖为空。另外注意你的代码存在一处笔误:Transformer的doAction方法调用了cacheService.perform(o),但CacheService中定义的方法是doAction(CustomObject o),需要先统一方法名才能正常运行。
解决方案
方案1:通过Spring上下文加载完整依赖(集成测试)
如果需要验证整个依赖链的真实行为,使用Spring Boot Test相关注解让容器自动管理所有Bean的注入:
@SpringBootTest class TransformerTest { @Autowired private Transformer transformer; @Test void doAction_ShouldExecuteSuccessfully() { CustomObject testObj = new CustomObject(); transformer.doAction(testObj); // 添加你的断言逻辑,比如验证dao的fetch方法被调用 } }
如果只想加载特定组件的上下文(避免启动完整应用),可以用@ContextConfiguration指定需要的Bean:
@ContextConfiguration(classes = {Transformer.class, CacheService.class, GenericBean.class, Dao.class}) @ExtendWith(SpringExtension.class) class TransformerTest { @Autowired private Transformer transformer; // 测试方法... }
方案2:用Mockito模拟依赖(纯单元测试)
如果只需要测试Transformer的逻辑,不关心CacheService内部依赖的真实行为,用Mockito模拟相关依赖即可:
方式A:模拟CacheService的内部依赖
@ExtendWith(MockitoExtension.class) class TransformerTest { @Mock private Dao mockDao; @Mock private GenericBean mockGenericBean; @InjectMocks private CacheService cacheService; private Transformer transformer; @BeforeEach void setUp() { transformer = new Transformer(cacheService); } @Test void doAction_ShouldCallDaoFetch() { CustomObject testObj = new CustomObject(); transformer.doAction(testObj); Mockito.verify(mockDao).fetch(testObj); } }
方式B:直接模拟CacheService
如果不需要关心CacheService的内部逻辑,直接模拟它的行为:
@ExtendWith(MockitoExtension.class) class TransformerTest { @Mock private CacheService mockCacheService; private Transformer transformer; @BeforeEach void setUp() { transformer = new Transformer(mockCacheService); } @Test void doAction_ShouldCallCacheService() { CustomObject testObj = new CustomObject(); transformer.doAction(testObj); Mockito.verify(mockCacheService).doAction(testObj); // 注意统一方法名 } }
方案3:改造CacheService为构造注入(推荐最佳实践)
字段注入(@Autowired标注在字段上)不利于测试和依赖管理,改成构造注入后,测试时可以手动传入依赖,无需反射:
@Component public class CacheService { private final GenericBean genericBean; private final Dao dao; // Spring会自动装配构造方法的参数 public CacheService(GenericBean genericBean, Dao dao) { this.genericBean = genericBean; this.dao = dao; } public void doAction(CustomObject o){ dao.fetch(o); } }
对应的测试代码:
class TransformerTest { @Test void doAction_Test() { // 模拟依赖 Dao mockDao = Mockito.mock(Dao.class); GenericBean mockGenericBean = Mockito.mock(GenericBean.class); // 手动组装依赖链 CacheService cacheService = new CacheService(mockGenericBean, mockDao); Transformer transformer = new Transformer(cacheService); CustomObject testObj = new CustomObject(); transformer.doAction(testObj); // 验证行为 Mockito.verify(mockDao).fetch(testObj); } }
内容的提问来源于stack exchange,提问作者Cris Manrique
相关产品推荐
相关产品推荐

