SuperAPI AI 工具集

moq

测试与质量

查看项目

更新于 10-16 06:39

技能介绍

用途

Moq 是 VanDaemon 项目中贯穿使用的模拟框架,用于单元测试。项目中的所有服务均注册为单例并依赖接口,因此非常适合被模拟。测试通过 Mock<T> 创建测试替身,用 Setup() 配置行为,用 Verify() 断言交互。

主要能力

  • 创建模拟实例:new Mock<ITankService>()
  • 获取被模拟的实例:mock.Object
  • 配置方法行为:.Setup(x => x.Method())
  • 同步返回值:.Returns(value)
  • 异步返回值:.ReturnsAsync(value)
  • 匹配任意参数:It.IsAny<CancellationToken>()
  • 使用谓词匹配参数:It.Is<Guid>(g => g != Guid.Empty)
  • 断言方法被调用:.Verify(x => x.Method(), Times.Once)

使用方式

基础服务模拟

// Mock a service interface
var mockTankService = new Mock<ITankService>();
mockTankService
    .Setup(x => x.GetAllTanksAsync(It.IsAny<CancellationToken>()))
    .ReturnsAsync(new List<Tank> { new Tank { Id = Guid.NewGuid(), Name = "Fresh Water" } });

var controller = new TanksController(mockTankService.Object);

带参数匹配的模拟

var mockControlService = new Mock<IControlService>();
mockControlService
    .Setup(x => x.SetControlStateAsync(
        It.Is<Guid>(id => id != Guid.Empty),
        It.IsAny<object>(),
        It.IsAny<CancellationToken>()))
    .ReturnsAsync(true);

模拟 Logger(通过 ILogger 使用 Serilog)

var mockLogger = new Mock<ILogger<TankService>>();
var service = new TankService(mockLogger.Object, mockFileStore.Object);

// Verify logging occurred (Moq can't verify extension methods directly)
// Use LoggerFactory or just trust the implementation

模拟 JsonFileStore

var mockFileStore = new Mock<IJsonFileStore>();
mockFileStore
    .Setup(x => x.LoadAsync<List<Tank>>("tanks.json", It.IsAny<CancellationToken>()))
    .ReturnsAsync(testTanks);

模拟 SignalR Hub 上下文

var mockHubContext = new Mock<IHubContext<TelemetryHub>>();
var mockClients = new Mock<IHubClients>();
var mockGroup = new Mock<IClientProxy>();

mockHubContext.Setup(x => x.Clients).Returns(mockClients.Object);
mockClients.Setup(x => x.Group("tanks")).Returns(mockGroup.Object);

适用场景

  • 编写需要隔离依赖的单元测试
  • 验证方法调用
  • 模拟服务接口、日志、文件存储、SignalR Hub 上下文等依赖

限制

  • Moq 无法直接验证扩展方法(如日志扩展方法),需改用 LoggerFactory 或信任实现。

相关技能

  • 测试结构与断言参见 xunit 技能
  • 可读断言参见 fluent-assertions 技能
  • 与模拟配合使用的 async/await 模式参见 csharp 技能