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

Blazor Server迁移至MediatR后,如何测试INotification的预期发布?

验证MediatR通知发布的单元测试方案

问题背景

我在Blazor Server应用中将原有的事件驱动客户端更新逻辑迁移为MediatR通知模式后,功能已正常运行,但需要编写与原事件测试风格一致的单元测试——验证符合预期类型和值的INotification是否被正确发布,且全程使用实际实现、不Mock MediatR内部逻辑。

原基于事件的实现及测试(使用FluentAssertions+IMonitor)已稳定运行,迁移后MessageService改为依赖IMediator,通过_mediator.Publish发送MessagePublishedNotification通知。

解决方案:自定义测试用Mediator捕获通知

我们可以实现一个轻量的TestMediator类,它实现IMediator接口并记录所有发布的通知,以此替代真实环境中的MediatR实例,从而在测试中直接验证通知的发布情况,完全符合原测试风格且无需Mock。

1. 实现TestMediator

这个类的核心是维护一个已发布通知的列表,并重写Publish方法将通知加入列表:

public class TestMediator : IMediator
{
    // 存储所有已发布的通知
    public List<INotification> PublishedNotifications { get; } = new();

    // 处理泛型Publish调用
    public Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default) 
        where TNotification : INotification
    {
        PublishedNotifications.Add(notification);
        return Task.CompletedTask;
    }

    // 处理非泛型Publish调用
    public Task Publish(object notification, CancellationToken cancellationToken = default)
    {
        if (notification is INotification notif)
            PublishedNotifications.Add(notif);
        return Task.CompletedTask;
    }

    // 测试中无需用到的Send方法可抛出异常或留空
    public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default)
    {
        throw new NotImplementedException("测试场景无需使用Send方法");
    }

    public Task Send(IRequest request, CancellationToken cancellationToken = default)
    {
        throw new NotImplementedException("测试场景无需使用Send方法");
    }
}

2. 迁移后的业务代码

MessageService和MessagePublishedNotification的实现如下:

public class MessageService
{
    private readonly IMediator _mediator;

    public MessageService(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task<bool> PublishMessage(DateTimeOffset timestamp, string username, string message)
    {
        if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(message))
        {
            return false;
        }

        await _mediator.Publish(new MessagePublishedNotification(timestamp, username, message));
        return true;
    }
}

public class MessagePublishedNotification : INotification
{
    public MessagePublishedNotification(DateTimeOffset timestamp, string username, string message)
    {
        Timestamp = timestamp;
        Username = username;
        Message = message;
    }

    public DateTimeOffset Timestamp { get; init; }
    public string Username { get; init; }
    public string Message { get; init; }
}

3. 编写单元测试

测试逻辑与原事件测试风格一致,通过验证TestMediator中记录的通知来确认业务行为:

public class MessageServiceTests
{
    private readonly TestMediator _testMediator;
    private readonly MessageService _messageService;

    public MessageServiceTests()
    {
        _testMediator = new TestMediator();
        _messageService = new MessageService(_testMediator);
    }

    [Fact]
    public async Task PublishMessage_ValidInput_PublishesExpectedNotification()
    {
        // Arrange
        var timestamp = DateTimeOffset.Now;
        var username = "Eric";
        var message = "Hi! :)";

        // Act
        var publishResult = await _messageService.PublishMessage(timestamp, username, message);

        // Assert
        publishResult.Should().BeTrue();
        
        // 验证通知数量、类型及参数值
        _testMediator.PublishedNotifications.Should().ContainSingle()
            .Which.Should().BeOfType<MessagePublishedNotification>()
            .Which.Matches(notification => 
                notification.Timestamp.Equals(timestamp) && 
                notification.Username.Equals(username) && 
                notification.Message.Equals(message));
    }

    [Fact]
    public async Task PublishMessage_InvalidInput_DoesNotPublishNotification()
    {
        // Arrange
        var timestamp = DateTimeOffset.Now;
        var username = string.Empty; // 无效输入
        var message = "Hi! :)";

        // Act
        var publishResult = await _messageService.PublishMessage(timestamp, username, message);

        // Assert
        publishResult.Should().BeFalse();
        _testMediator.PublishedNotifications.Should().BeEmpty();
    }
}

方案说明

  • 全程使用实际实现:TestMediator是IMediator的真实实现,仅用于测试场景捕获通知,未Mock任何组件
  • 测试风格一致:通过FluentAssertions的链式断言,与原事件测试的验证逻辑保持统一
  • 灵活性高:如果后续需要验证通知被处理逻辑,可扩展TestMediator添加Handler的注册和执行逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 08:27:11