Unittest SignalR Hubs

时间:2012-05-29 10:30:55

标签: unit-testing testing nunit signalr

我想在SignalR中测试我的Hub,最好的方法是什么?

到目前为止我已经考虑过可能的解决方案:

  • 创建可测试的中心
  • 分离类
  • 的抽象逻辑
  • Selenium(想测试较小的单位)
  • 或者忽略了一些SignalR测试功能

目前使用SignalR 0.4和NUnit作为测试框架。

7 个答案:

答案 0 :(得分:8)

这个link展示了如何使用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);  
     }  
   }  

答案 1 :(得分:5)

使用一些巧妙的技巧创建单元测试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应该很简单。

答案 2 :(得分:4)

这个问题是从不久前开始的,但无论如何我都会尽力回答。

如果你的实际集线器类中有很多逻辑,那么将逻辑抽象到一个单独的类中肯定是有意义的。我为SignalR驱动的多人游戏演示做了同样的事情。您的集线器类本身应该进行的唯一行为是与消息传递相关的行为。应该委派所有进一步的行动。

注意:这与ASP .NET MVC中的控制器设计指南非常相似:保持控制器的小型化并委派实际工作。

如果你想让SignalR的集成测试真正做一些工作,那么selenium webdriver将是一个不错的选择。但是您可能需要进行一些调整以使SignalR消息在测试环境中完美地工作。谷歌搜索“signalr selenium”(没有引号)开始正确的轨道。

有关SignalR的自动化测试的一些博文=&gt; herehere

答案 3 :(得分:2)

使用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);

答案 4 :(得分:0)

这是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();
    }
}

}

答案 5 :(得分:0)

答案 6 :(得分:0)

我的界面不在测试项目中,但这是我如何使用 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();
        }
    }
}