Unittest SignalR Hubs

2019-03-12 18:42发布

I Would like to test my Hub in SignalR, what is the best approach?

Possible solutions I have thought about so far:

  • Create a testable Hub
  • Abstract logic to separate class
  • Selenium (would like to test smaller units)
  • Or is it some SignalR testing features have overlooked

Currently using SignalR 0.4, and NUnit as the testing framework.

6条回答
小情绪 Triste *
2楼-- · 2019-03-12 19:05

This is modified version of Iarsm's answer, to work with XUnit and 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();
    }
}

}

查看更多
祖国的老花朵
4楼-- · 2019-03-12 19:11

This link shows how to unit test SignalR hub methods using Moq. You mock up the respository, clients, context, and the caller. Here's the code from the site, I made some minor changes to make it work with the latest 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);  
 }  
} 

Then the site shows that you can use this testable hub to write unit tests:

   [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);  
     }  
   }  
查看更多
Luminary・发光体
5楼-- · 2019-03-12 19:16

With the SignalR 2.0 you can do it this way:

// 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);
查看更多
SAY GOODBYE
6楼-- · 2019-03-12 19:19

This question is from a while ago, but I'll do my best to answer anyway.

If you have a lot of logic in your actual hub class, it would certainly make sense to abstract the logic to a separate class. I did the same for my SignalR-powered multiplayer demo. The only behaviour that should go in your hub class itself is the one related to messaging. All further action should be delegated.

Note: This is very much like the guidelines for controller design in ASP .NET MVC: Keep your controllers small and delegate the real work.

If you want integration tests with SignalR actually doing some work, selenium webdriver would be a good option. But you will probably need to do some tweaking to get the SignalR messaging working perfectly in the context of the tests. Do a google search for "signalr selenium" (without the quotes) to get started on the right track.

Some blogposts about automated tests for SignalR => here and here

查看更多
唯我独甜
7楼-- · 2019-03-12 19:26

It's quite simple to create to unit test SignalR hubs using a couple of neat tricks. One thing to note is that SignalR uses dynamic classes which might not be supported by your mocking framework (I use 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");
    }
}

Rewriting this to use e.g. Moq should be pretty simple.

查看更多
登录 后发表回答