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

使用Moq与XUnit测试SignalR Hub(客户端及服务端)遇阻

SignalR Hub单元测试(Moq+XUnit)问题解决

问题场景

有一个简单的SignalR Hub,服务端方法SendMessage会向所有客户端推送OnMessageReceived事件,想用Moq和XUnit编写单元测试验证服务端Hub方法是否正确调用客户端方法,但遇到扩展方法无法验证的错误。

服务端Hub代码:

public async Task SendMessage(string userName, string message)
{
    await Clients.All.SendAsync("OnMessageReceived", userName, message);
}

客户端JS代码:

connection.on("OnMessageReceived", function (userName, message) {
    console.log("this is the user Name =>", userName)
    console.log("this is the user Message =>", message)
})

原测试代码运行时报错:

System.NotSupportedException : Unsupported expression: clientProxy => clientProxy.SendAsync("OnMessageReceived", It.IsAny(), It.IsAny(), CancellationToken)
扩展方法(此处为ClientProxyExtensions.SendAsync)不能用于设置/验证表达式。

错误原因

SendAsync是ClientProxyExtensions类提供的扩展方法,Moq无法直接对扩展方法进行设置或验证。实际上SendAsync内部调用的是IClientProxy接口的SendCoreAsync方法,我们需要验证这个原始接口方法。

修正后的测试代码

[Fact]
public async Task SendMessage_ShouldBroadcastToAllClients()
{
    // 模拟依赖对象
    var mockClients = new Mock<IHubCallerClients>();
    var mockClientProxy = new Mock<IClientProxy>();

    // 设置Clients.All返回模拟的ClientProxy实例
    mockClients.Setup(clients => clients.All).Returns(mockClientProxy.Object);

    // 初始化Hub并注入模拟依赖
    var testHub = new CreateConnectionHub
    {
        Clients = mockClients.Object
    };

    // 执行待测试的Hub方法
    const string testUser = "hello";
    const string testMessage = "user";
    await testHub.SendMessage(testUser, testMessage);

    // 验证依赖调用逻辑
    // 验证Clients.All属性被获取过一次
    mockClients.Verify(clients => clients.All, Times.Once);

    // 验证底层的SendCoreAsync方法被正确调用,同时校验参数
    mockClientProxy.Verify(
        proxy => proxy.SendCoreAsync(
            "OnMessageReceived",
            It.Is<object[]>(args => args[0].Equals(testUser) && args[1].Equals(testMessage)),
            It.IsAny<CancellationToken>()),
        Times.Once);
}

额外说明

  1. 参数验证:代码中通过It.Is<object[]>精准校验了传递的用户名和消息参数,比泛用的It.IsAny<string>()更严谨,能确保业务逻辑的正确性。
  2. 客户端测试边界:服务端单元测试仅能验证Hub是否正确触发了客户端方法调用,无法覆盖JS客户端的OnMessageReceived逻辑。若要测试客户端代码,需使用Jest等JS测试框架编写前端单元测试,或通过Playwright、Selenium等工具做集成测试。
  3. 空引用规避:确保Hub的Clients属性被正确注入模拟对象,从根源避免空引用异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:23:32