Moq模拟Bot SDK ContinueConversationAsync抛出Unsupported expression异常
报错根因
System.ArgumentException : Unsupported expression: (TestAdapter)bot 是Moq抛出的,原因是你在Setup表达式里写了强制类型转换(TestAdapter)bot,Moq无法解析这种包含运行时类型转换的Lambda表达式,自然无法完成Mock配置。
另外你之前的Setup还有一个隐蔽错误:你配置的ContinueConversationAsync重载第二个参数是Activity类型,但你控制器里实际调用的重载第二个参数是ConversationReference类型,就算类型转换的问题解决了,Mock逻辑也不会命中。
解决方案
方案1:直接Mock基类方法(推荐,符合单元测试隔离依赖的要求)
你控制器里本身是把注入的_adapter强转为BotAdapter基类后调用方法,所以直接针对BotAdapter的对应方法重载做Mock即可,完全不需要转成TestAdapter。
- 先确认你测试类里
_adapter的声明类型改成Mock<BotAdapter>(如果之前声明的是其他派生类,统一改成Mock基类即可,因为你调用的方法是基类定义的虚方法) - 替换原来错误的Setup代码,保证参数类型和你实际调用的方法签名完全一致:
_adapter .Setup(a => a.ContinueConversationAsync( It.IsAny<string>(), It.IsAny<ConversationReference>(), It.IsAny<BotCallbackHandler>(), It.IsAny<CancellationToken>())) .Returns(Task.CompletedTask) .Verifiable();
调整后完整测试代码:
[Fact] public async Task PostTeams_ReturnsAViewResult_ContainingTheMessageSent() { // Arrange _microsoftGraphService .Setup(mock => mock.GetGraphUserDetails(It.IsAny<List<string>>(), It.IsAny<List<string>>())) .ReturnsAsync(GetUserDetails()); _cosmosDBService .Setup(repo => repo.GetItemAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())) .ReturnsAsync(GetConversationReference()); // 修正后的适配器Mock _adapter .Setup(a => a.ContinueConversationAsync( It.IsAny<string>(), It.IsAny<ConversationReference>(), It.IsAny<BotCallbackHandler>(), It.IsAny<CancellationToken>())) .Returns(Task.CompletedTask) .Verifiable(); NotifyController controller = new NotifyController( _adapter.Object, _configuration, _cosmosDBService.Object, _microsoftGraphService.Object, _environment.Object, _sendGridEmailService.Object, _teamsChannelService.Object) { ControllerContext = _controllerContext }; // Act var result = await controller.PostTeams(_proactiveMessageModel); // Assert var contentResult = Assert.IsType<ContentResult>(result); Assert.Contains("Test message", contentResult.Content); // 验证主动消息调用确实触发 _adapter.Verify(); }
方案2:直接使用TestAdapter实例,无需Mock
如果不需要完全隔离适配器逻辑,直接实例化Bot SDK自带的TestAdapter传入控制器即可,不需要用Moq包装。TestAdapter本身已经实现了ContinueConversationAsync的测试逻辑,会直接执行你传入的Bot回调,你还可以额外断言回调里发送的消息内容是否符合预期,更贴近真实运行场景:
// 不需要Mock<BotAdapter>,直接实例化测试适配器 var testAdapter = new TestAdapter(); // 构造控制器的时候直接传入testAdapter实例即可 var controller = new NotifyController( testAdapter, // 其他参数保持不变 ... );
内容的提问来源于stack exchange,提问作者dickiebow
相关产品推荐
相关产品推荐

