如何单元测试含TransactionTemplate.execute的方法并验证Repository调用
如何Mock MyRepository并验证save方法执行
问题背景
你有一段Reactor风格的业务代码,通过TransactionTemplate包裹数据库操作,当前测试已Mock TransactionTemplate,但需要进一步Mock MyRepository的save方法,并通过Mockito.verify验证该方法是否被执行。
业务代码:
public Mono<MyObject> create(MyObject myobject) { return Mono.fromCallable(() -> transactionTemplate.execute(status -> { try { return myRepository.save(myobject); } catch (Exception e) { log.error(e.getMessage()); throw new ...; } })).subscribeOn(Schedulers.boundedElastic()); }
现有测试代码:
@Mock private transient TransactionTemplate transactionTemplate; @Test void test() { when(transactionTemplate.execute(any())).thenReturn(myObject); }
解决方案
要实现需求,需调整测试逻辑,让TransactionTemplate的execute回调真正执行,同时Mock并验证MyRepository,步骤如下:
第一步:Mock MyRepository
在测试类中添加@Mock注解声明MyRepository的Mock实例:@Mock private MyRepository myRepository;第二步:调整TransactionTemplate的Mock逻辑
原有的thenReturn直接返回对象,不会触发回调内的save方法。需改用thenAnswer让传入的TransactionCallback回调执行:when(transactionTemplate.execute(any(TransactionCallback.class))) .thenAnswer(invocation -> { // 获取传入的回调实例 TransactionCallback<?> callback = invocation.getArgument(0); // 执行回调,触发内部的save方法 return callback.doInTransaction(null); // 测试场景下传入null的TransactionStatus即可 });同时Mock MyRepository的save方法,指定返回值:
when(myRepository.save(any(MyObject.class))).thenReturn(myObject);第三步:调用目标方法并触发响应式流
Reactor的Mono是惰性执行的,必须触发订阅才会执行整个流程,推荐用StepVerifier验证结果的同时自动触发订阅:StepVerifier.create(myService.create(myObject)) .expectNext(myObject) .verifyComplete();第四步:验证save方法的执行
使用Mockito.verify验证save方法的调用情况,可指定调用次数、参数匹配规则:verify(myRepository, times(1)).save(eq(myObject));
完整测试代码示例
import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.transaction.support.TransactionCallback; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class MyServiceTest { @Mock private TransactionTemplate transactionTemplate; @Mock private MyRepository myRepository; @InjectMocks private MyService myService; // 你的业务类实例 @Test void testCreate() { MyObject myObject = new MyObject(); // 初始化测试用对象 // 配置TransactionTemplate的Mock行为,执行传入的回调 when(transactionTemplate.execute(any(TransactionCallback.class))) .thenAnswer(invocation -> { TransactionCallback<?> callback = invocation.getArgument(0); return callback.doInTransaction(null); }); // 配置MyRepository的save方法Mock行为 when(myRepository.save(eq(myObject))).thenReturn(myObject); // 调用业务方法并验证响应式流结果 StepVerifier.create(myService.create(myObject)) .expectNext(myObject) .verifyComplete(); // 验证save方法被调用一次 verify(myRepository, times(1)).save(eq(myObject)); } }
关键说明
- 必须用
thenAnswer替代thenReturn,才能让TransactionCallback内的代码执行,进而触发save方法 - 使用
StepVerifier是Reactor测试的规范方式,既能验证流的结果,又能自动触发订阅 - 验证时可根据需求调整参数匹配器(如
eq、any)和调用次数(如times、atLeastOnce)
内容的提问来源于stack exchange,提问作者crimson589
相关产品推荐
相关产品推荐

