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

如何用xUnit.net和Moq测试整洁架构+CQRS下的控制器?

用xUnit和Moq测试CQRS架构下的Product API控制器

对于CQRS架构的API控制器,核心是测试控制器的逻辑——也就是它是否正确调用IMediator,并将结果正确包装为HTTP响应。不需要重构应用层的命令/查询,只需要Mock控制器依赖的IMediator接口即可。

步骤1:准备测试依赖

确保你的xUnit测试项目已安装以下NuGet包:

  • xunit
  • xunit.runner.visualstudio(可选,方便在VS中运行)
  • Moq
  • MediatR(与API项目版本一致)
  • Microsoft.AspNetCore.Mvc(用于识别OkObjectResult等控制器返回类型)

步骤2:编写测试类

以下是针对你的GetAllProducts方法的完整测试示例:

using Xunit;
using Moq;
using MediatR;
using YourApi.Controllers; // 替换为你的控制器命名空间
using YourApi.Queries; // 替换为GetAllProductsQuery所在的命名空间
using YourApi.Models; // 替换为Product模型所在的命名空间

public class ProductsControllerTests
{
    private readonly Mock<IMediator> _mockMediator;
    private readonly ProductsController _controller;

    public ProductsControllerTests()
    {
        // 初始化Mock的IMediator实例
        _mockMediator = new Mock<IMediator>();
        // 注入Mock对象,构造控制器实例
        _controller = new ProductsController(_mockMediator.Object);
    }

    [Fact]
    public async Task GetAllProducts_ReturnsOkResult_WithExpectedProducts()
    {
        // Arrange:准备测试数据和Mock行为
        var testProducts = new List<Product>
        {
            new Product { Id = 1, Name = "Test Product 1" },
            new Product { Id = 2, Name = "Test Product 2" }
        };

        // 配置Mock:当调用Mediator.Send(GetAllProductsQuery)时,返回预设的产品列表
        _mockMediator.Setup(m => m.Send(It.IsAny<GetAllProductsQuery>(), default))
                     .ReturnsAsync(testProducts);

        // Act:执行要测试的控制器方法
        var result = await _controller.GetAllProducts();

        // Assert:验证结果是否符合预期
        // 确认返回的是OkObjectResult
        var okResult = Assert.IsType<OkObjectResult>(result);
        // 确认返回的数据是正确的产品列表
        var returnedProducts = Assert.IsType<List<Product>>(okResult.Value);
        Assert.Equal(2, returnedProducts.Count);
        Assert.Equal("Test Product 1", returnedProducts[0].Name);
    }
}

关键说明

  1. Mock的核心是隔离依赖:控制器只依赖IMediator,我们不需要关心GetAllProductsQuery的具体处理逻辑(那是QueryHandler的单元测试范围),只需要验证控制器是否正确触发查询并返回响应。
  2. 测试边界清晰:这个测试只覆盖控制器的职责——接收HTTP请求、调用Mediator、返回正确的HTTP结果。
  3. 异常场景扩展:如果需要测试Mediator抛出异常的情况,可以修改Mock配置:
    _mockMediator.Setup(m => m.Send(It.IsAny<GetAllProductsQuery>(), default))
                 .ThrowsAsync(new Exception("Query failed"));
    
    然后断言控制器返回的是StatusCodeResult或对应错误类型。

注意事项

  • 确保测试项目能访问到GetAllProductsQuery、Product等类:可以直接引用API项目,或者将这些类放在独立的类库中供API和测试项目共同引用。
  • 不要重复实现应用层逻辑:你的错误尝试是重构应用层,这完全没必要——单元测试控制器的核心是隔离外部依赖,而非重新实现业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 12:01:30