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

如何为R2DBC DatabaseClient编写JUnit Mock类并修复报错

问题描述

在Spring Boot应用中,我通过R2DBC的DatabaseClient实现了一个Repository类,核心数据库操作代码如下:

return client.sql(sqlToSearch)
        .bind("alternateCodeType", alternateCodeType)
        .bind("alternateCode", alternateCode)
        .map(locationR2DBCMapper::apply).one();

其中locationR2DBCMapper是自定义的BiFunction映射类,代码如下:

public class LocationR2DBCMapper implements BiFunction<Row, Object, Location> {

    @Override
    public Location apply(Row row, Object o) {
        if(row.get(0)!=null)
        {
            Location location = new Location();
            location.setId(row.get("location_id", Long.class));
            location.setLocationName(row.get("location_name", String.class));
            location.setLocationType(row.get("location_type", String.class));
            location.setProjectionMsg(row.get("projection_msg", String.class));
            location.setIsFacility(row.get("is_facility",Boolean.class));
            return location;
        }
        else {
            return new Location();
        }
    }
}

我想为这个Repository类编写JUnit测试用例,Mock上述数据库调用。尝试的Mock代码如下:

@BeforeEach
void setUp() {
    databaseClientMock = Mockito.mock(DatabaseClient.class);

    locationR2DBCMapperMock = Mockito.mock(LocationR2DBCMapper.class);

    DatabaseClient databaseClientMock = mock(DatabaseClient.class);
    DatabaseClient.GenericExecuteSpec executeSpecMock = mock(DatabaseClient.GenericExecuteSpec.class);

    Mono<Location> monoResultMock = mock(Mono.class);

    locationCustomRepositoryImplMock = new LocationCustomRepositoryImpl(databaseClientMock,locationR2DBCMapperMock);
}

@Test
public void getLocationByLocationTypeTest()
{
    RowsFetchSpec<?> rowsFetchSpecMock = mock(RowsFetchSpec.class);
    when(databaseClientMock.sql(anyString())).thenReturn(executeSpecMock);
    when(executeSpecMock.map(Mockito.any(BiFunction.class))).thenReturn(rowsFetchSpecMock);
    Flux<Object> resultFluxMock = Flux.empty(); // Replace this with your own Flux instance
    when(rowsFetchSpecMock.all()).thenReturn(resultFluxMock);

    //Mockito.when(databaseClientMock.sql(anyString())).thenReturn(Flux.just(Location.builder().locationName("someName").id(Long.valueOf(123)).build()));
}

但在when(rowsFetchSpecMock.all()).thenReturn(resultFluxMock)这一行报错,提示“Cannot resolve method 'thenReturn(Flux)'”,请问如何正确Mock并修复该错误?

解决方案

报错核心原因是类型不匹配:你Mock的RowsFetchSpec<?>的all()方法返回Flux<?>,但传入的是Flux<Object>;同时业务代码实际调用的是one()方法(返回Mono<Location>),而非all(),需要调整Mock的类型和调用链。

步骤1:修正Mock泛型类型

不要使用通配符RowsFetchSpec<?>,指定具体泛型RowsFetchSpec<Location>,让all()/one()方法的返回类型与业务代码匹配。

步骤2:对齐实际调用链

业务代码最终调用的是.one()方法,因此需要Mockone()的返回值,而非all()。

步骤3:清理重复变量定义

setUp方法中重复定义了databaseClientMock,会覆盖原有变量,需删除重复定义。

完整修正后的测试代码

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import reactor.core.publisher.Mono;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class LocationCustomRepositoryImplTest {

    @Mock
    private DatabaseClient databaseClientMock;
    @Mock
    private LocationR2DBCMapper locationR2DBCMapperMock;
    private LocationCustomRepositoryImpl locationCustomRepositoryImplMock;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        DatabaseClient.GenericExecuteSpec executeSpecMock = mock(DatabaseClient.GenericExecuteSpec.class);
        RowsFetchSpec<Location> rowsFetchSpecMock = mock(RowsFetchSpec.class);

        // 构建完整调用链:sql -> bind -> bind -> map -> one
        when(databaseClientMock.sql(anyString())).thenReturn(executeSpecMock);
        // 多次bind调用返回同一个mock对象
        when(executeSpecMock.bind(anyString(), any())).thenReturn(executeSpecMock);
        when(executeSpecMock.map(any(BiFunction.class))).thenReturn(rowsFetchSpecMock);
        
        // 模拟one()方法返回预期结果
        Location mockLocation = new Location();
        mockLocation.setId(123L);
        mockLocation.setLocationName("测试地点");
        when(rowsFetchSpecMock.one()).thenReturn(Mono.just(mockLocation));

        locationCustomRepositoryImplMock = new LocationCustomRepositoryImpl(databaseClientMock, locationR2DBCMapperMock);
    }

    @Test
    void getLocationByLocationTypeTest() {
        // 调用Repository目标方法
        Mono<Location> result = locationCustomRepositoryImplMock.getLocationByAlternateCode("TYPE", "CODE");
        
        // 推荐用StepVerifier验证Reactor结果
        // StepVerifier.create(result)
        //             .expectNextMatches(location -> location.getId().equals(123L) && location.getLocationName().equals("测试地点"))
        //             .verifyComplete();
    }
}

额外说明

  • 如果要验证locationR2DBCMapper的映射逻辑,不要Mock它,使用真实实例即可测试映射是否正确。
  • 验证Reactor的Mono/Flux结果,推荐使用StepVerifier,这是Reactor测试的标准方式。

内容的提问来源于stack exchange,提问作者Sujoy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 15:47:10