如何测试自定义 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++ 客户端实现对应的测试驱动:
- 启动指定版本的 ASP.NET Core 测试服务器。
- 让官方 .NET 客户端执行测试场景,确认服务器配置和预期一致。
- 让 C++ 客户端执行相同场景,检查返回值、回调参数、错误和连接状态。
- 为每个异步断言设置超时,避免失败用例一直等待。
官方客户端可以用于比较正常交互,但不能作为唯一判据。特别是畸形消息、边界输入和错误恢复等场景,仍需依据协议定义断言。
搭建可扩展的测试服务器
可以先建立一个小型 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。
将每个声称支持的“服务端版本 × 传输方式 × 编码”组合纳入自动化测试。每次修复兼容性问题时,保留能够复现问题的回归用例。相比不断增加简单的回显测试,这更有助于发现实现缺口。
备注:内容仅供参考。
