我想在 SignalR 中测试我的集线器,最好的方法是什么?
到目前为止我想到的可能的解决方案:
- 创建一个可测试的 Hub
- 分离类的抽象逻辑
- Selenium(想测试更小的单元)
- 还是忽略了一些 SignalR 测试功能
目前使用 SignalR 0.4 和 NUnit 作为测试框架。
我想在 SignalR 中测试我的集线器,最好的方法是什么?
到目前为止我想到的可能的解决方案:
目前使用 SignalR 0.4 和 NUnit 作为测试框架。
此链接显示如何使用 Moq 对 SignalR 集线器方法进行单元测试。您模拟存储库、客户端、上下文和调用者。这是来自该站点的代码,我做了一些小改动以使其与最新的 SignalR 一起使用:
public class TestableChatHub : ChatHub
{
public Mock<IChatRepository> MockChatRepository { get; private set; }
public TestableChatHub(Mock<IChatRepository> mockChatRepository)
: base(mockChatRepository.Object)
{
const string connectionId = "1234";
const string hubName = "Chat";
var mockConnection = new Mock<IConnection>();
var mockUser = new Mock<IPrincipal>();
var mockCookies = new Mock<IRequestCookieCollection>();
var mockRequest = new Mock<IRequest>();
mockRequest.Setup(r => r.User).Returns(mockUser.Object);
mockRequest.Setup(r => r.Cookies).Returns(mockCookies.Object);
Clients = new ClientProxy(mockConnection.Object, hubName);
Context = new HubCallerContext(mockRequest.Object, connectionId);
var trackingDictionary = new TrackingDictionary();
Caller = new StatefulSignalProxy(
mockConnection.Object, connectionId, hubName, trackingDictionary);
}
}
然后该站点显示您可以使用这个可测试的集线器来编写单元测试:
[TestClass]
public class ChatHubTests
{
private TestableChatHub _hub;
public void SetUpTests()
{
_hub = GetTestableChatHub();
}
[Test]
public void ExampleTest()
{
SetUpTests();
const string message = "test";
const string connectionId = "1234";
var result = _hub.Send(message);
_hub.MockChatRepository.Verify(r => r.SaveMessage(message, connectionId));
Assert.IsTrue(result);
}
private TestableChatHub GetTestableChatHub()
{
var mockRepository = new Mock<IChatRepository>();
mockRepository.Setup(m => m.SaveMessage(
It.IsAny<string>(), It.IsAny<string())).Returns(true);
return new TestableChatHub(mockRepository);
}
}
使用几个巧妙的技巧来创建单元测试 SignalR 集线器非常简单。需要注意的一件事是 SignalR 使用dynamic
了您的模拟框架可能不支持的类(我使用NSubstitute)。
public class ProjectsHub: Hub
{
public void AddProject(string id)
{
Clients.All.AddProject(id);
}
}
[TestFixture]
public class ProjectsHubTests
{
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals
{
void AddProject(string id);
}
[Test]
public void AddProject_Broadcasts()
{
// Arrange
ProjectsHub hub = new ProjectsHub();
IHubCallerConnectionContext clients =
Substitute.For<IHubCallerConnectionContext>();
ISignals signals = Substitute.For<ISignals>();
SubstituteExtensions.Returns(clients.All, signals);
hub.Clients = clients;
// Act
hub.AddProject("id");
// Assert
signals.Received(1).AddProject("id");
}
}
重写它以使用例如 Moq 应该非常简单。
这个问题是很久以前的问题了,但我还是会尽力回答。
如果您的实际集线器类中有很多逻辑,那么将逻辑抽象到一个单独的类肯定是有意义的。我为我的 SignalR 驱动的多人演示做了同样的事情。集线器类本身的唯一行为是与消息传递相关的行为。所有进一步的行动都应授权。
注意:这与 ASP .NET MVC 中的控制器设计指南非常相似:保持控制器小型化并委派实际工作。
如果您希望与 SignalR 的集成测试实际做一些工作,selenium webdriver 将是一个不错的选择。但是您可能需要进行一些调整才能使 SignalR 消息传递在测试环境中完美运行。在谷歌上搜索“signalr selenium”(不带引号)以开始正确的轨道。
使用 SignalR 2.0,您可以这样做:
// Arrange
var hub = new CodeInteractivePreviewHub();
var mockClients = new Mock<IHubCallerConnectionContext<dynamic>>();
hub.Clients = mockClients.Object;
dynamic all = new ExpandoObject();
mockClients.Setup(m => m.All).Returns((ExpandoObject)all);
// Act
var allSourceCodes = hub.InitiateCommunication(); //Change this line to your Hub's method
// Assert
Assert.IsNotNull(allSourceCodes);
这是 Iarsm 答案的修改版本,可与 XUnit 和 MOQ 一起使用。
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Moq;
using Xunit;
namespace TestLibrary {
public class ProjectsHub : Hub {
public void AddProject(string id) {
Clients.All.AddProject(id);
}
}
public class ProjectsHubTests {
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals {
void AddProject(string id);
}
[Fact]
public void AddProject_Broadcasts() {
// Arrange
ProjectsHub hub = new ProjectsHub();
var clients = new Mock<IHubCallerConnectionContext<dynamic>>();
var signals = new Mock<ISignals>();
hub.Clients = clients.Object;
signals.Setup(m => m.AddProject(It.Is<string>(s => s == "id"))).Verifiable();
clients.Setup(m => m.All).Returns(signals.Object);
// Act
hub.AddProject("id");
// Assert
signals.VerifyAll();
}
}
}
我的界面不在测试项目中,但这是我使用 NUnit 和 Moq 的方式。
using Microsoft.AspNetCore.SignalR;
using Moq;
using NUnit.Framework;
namespace TestLibrary
{
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals
{
void AddProject(string id);
}
public class ProjectsHub : Hub<ISignals>
{
public void AddProject(string id)
{
Clients.All.AddProject(id);
}
}
public class ProjectsHubTests
{
[Test]
public void AddProject_Broadcasts()
{
// Arrange
ProjectsHub hub = new ProjectsHub();
var clients = new Mock<IHubCallerClients<ISignals>>();
var signals = new Mock<ISignals>();
hub.Clients = clients.Object;
signals.Setup(m => m.AddProject(It.Is<string>(s => s == "id"))).Verifiable();
clients.Setup(m => m.All).Returns(signals.Object);
// Act
hub.AddProject("id");
// Assert
signals.VerifyAll();
}
}
}