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

如何在C#中模拟ServiceBusProcessor实例进行单元测试

解决ServiceBusProcessor单元测试模拟问题

核心问题分析

你遇到的两个关键问题:

  1. 自定义MockProcessor使用无参构造函数,导致ServiceBusProcessor的核心属性(FullyQualifiedNamespace、EntityPath)未初始化,触发事件时抛出空引用错误。
  2. 测试流程顺序错误:先触发消息再启动MyTest.StartAsync(),此时业务逻辑的ProcessMessageAsync处理器还未注册到模拟处理器上,导致逻辑无法执行。

修复步骤

1. 修正MockProcessor实现

重写MockProcessor,手动初始化必要属性,同时简化事件触发逻辑,避免依赖未初始化的基类组件:

internal class MockProcessor : ServiceBusProcessor
{
    public MockProcessor(string fullyQualifiedNamespace, string entityPath)
        : base(fullyQualifiedNamespace, entityPath, new ServiceBusProcessorOptions())
    {
        // 显式初始化核心属性,避免空引用
        FullyQualifiedNamespace = fullyQualifiedNamespace;
        EntityPath = entityPath;
    }

    // 公开方法用于外部触发消息处理事件
    public async Task TriggerProcessMessageAsync(ProcessMessageEventArgs args)
    {
        await OnProcessMessageAsync(args);
    }

    // 直接触发注册的事件处理器,跳过基类的复杂逻辑
    protected override Task OnProcessMessageAsync(ProcessMessageEventArgs args)
    {
        return ProcessMessageAsync?.Invoke(args) ?? Task.CompletedTask;
    }

    protected override Task OnProcessErrorAsync(ProcessErrorEventArgs args)
    {
        return ProcessErrorAsync?.Invoke(args) ?? Task.CompletedTask;
    }

    // 重写启动/停止方法,无需实际运行服务
    public override Task StartProcessingAsync(CancellationToken cancellationToken = default)
    {
        return Task.CompletedTask;
    }

    public override Task StopProcessingAsync(CancellationToken cancellationToken = default)
    {
        return Task.CompletedTask;
    }
}

2. 调整测试流程顺序

先启动MyTest完成处理器注册,再触发消息事件,确保业务逻辑被执行:

[TestMethod]
public async Task MyTester()
{
    // 1. 准备模拟依赖和测试数据
    var mockReceiver = new Mock<ServiceBusReceiver>();
    mockReceiver.Setup(r => r.FullyQualifiedNamespace).Returns("test-namespace");
    mockReceiver.Setup(r => r.EntityPath).Returns("test-entity");

    var testMessage = ServiceBusModelFactory.ServiceBusReceivedMessage(messageId: "1");
    var processArgs = new ProcessMessageEventArgs(
        testMessage,
        mockReceiver.Object,
        CancellationToken.None);

    // 2. 创建MockProcessor并传入必要属性
    var mockProcessor = new MockProcessor("test-namespace", "test/mytest");

    // 3. 模拟ServiceBusClient和Builder
    var mockServiceBusClient = new Mock<ServiceBusClient>();
    mockServiceBusClient
        .Setup(x => x.CreateProcessor(
            It.IsAny<string>(), 
            It.IsAny<string>(), 
            It.IsAny<ServiceBusProcessorOptions>()))
        .Returns(mockProcessor)
        .Verifiable();

    var mockServiceBusBuilder = new Mock<ServiceBusBuilder>();
    mockServiceBusBuilder
        .Setup(x => x.CreateClient(It.IsAny<NetStateWatcherConfig>()))
        .Returns(mockServiceBusClient.Object)
        .Verifiable();

    // 4. 初始化被测试类并启动服务(完成处理器注册)
    var stopEvent = new ManualResetEventSlim(false);
    var myTest = new MyTest(mockServiceBusBuilder.Object, stopEvent);
    await myTest.StartAsync();

    try
    {
        // 5. 触发消息处理,执行业务逻辑
        await mockProcessor.TriggerProcessMessageAsync(processArgs);

        // 6. 验证业务逻辑结果(示例:验证消息被完成)
        mockReceiver.Verify(r => r.CompleteMessageAsync(
            testMessage, 
            It.IsAny<CancellationToken>()), 
            Times.Once);

        // 其他业务逻辑验证(如依赖服务调用次数等)
    }
    finally
    {
        // 确保停止服务
        await myTest.StopAsync();
    }
}

代码优化建议

将消息处理逻辑从StartAsync中抽离为独立方法,可大幅降低测试复杂度:

public class MyTest
{
    // 原有成员...

    public async Task StartAsync()
    {
        // ... 初始化processor的代码 ...

        // 注册独立的处理方法
        processor.ProcessMessageAsync += HandleMessageAsync;
        // ... 其他初始化代码 ...
    }

    // 抽离独立的消息处理方法,直接测试该方法即可
    public async Task HandleMessageAsync(ProcessMessageEventArgs args)
    {
        // 原有的业务逻辑
        await args.CompleteMessageAsync(args.Message);
    }
}

对应的简化测试:

[TestMethod]
public async Task HandleMessageAsync_ShouldCompleteMessage()
{
    var mockReceiver = new Mock<ServiceBusReceiver>();
    var testMessage = ServiceBusModelFactory.ServiceBusReceivedMessage(messageId: "1");
    var args = new ProcessMessageEventArgs(testMessage, mockReceiver.Object, CancellationToken.None);

    // 无需初始化完整的ServiceBus依赖链
    var myTest = new MyTest(null, null);

    await myTest.HandleMessageAsync(args);

    mockReceiver.Verify(r => r.CompleteMessageAsync(testMessage, It.IsAny<CancellationToken>()), Times.Once);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:11:01