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

如何Mock外部消费者调用的依赖中的Task?(MSTest+Moq)

问题描述

我是Mock技术新手,需要为MessageProcessor类的StartProcess方法编写单元测试。该方法依赖IMessageReceiver的OnMessageReceived方法,同时接收外部传入的Func<string, string, Task<bool>>委托,但我无法获取该委托的实现代码。我希望在测试中自定义该委托的执行逻辑或直接返回自定义结果,同时能触发OnMessageReceived并传入不同测试数据。目前使用MSTest和Moq,已编写部分测试代码,但不知如何调用StartProcess及完成后续测试逻辑,请求帮助。

待测试类及方法:

public class MessageProcessor
{
    private readonly IMessageReceiver _receiver;

    public MessageProcessor(IMessageReceiver receiver)
    {
        _receiver = receiver;
    }

    public async void StartProcess(Func<string, string, Task<bool>> funcToBeExecutedInTheCaller)
    {
        await _receiver.OnMessageReceived<object>("string param", async (messageReceived, cancelationToken) =>
        {
            // Some validations to the messageReceived object
            var callerParam1 = string.Empty;
            var callerParam2 = string.Empty;
            var result = await funcToBeExecutedInTheCaller(callerParam1, callerParam2);
            // validate result and do some more work            
        });
    }
}

已编写的测试代码:

[TestClass()]
public class MessageProcessorTest
{
    private Mock<IMessageReceiver> receiverMock = null!;

    [TestInitialize]
    public void TestInitialize()
    {
        receiverMock = new Mock<IMessageReceiver>();
    }

    [TestMethod()]
    public async Task On_Received_Message()
    {            
        receiverMock.Setup(o => o.OnMessageReceived("some string Param", It.IsAny<DataObject>())).Returns(Task.Run(() => {  }));
        consumer = new MessageProcessor(receiverMock.Object);
        
        try
        {       
            consumer.StartProcess(...) //No idea how to execute it here
        }
        catch (Exception ex)
        {
            Assert.Fail("No exception expected: " + ex.Message);
        }
    }
}

解决方案

1. 自定义传入的委托逻辑

调用StartProcess时,直接传入自定义的Func<string, string, Task<bool>>实现,完全控制其行为:

// 示例1:返回预设结果,同时验证传入参数
var testFunc = new Func<string, string, Task<bool>>((p1, p2) => 
{
    Assert.AreEqual(string.Empty, p1);
    Assert.AreEqual(string.Empty, p2);
    return Task.FromResult(true);
});

// 示例2:模拟异常场景
var testFuncWithException = new Func<string, string, Task<bool>>((p1, p2) => 
{
    throw new InvalidOperationException("Test exception");
});

2. 正确Mock IMessageReceiver.OnMessageReceived方法

原测试的Setup存在参数匹配错误,需修正为捕获回调委托并手动触发,模拟消息接收:

// 匹配泛型参数<object>和方法参数,捕获传入的回调
receiverMock.Setup(o => o.OnMessageReceived<object>(
    "string param",
    It.IsAny<Func<object, CancellationToken, Task>>()
))
.Returns(async (string param, Func<object, CancellationToken, Task> callback) =>
{
    // 传入自定义测试数据,触发回调执行
    var testMessage = new object(); // 可替换为任意测试对象
    await callback(testMessage, CancellationToken.None);
});

3. 完整测试代码实现

修改后的测试类包含验证委托调用、异常处理等核心逻辑:

[TestClass()]
public class MessageProcessorTest
{
    private Mock<IMessageReceiver> _receiverMock = null!;
    private MessageProcessor _processor = null!;

    [TestInitialize]
    public void TestInitialize()
    {
        _receiverMock = new Mock<IMessageReceiver>();
    }

    [TestMethod()]
    public async Task On_Received_Message_Should_Execute_Custom_Func()
    {
        bool funcWasCalled = false;

        // 自定义测试委托
        var testFunc = new Func<string, string, Task<bool>>((p1, p2) =>
        {
            funcWasCalled = true;
            Assert.AreEqual(string.Empty, p1);
            Assert.AreEqual(string.Empty, p2);
            return Task.FromResult(true);
        });

        // Mock并触发消息回调
        _receiverMock.Setup(o => o.OnMessageReceived<object>(
            "string param",
            It.IsAny<Func<object, CancellationToken, Task>>()
        ))
        .Returns(async (string param, Func<object, CancellationToken, Task> callback) =>
        {
            await callback(new object(), CancellationToken.None);
        });

        // 执行测试方法
        _processor = new MessageProcessor(_receiverMock.Object);
        _processor.StartProcess(testFunc);

        // 等待async void异步操作完成
        await Task.Delay(100);

        // 验证逻辑
        Assert.IsTrue(funcWasCalled, "自定义委托未被执行");
        _receiverMock.Verify(o => o.OnMessageReceived<object>(
            "string param",
            It.IsAny<Func<object, CancellationToken, Task>>()
        ), Times.Once);
    }

    [TestMethod()]
    public async Task On_Received_Message_Handle_Func_Failure()
    {
        // 自定义抛出异常的委托
        var testFunc = new Func<string, string, Task<bool>>((p1, p2) =>
        {
            throw new InvalidOperationException("Test error");
        });

        // Mock并触发回调
        _receiverMock.Setup(o => o.OnMessageReceived<object>(
            "string param",
            It.IsAny<Func<object, CancellationToken, Task>>()
        ))
        .Returns(async (string param, Func<object, CancellationToken, Task> callback) =>
        {
            await callback(new object(), CancellationToken.None);
        });

        _processor = new MessageProcessor(_receiverMock.Object);

        // 捕获async void方法的异常
        Exception? caughtException = null;
        try
        {
            _processor.StartProcess(testFunc);
            await Task.Delay(100);
        }
        catch (AggregateException ex)
        {
            caughtException = ex.InnerException;
        }
        catch (Exception ex)
        {
            caughtException = ex;
        }

        // 验证异常
        Assert.IsNotNull(caughtException);
        Assert.IsInstanceOfType(caughtException, typeof(InvalidOperationException));
        Assert.AreEqual("Test error", caughtException.Message);
    }
}

关键注意事项

  • async void处理:StartProcess是async void方法,无法直接await,测试中需通过短暂延迟等待异步逻辑完成;建议后续将方法改为async Task,简化测试流程。
  • 参数匹配:Mock时需严格匹配泛型参数、字符串参数值,避免因不匹配导致Mock失效。
  • 回调触发:通过Mock的Returns方法捕获回调委托并手动调用,才能让MessageProcessor内部逻辑执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 19:45:27