PAYATHON 2026

如何测试自定义 C++ SignalR 客户端与服务器的兼容性?

数据小许

结论

只实现“hello world”的服务器可以用于冒烟测试,但不能证明自定义客户端已完整兼容 SignalR。要尽量覆盖协议,需要结合官方协议规范、官方测试用例和自己的自动化测试驱动。

官方 ASP.NET Core 仓库中的 HubConnection 功能测试可以作为起点,其中包含回显、空值、连接启停、服务端回调、流式调用和取消等场景。这些测试依赖 .NET 客户端和仓库内部测试设施,需要将场景和断言适配到 C++ 客户端,不能直接当作适用于任意客户端的黑盒认证套件。

先确定兼容范围

为每个准备支持的 .NET LTS 服务端版本建立测试记录,明确客户端支持哪些传输方式、消息编码和功能。测试源码应选择对应的发布标签,因为 main 分支可能包含目标版本尚未提供的行为。

协议测试需要分别覆盖连接建立和 Hub 消息两个层次,依据是官方的 Transport Protocols 和 Hub Protocol。

范围建议覆盖的内容
连接建立/negotiate、传输选择、协商失败、连接标识处理
握手与编码握手成功或失败、消息边界、JSON;计划支持时加入 MessagePack
普通调用有返回值、无返回值、错误结果、服务端调用客户端
流式通信服务端流、客户端上传流、流结束、取消、流中异常
生命周期Ping、超时、主动关闭、网络中断、重新连接
数据与并发空值、Unicode、嵌套对象、多个未完成调用的结果关联

具体的预期行为应以目标版本的规范和实现为准。暂未实现的功能应标记为不支持,不能因部分测试通过就宣称完整兼容协议。

复用官方测试的场景和断言

可以先从官方功能测试中提取场景,再为 C++ 客户端实现对应的测试驱动:

  1. 启动指定版本的 ASP.NET Core 测试服务器。
  2. 让官方 .NET 客户端执行测试场景,确认服务器配置和预期一致。
  3. 让 C++ 客户端执行相同场景,检查返回值、回调参数、错误和连接状态。
  4. 为每个异步断言设置超时,避免失败用例一直等待。

官方客户端可以用于比较正常交互,但不能作为唯一判据。特别是畸形消息、边界输入和错误恢复等场景,仍需依据协议定义断言。

搭建可扩展的测试服务器

可以先建立一个小型 Hub,将回显、服务端回调和错误返回设为固定测试入口。下面的示例采用 .NET 6 及以上的最小托管写法;实际测试时应使用选定的目标 SDK。

dotnet new web -n SignalRCompat
cd SignalRCompat

将 Program.cs 替换为:

using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR();

var app = builder.Build();
app.MapHub<CompatibilityHub>("/compat");
app.Run();

public sealed class CompatibilityHub : Hub
{
    public string Echo(string value) => value;

    public Task Notify(string value) =>
        Clients.Caller.SendAsync("Receive", value);

    public string Fail() =>
        throw new HubException("expected-error");
}

启动服务器:

dotnet run --urls http://127.0.0.1:5000

C++ 测试驱动连接 http://127.0.0.1:5000/compat,至少验证:

  • 调用 Echo("你好 SignalR"),返回值与输入一致。
  • 注册 Receive 处理器,再调用 Notify,确认收到对应参数。
  • 以需要结果的方式调用 Fail,确认该调用以错误结束,且不会无限等待。

这只是基础测试入口。要逐步扩大覆盖范围,还需要增加流式方法、取消处理和其他目标场景。

单独测试消息解析器和异常路径

正常运行的服务器不容易稳定产生所有边界输入。应为客户端的消息解析器准备固定测试数据,再通过可控传输或故障注入测试异常路径。

根据 Hub Protocol,握手请求和响应始终使用 JSON,并以实际的 0x1E 字节结束。解析测试应覆盖分隔符尚未到达、一次输入包含多条消息,以及握手响应后紧跟 Hub 消息的情况。不要假设一次底层读取恰好得到一条完整协议消息。

连接协商也要单独验证。例如,协商版本 1 的 connectionToken 和 connectionId 用途不同,后续请求中的 id 应按对应协议规则填写。相关字段和错误响应见 Transport Protocols。

将每个声称支持的“服务端版本 × 传输方式 × 编码”组合纳入自动化测试。每次修复兼容性问题时,保留能够复现问题的回归用例。相比不断增加简单的回显测试,这更有助于发现实现缺口。

备注:内容仅供参考。