如何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
相关产品推荐
相关产品推荐

