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

.NET中MediatR结构的单元测试:无适配库时如何编写?

MediatR 单元测试实操方案

其实根本不需要专门的适配库——MediatR的组件都是基于接口设计的,用常规的单元测试框架(比如xUnit、NUnit)+ 模拟工具(Moq)就能搞定,下面是具体的测试场景和代码示例:

1. 直接测试请求处理程序(IRequestHandler)

处理程序本身就是普通类,直接实例化并注入依赖的模拟对象即可,完全不需要通过MediatR中介者调用。

示例代码

假设你有如下请求和处理程序:

public record CreateUserRequest(string Name) : IRequest<Guid>;

public class CreateUserHandler : IRequestHandler<CreateUserRequest, Guid>
{
    private readonly IUserRepository _userRepo;

    public CreateUserHandler(IUserRepository userRepo)
    {
        _userRepo = userRepo;
    }

    public async Task<Guid> Handle(CreateUserRequest request, CancellationToken cancellationToken)
    {
        var userId = Guid.NewGuid();
        await _userRepo.AddUserAsync(new User(userId, request.Name), cancellationToken);
        return userId;
    }
}

对应的测试代码(xUnit + Moq):

public class CreateUserHandlerTests
{
    private readonly Mock<IUserRepository> _mockUserRepo;
    private readonly CreateUserHandler _handler;

    public CreateUserHandlerTests()
    {
        _mockUserRepo = new Mock<IUserRepository>();
        _handler = new CreateUserHandler(_mockUserRepo.Object);
    }

    [Fact]
    public async Task Handle_ValidRequest_ReturnsNewUserIdAndCallsRepo()
    {
        // 准备测试数据
        var request = new CreateUserRequest("Alice");
        _mockUserRepo.Setup(x => x.AddUserAsync(It.IsAny<User>(), It.IsAny<CancellationToken>()))
                     .Returns(Task.CompletedTask);

        // 执行测试
        var result = await _handler.Handle(request, CancellationToken.None);

        // 验证结果
        Assert.NotEqual(Guid.Empty, result);
        _mockUserRepo.Verify(x => x.AddUserAsync(It.Is<User>(u => u.Name == "Alice"), It.IsAny<CancellationToken>()), Times.Once);
    }
}

2. 测试管道行为(IPipelineBehavior)

管道行为是包裹处理流程的中间件,测试时需要模拟请求和后续委托,验证行为的逻辑是否生效(比如验证、日志、异常处理等)。

示例代码

假设你有一个参数验证行为:

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IValidator<TRequest> _validator;

    public ValidationBehavior(IValidator<TRequest> validator)
    {
        _validator = validator;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        var validationResult = await _validator.ValidateAsync(request, cancellationToken);
        if (!validationResult.IsValid)
        {
            throw new ValidationException(validationResult.Errors);
        }
        return await next();
    }
}

对应的测试代码:

public class ValidationBehaviorTests
{
    private readonly Mock<IValidator<CreateUserRequest>> _mockValidator;
    private readonly ValidationBehavior<CreateUserRequest, Guid> _behavior;

    public ValidationBehaviorTests()
    {
        _mockValidator = new Mock<IValidator<CreateUserRequest>>();
        _behavior = new ValidationBehavior<CreateUserRequest, Guid>(_mockValidator.Object);
    }

    [Fact]
    public async Task Handle_InvalidRequest_ThrowsValidationException()
    {
        // 准备无效请求和验证错误
        var request = new CreateUserRequest("");
        var validationErrors = new List<ValidationFailure> { new ValidationFailure("Name", "名称不能为空") };
        _mockValidator.Setup(x => x.ValidateAsync(request, It.IsAny<CancellationToken>()))
                      .ReturnsAsync(new ValidationResult(validationErrors));

        // 验证抛出异常
        await Assert.ThrowsAsync<ValidationException>(() => 
            _behavior.Handle(request, () => Task.FromResult(Guid.NewGuid()), CancellationToken.None));
    }

    [Fact]
    public async Task Handle_ValidRequest_CallsNextDelegate()
    {
        // 准备有效请求
        var request = new CreateUserRequest("Alice");
        _mockValidator.Setup(x => x.ValidateAsync(request, It.IsAny<CancellationToken>()))
                      .ReturnsAsync(new ValidationResult());
        
        var isNextCalled = false;
        Task<Guid> NextDelegate()
        {
            isNextCalled = true;
            return Task.FromResult(Guid.NewGuid());
        }

        // 执行测试
        await _behavior.Handle(request, NextDelegate, CancellationToken.None);

        // 验证后续委托被调用
        Assert.True(isNextCalled);
    }
}

3. 测试完整的MediatR调度流程

如果需要验证MediatR是否能正确找到处理程序并完成调度,可以手动构建MediatR实例进行集成测试:

public class MediatrIntegrationTests
{
    [Fact]
    public async Task SendRequest_MediatorFindsHandlerAndReturnsResult()
    {
        // 构建服务容器并注册MediatR及依赖
        var services = new ServiceCollection();
        services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(CreateUserHandler).Assembly));
        var mockRepo = new Mock<IUserRepository>();
        services.AddSingleton(mockRepo.Object);
        
        var provider = services.BuildServiceProvider();
        var mediator = provider.GetRequiredService<IMediator>();
        var request = new CreateUserRequest("Bob");

        // 发送请求
        var result = await mediator.Send(request);

        // 验证结果
        Assert.NotEqual(Guid.Empty, result);
        mockRepo.Verify(x => x.AddUserAsync(It.Is<User>(u => u.Name == "Bob"), It.IsAny<CancellationToken>()), Times.Once);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:05:16