如何使用ClientWebSocket连接WebApplicationFactory测试主机
问题描述
我正在按如下方式搭建用于集成测试的测试服务器,该实现目前对所有HttpClient请求均可正常工作:
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTesting"); var appBuilder = new WebApplicationFactory<Program>() .WithWebHostBuilder(builder => { builder.ConfigureTestServices(services => { services.AddScoped(sp => MockUserMessagingService.Object); services.AddSingleton(sp => MockDealerStrategy.Object); }); builder.UseSetting("https_port", "8080"); }); HttpClient = appBuilder.CreateClient();
现在需要将ClientWebSocket连接到该测试服务器,具体实现方案如下:
实现方案
根据测试场景不同,有两种可选实现方式:
方案一:使用TestServer内置WebSocket客户端(推荐)
WebApplicationFactory底层的TestServer原生支持WebSocket内存转发,不需要监听真实网络端口,和现有HttpClient的内存调用逻辑完全一致,测试执行效率更高,也不会有端口冲突问题,是集成测试的首选方案。
不需要修改现有WebApplicationFactory的配置,直接通过TestServer实例创建WebSocket客户端即可:
// 保留原有appBuilder初始化逻辑不变 Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTesting"); var appBuilder = new WebApplicationFactory<Program>() .WithWebHostBuilder(builder => { builder.ConfigureTestServices(services => { services.AddScoped(sp => MockUserMessagingService.Object); services.AddSingleton(sp => MockDealerStrategy.Object); }); builder.UseSetting("https_port", "8080"); }); // 原有HttpClient初始化逻辑不变 HttpClient = appBuilder.CreateClient(); // 创建TestServer内置的WebSocket客户端 var wsClient = appBuilder.Server.CreateWebSocketClient(); // 按需配置请求,比如添加认证头、自定义请求参数 wsClient.ConfigureRequest = req => { // 示例:添加JWT认证头 // req.Headers.Add("Authorization", $"Bearer {testToken}"); }; // 直接传入WebSocket端点的相对路径即可建立连接 var webSocket = await wsClient.ConnectAsync( new Uri("/你的WebSocket接口路径", UriKind.Relative), CancellationToken.None );
连接建立后,就可以像使用普通ClientWebSocket一样,调用SendAsync/ReceiveAsync方法和服务端通信。
方案二:使用标准ClientWebSocket连接(适配需要真实网络栈的场景)
如果有特殊测试需求必须用标准ClientWebSocket走真实网络连接,就需要让测试服务启动Kestrel监听真实端口,再拿到监听地址建立连接:
- 修改
WebApplicationFactory配置,启用Kestrel监听本地空闲端口 - 启动测试主机后获取实际监听地址
- 初始化
ClientWebSocket拼接wss/ws地址建立连接
示例代码:
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTesting"); var appBuilder = new WebApplicationFactory<Program>() .WithWebHostBuilder(builder => { builder.ConfigureTestServices(services => { services.AddScoped(sp => MockUserMessagingService.Object); services.AddSingleton(sp => MockDealerStrategy.Object); }); // 启用Kestrel,监听本地随机空闲端口,避免端口冲突 builder.UseKestrel(options => { options.ListenLocalhost(0, listenOpt => { // 如果用wss协议就开启HTTPS,纯ws可以注释掉这行 listenOpt.UseHttps(); }); }); }); // 启动测试主机 await appBuilder.StartAsync(); // 原有HttpClient初始化逻辑不变 HttpClient = appBuilder.CreateClient(); // 获取Kestrel实际监听的地址 var serverAddr = appBuilder.Server .Features .Get<Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature>() .Addresses .First(); // 拼接WebSocket地址:http替换为ws,https替换为wss var wsAddr = serverAddr .Replace("http://", "ws://") .Replace("https://", "wss://") + "/你的WebSocket接口路径"; var wsClient = new ClientWebSocket(); // *仅测试场景使用*:忽略本地开发证书校验 wsClient.Options.RemoteCertificateValidationCallback = (_, _, _, _) => true; // 建立连接 await wsClient.ConnectAsync(new Uri(wsAddr), CancellationToken.None);
注意事项
- 集成测试不要写死固定端口(比如原配置的8080),用
ListenLocalhost(0)让系统分配空闲端口,避免并行测试时端口占用 - 测试执行完成后记得调用
await appBuilder.DisposeAsync()释放测试主机资源,避免进程残留 - 方案一的内存WebSocket客户端不会走真实网络过滤、SSL校验流程,如果需要测试这部分逻辑,再选择方案二
内容的提问来源于stack exchange,提问作者Peter Morris
相关产品推荐
相关产品推荐

