如何用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
相关产品推荐
相关产品推荐

