/PlayingWithSignalR

Try out the SignalR back-end. Unit and Integration test. JWT Authentication.

Primary LanguageC#

Playing with SignalR

This is a .Net Web API example to build back-end SignalR service.

Separate branch with the .NET Core 2.2 version.

Resources

MessageHub

  • The web application has a simple and strongly-typed Hub definition named IMessageClient, which implements the IMessageHub interface.
  • Both interfaces make it easier to implement the hub and write unit and integration tests.
  • To connect to the Hub, authentication is required, and I have used JWT for this purpose.
[Authorize]
public class MessageHub : Hub<IMessageClient>, IMessageHub
public interface IMessageClient
{
    Task ReceiveMessage(Message message);
}
public interface IMessageHub
{
    Task SendMessageToAll(string message);

    Task SendPrivateMessage(Guid userId, string message);
}

UnitTest (strongly typed Hub)

  • Mocking the strongly typed Hub becomes easier, allowing you to validate the object you intend to send to clients.
_mockMessageClient.Verify(mc => mc.ReceiveMessage(It.Is<Message>(m => ..., Times.Once);

UnitTest (non strongly typed Hub)

  • It is still possible to verify the message you send, but it is not as elegant as the strongly-typed version.
_mockClients.Verify(clients => clients.All, Times.Once);

_mockClientProxy.Verify(clientProxy =>
    clientProxy.SendCoreAsync(
        "ReceiveMessage",
        It.Is<object[]>(o => o != null && o.Length == 1 && checkMessage(o[0] as Message)),
        It.IsAny<CancellationToken>()),
    Times.Once);

IntegrationTest

  • Using the built-in TestServer like in my PlayingWithTestHost repository.
  • Bypassing ASP.NET Core authorization in integration tests.
  • Leveraging the interfaces with the nameof operator can also be advantageous for us.
// Subscribe for a message.
connection1.On<Message>(nameof(IMessageClient.ReceiveMessage), msg => ...);

// Send a message.
connection1.InvokeAsync(nameof(IMessageHub.SendMessageToAll), new Message(...));