测试 ASP.NET Core 2.1 中的 SignalR Hub





0/5 (0投票)
如何在 ASP.NET Core 2.1 中测试 SignalR Hub
引言
正如你们中的一些人可能已经听说的,ASP.NET Core 2.1.0 已于上个月底发布。此版本附带的功能之一是SignalR的发布,对于不了解它的人来说,它是一个库,允许在 Web 服务器和浏览器之间通过 HTTP 或 WebSockets 进行双向通信。
我将不会详细介绍 SignalR 的工作原理,因为上面链接中提供了相当多的文档,包括一个教程,所以在这篇文章中,我们将看看如何对 SignalR hub 进行单元测试,以便我们可以确保我们的服务器正在发送正确的信号。
此练习的代码可以在这里找到。
设置
创建 Web 项目
在这篇文章中,我们将创建一个新的 ASP.NET Core 2.1 应用程序(应该适用于所有 ASP.Core Web 应用程序模板),不带身份验证或其他详细信息,因为我们对此不感兴趣。
创建测试项目
然后我们将创建一个 .NET Core 测试项目,该项目将引用以下 Nuget 包
我认为这些是我进行单元测试时最少需要使用的软件包。
然后我们从测试项目中引用我们自己的 Web 应用程序。
创建 Hub
现在我们已经完成了项目,让我们在 Web 应用程序中注册一个简单的 hub。
让我们在 Web 应用程序中创建一个名为 SimpleHub 的文件,它看起来像这样
namespace SignalRWebApp
{
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.SignalR;
    public class SimpleHub : Hub
    {
        public override async Task OnConnectedAsync()
        {
            await Welcome();
            await base.OnConnectedAsync();
        }
        public async Task Welcome()
        {
            await Clients.All.SendAsync("welcome", new[] 
                      { new HubMessage(), new HubMessage(), new HubMessage() });
        }
    }
}
为此,我们还将创建一个 HubMessage 类,它只是一个占位符,这样我们就不会使用匿名对象,它看起来像这样
namespace SignalRWebApp
{
    public class HubMessage
    {
    }
}
这将向连接到 hub 的任何人发送一系列 3 条消息。我选择了任意数字 3,以便我也可以测试服务器发送的消息的内容和长度。
在Startup.cs中,我们添加以下行
- 在 ConfigureServices中,我们添加行services.AddSignalR();
- 在 Configure中,在app.UseMvc行之前,我们添加行app.UseSignalR(builder => builder.MapHub("/hub"));
这样,我们现在就有了一个可以供客户端连接的工作 hub。
创建客户端连接
为了进行测试,我们将使用此处找到的安装步骤来安装 JavaScript SignalR,以便我们可以在浏览器中使用它并编写我们自己的脚本,如下所示
@section Scripts
{
        $(document).ready(() => {
            const connection = new signalR.HubConnectionBuilder().withUrl("/hub").build();
            connection.on("welcome", (messages) => {
                alert(messages);
            });
            connection.start().catch(err => console.error(err.toString()));
        });
}
现在,我们只需要运行网站并查看我们收到一个带有 3 个对象的警报。
测试
现在我们进入有趣的部分,我将粘贴测试代码,然后进行分解。
namespace SignalRWebApp.Tests
{
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.SignalR;
    using Moq;
    using NUnit.Framework;
    [TestFixture]
    public class Test
    {
        [Test]
        public async Task SignalR_OnConnect_ShouldReturn3Messages()
        {
            // arrange
            Mock<IHubCallerClients> mockClients = new Mock<IHubCallerClients>();
            Mock<IClientProxy> mockClientProxy = new Mock<IClientProxy>();
            mockClients.Setup(clients => clients.All).Returns(mockClientProxy.Object);
            SimpleHub simpleHub = new SimpleHub()
            {
                Clients = mockClients.Object
            };
            // act
            await simpleHub.Welcome();
            // assert
            mockClients.Verify(clients => clients.All, Times.Once);
            mockClientProxy.Verify(
                clientProxy => clientProxy.SendCoreAsync(
                    "welcome",
                    It.Is<object[]>(o => o != null && o.Length == 1 && ((object[])o[0]).Length == 3),
                    default(CancellationToken)),
                Times.Once);
        }
    }
}
现在我们来分解一下
- 在真正的测试精神下,测试分为 3 个部分:Arrange(处理测试设置)、Act(执行我们想要测试的实际逻辑)和Assert(测试我们的逻辑是否如我们所愿)。
- SignalR hubs 实际上不包含太多逻辑,它们所做的就是将工作委托给 IHubCallerClients,然后当发送消息时,IHubCallerClients会将调用委托给IClientProxy
- 然后我们为 IHubCallerClients和IClientProxy创建一个 mock 对象
- 在第 22 行,我们设置 mock 对象,以便在调用 All属性时,返回IClientProxymock 对象的实例。
- 然后我们创建一个 SimpleHub并告诉它使用我们的 mock 对象进行Clients委托。现在我们对流程拥有完全的控制权。
- 我们调用 SimpleHub.Welcome,这会启动将消息发送到已连接客户端的整个过程。
- 在第 35 行,我们检查 IHubCallerClients的 mock 对象确实被使用了,并且只被调用了一次。
- 第 37 行更具体- 首先,我们正在检查对 SendCoreAsync的调用,因为我们在 hub 中使用的SendAsync方法实际上是一个扩展方法,它只是将参数包装到数组中并将其发送到SendCoreAsync。
- 我们检查在客户端实际要调用的方法确实命名为 welcome。
- 然后我们检查发送的消息不是 null(作为一个*and*子句,如果它是null,它会短路),它有一个Length为 1(记住,早些时候消息被包装在一个额外的数组中),并且该集合的第一个元素确实是一个包含 3 个项目(我们的消息)的对象数组。
- 由于 Moq无法验证可选参数,我们还必须为CancelationToken提供默认值。
- 最后,我们检查消息只发送了一次。
 
- 首先,我们正在检查对 
至此,我们已经测试了我们的 SignalR hub 确实按预期工作。使用这种方法,在一个单独的项目中,我还可以细致地测试所有传入的内容,包括消息只发送给一个特定客户端的情况。
这就是我们关于测试 ASP.NET Core 2.1.0 的 SignalR 的帖子。
希望您喜欢,下次再见,

