使用Axon框架时如何Mock Spring Boot Repository?
解决Axon Saga测试中Spring Repository Mock注入失败的问题
你遇到的问题很典型:Axon的SagaTestFixture会自行创建Saga实例,而不是使用你通过@InjectMocks初始化的那个对象,导致Mockito的Mock注入完全没生效,所以employeeRepository一直是null。下面给你两种可行的解决方案:
方案一:手动注入Mock并注册到Fixture
这种方法不需要Spring Test支持,直接修改测试代码,把Mock的Repository手动注入到Fixture使用的Saga实例中:
修改后的测试代码
@Slf4j @RunWith(MockitoJUnitRunner.class) public class ValidationSagaTest { @Mock private EmployeeRepository employeeRepository; private ValidationSaga validationSaga; private FixtureConfiguration fixture; @Before public void setUp() throws Exception { // 1. 创建Saga实例 validationSaga = new ValidationSaga(); // 2. 用反射注入私有依赖(因为Repository是private且无setter) ReflectionTestUtils.setField(validationSaga, "employeeRepository", employeeRepository); // 3. 注入Fixture提供的CommandGateway,方便验证命令发送 ReflectionTestUtils.setField(validationSaga, "commandGateway", fixture.getCommandGateway()); // 4. 初始化Fixture并注册我们的Saga实例 fixture = new SagaTestFixture<>(ValidationSaga.class); fixture.registerSaga(validationSaga); } @Test public void shouldSendApplyContextCommand_whenEmployeesExists_givenSomeEvent() { val correlationId = "correlationId"; val emp1Code = "emp1Code "; val emp2Code = "emp2Code "; val emp1Id = "emp1Id "; val emp2Id = "emp2Id "; val emp3Id = "emp3Id "; val emp3Code = "emp3Code "; when(employeeRepository.existsById(emp1Id)).thenReturn(true); when(employeeRepository.existsById(emp2Id)).thenReturn(true); fixture.givenNoPriorActivity() .whenAggregate(correlationId) .publishes(new NewMatchingDocumentAggregate.MatchingSubmittedEvent(correlationId, emp1Code, emp2Code, emp1Id, emp2Id, emp3Id, emp3Code)) .expectActiveSagas(1) .expectDispatchedCommands(new NewMatchingDocumentAggregate.ApplyContextCommand(correlationId, emp1Code, emp2Code, emp1Id, emp2Id, emp3Id, emp3Code)); } }
关键说明
- 使用
ReflectionTestUtils(来自Spring Test模块)可以绕过私有字段的访问限制,直接注入Mock对象 - 调用
fixture.registerSaga()告诉Fixture使用我们预配置好的Saga实例,而不是自行创建 - 需要确保你的测试依赖中包含
spring-test包
方案二:结合Spring Boot Test和Axon的Spring测试支持
如果你的项目已经使用Spring Boot,推荐这种更简洁的方式,让Spring来管理Bean和Mock:
修改后的测试代码
@Slf4j @SpringBootTest @RunWith(SpringRunner.class) public class ValidationSagaTest { @MockBean private EmployeeRepository employeeRepository; @Autowired private SagaTestFixture<ValidationSaga> fixture; @Test public void shouldSendApplyContextCommand_whenEmployeesExists_givenSomeEvent() { val correlationId = "correlationId"; val emp1Code = "emp1Code "; val emp2Code = "emp2Code "; val emp1Id = "emp1Id "; val emp2Id = "emp2Id "; val emp3Id = "emp3Id "; val emp3Code = "emp3Code "; when(employeeRepository.existsById(emp1Id)).thenReturn(true); when(employeeRepository.existsById(emp2Id)).thenReturn(true); fixture.givenNoPriorActivity() .whenAggregate(correlationId) .publishes(new NewMatchingDocumentAggregate.MatchingSubmittedEvent(correlationId, emp1Code, emp2Code, emp1Id, emp2Id, emp3Id, emp3Code)) .expectActiveSagas(1) .expectDispatchedCommands(new NewMatchingDocumentAggregate.ApplyContextCommand(correlationId, emp1Code, emp2Code, emp1Id, emp2Id, emp3Id, emp3Code)); } }
关键说明
@MockBean会让Spring自动替换容器中的EmployeeRepository为Mock实例,Axon的Saga会自动注入这个Mock- 需要确保你的项目依赖中包含
axon-spring-boot-starter-test(或对应版本的Axon Spring测试依赖) - 这种方式不需要手动管理Saga实例,Spring和Axon会协同完成依赖注入
额外建议
- 你的
ValidationSaga上的@Service注解可以移除,因为Axon会自行管理Saga的生命周期,Spring的@Service会额外创建一个无用的实例 - Saga中的
commandGateway标注为transient是正确的,因为Saga需要序列化,而CommandGateway是不可序列化的服务对象
内容的提问来源于stack exchange,提问作者Bartłomiej Adamczyk
相关产品推荐
相关产品推荐

