在最新的ASP.NET Core2.1版本中,SignalR库的发布为开发者提供了一种在Web服务器和浏览器之间进行双向通信的新方式。SignalR支持通过HTTP或WebSockets进行通信。本文不会深入探讨SignalR的工作原理,因为官方文档已经提供了丰富的教程和示例。本文将重点介绍如何对SignalR Hub进行单元测试,以确保服务器发送的信号是正确的。
要进行这项测试,首先需要设置一个ASP.NET Core2.1的Web项目,不需要添加任何身份验证或其他细节。接着,创建一个.NET Core测试项目,并引用以下NuGet包:
Microsoft.NET.Test.Sdk v15.7.2
NUnit v3.10.1
NUnit3TestAdapter v3.10.0
Moq 4.8.3
这些是进行单元测试时最基本的包。然后,从测试项目引用自己的Web应用程序。
接下来,创建一个名为SimpleHub的Hub类,如下所示:
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文件中,添加以下代码:
services.AddSignalR();
app.UseSignalR(builder => builder.MapHub("/hub"));
这样,就有一个可以工作的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 mockClients = new Mock();
Mock mockClientProxy = new Mock();
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
现在让分解一下:
按照测试的标准,测试分为三个部分:arrange(测试设置),act(要测试的实际逻辑),和assert(测试逻辑是否按预期行为)。SignalRHub本身并没有包含太多逻辑,它们所做的只是将工作委托给IHubCallerClients,而IHubCallerClients在发送消息时会将调用委托给IClientProxy。
然后为IHubCallerClients和IClientProxy创建模拟对象。在第22行,设置模拟,以便当调用All属性时,返回IClientProxy模拟的实例。
然后创建一个SimpleHub,并告诉它使用模拟对象作为其Clients委托。现在完全控制了流程。
调用SimpleHub.Welcome,这开始了向连接的客户端发送消息的整个过程。在第35行,检查IHubCallerClients模拟是否确实被使用,并且只被调用了一次。
第37行更具体:首先,检查SendCoreAsync方法的调用,这是因为Hub中使用的方法SendAsync实际上是一个扩展方法,它只是将参数包装成一个数组并发送给SendCoreAsync。检查确实要在客户端调用的方法实际上是命名为welcome。
然后检查发送的消息不是null(因为是一个and语句,如果是null,它会短路),它有一个长度为1(记得从前面的消息被包装到一个额外的数组),并且该集合的第一个元素确实是一个包含3个项目的对象数组(消息)。
还需要提供CancelationToken的默认值,因为Moq不能验证可选参数。最后,检查消息只发送了一次。