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

如何用Xunit和Moq为SQL Kata的GetViewCode方法编写单元测试

为SQL Kata异步方法编写Xunit+Moq单元测试

测试前提

假设被测类及关联模型定义如下(可根据实际场景调整):

public class ViewRepository
{
    private readonly Func<QueryFactory> _queryFactory;
    private const string TableName = "Views";

    public ViewRepository(Func<QueryFactory> queryFactory)
    {
        _queryFactory = queryFactory;
    }

    public async Task<View?> GetViewCode(string id, string code)
    {
        using var queryFactory = _queryFactory();
        var row = await queryFactory.Query(TableName)
            .Where("Id", id)
            .Where("Code", code)
            .FirstOrDefaultAsync<Row>();
        return row?.ToView();
    }
}

// 数据库映射实体
public class Row
{
    public string Id { get; set; }
    public string Code { get; set; }
    public View ToView() => new View { Id = Id, Code = Code };
}

// 业务返回模型
public class View
{
    public string Id { get; set; }
    public string Code { get; set; }
}

完整单元测试代码

using Moq;
using Xunit;
using SQLKata;
using SQLKata.Execution;

public class ViewRepositoryTests
{
    private readonly Mock<QueryFactory> _mockQueryFactory;
    private readonly Mock<Query> _mockQuery;
    private readonly ViewRepository _repository;

    public ViewRepositoryTests()
    {
        // Mock Query对象,链式调用需返回自身
        _mockQuery = new Mock<Query>();
        _mockQuery.Setup(q => q.Where(It.IsAny<string>(), It.IsAny<object>()))
                  .ReturnsSelf();

        // Mock QueryFactory,Query方法返回Mock的Query实例
        _mockQueryFactory = new Mock<QueryFactory>();
        _mockQueryFactory.Setup(f => f.Query(It.IsAny<string>()))
                         .Returns(_mockQuery.Object);

        // 注入Func<QueryFactory>到被测类
        var queryFactoryFunc = new Func<QueryFactory>(() => _mockQueryFactory.Object);
        _repository = new ViewRepository(queryFactoryFunc);
    }

    [Fact]
    public async Task GetViewCode_WhenRowExists_ReturnsCorrespondingView()
    {
        // Arrange
        var testId = "123";
        var testCode = "VIEW_001";
        var testRow = new Row { Id = testId, Code = testCode };

        _mockQuery.Setup(q => q.FirstOrDefaultAsync<Row>(It.IsAny<CancellationToken>()))
                  .ReturnsAsync(testRow);

        // Act
        var result = await _repository.GetViewCode(testId, testCode);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(testId, result.Id);
        Assert.Equal(testCode, result.Code);

        // 验证调用逻辑符合预期
        _mockQueryFactory.Verify(f => f.Query("Views"), Times.Once);
        _mockQuery.Verify(q => q.Where("Id", testId), Times.Once);
        _mockQuery.Verify(q => q.Where("Code", testCode), Times.Once);
        _mockQuery.Verify(q => q.FirstOrDefaultAsync<Row>(It.IsAny<CancellationToken>()), Times.Once);
    }

    [Fact]
    public async Task GetViewCode_WhenRowDoesNotExist_ReturnsNull()
    {
        // Arrange
        var testId = "456";
        var testCode = "VIEW_002";

        _mockQuery.Setup(q => q.FirstOrDefaultAsync<Row>(It.IsAny<CancellationToken>()))
                  .ReturnsAsync((Row)null);

        // Act
        var result = await _repository.GetViewCode(testId, testCode);

        // Assert
        Assert.Null(result);

        // 验证关键调用
        _mockQueryFactory.Verify(f => f.Query("Views"), Times.Once);
        _mockQuery.Verify(q => q.Where("Id", testId), Times.Once);
        _mockQuery.Verify(q => q.Where("Code", testCode), Times.Once);
    }
}

核心测试逻辑说明

  • 链式调用Mock:SQL Kata的Query对象采用链式调用设计,所以给Where方法设置ReturnsSelf(),保证链式调用能在Mock环境下正常执行。
  • Func处理:被测类通过Func<QueryFactory>获取实例,测试时将Mock的QueryFactory包装成Func注入即可。
  • 异步方法模拟:针对FirstOrDefaultAsync<Row>用ReturnsAsync()模拟异步返回结果,用It.IsAny<CancellationToken>匹配默认的取消令牌参数。
  • 调用验证:通过Verify方法确认QueryFactory、Query的关键方法都按预期次数调用,确保业务逻辑的正确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 09:22:05