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

如何单元测试含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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:27:27