ASP.NET Core 3.1 - signalR integration tests - webhost fixture with auth

Viewed 1171

I write simple test for my signalR app. I create fixture for web host and start connection to hub. Unfortunately in OnConnected hub method I read Context.User.Identity.Name which is null when I start test. How to configure authentication for my webhost fixture? I'd like to test scenario where few users are connected to hub. In Production mode user first sign in using /Account/LogIn controller method, then auth cookie is created and send with every request.

public class AppFixture
    {
        public const string BaseUrl = "http://localhost:54321";

        static AppFixture()
        {
            var webhost = WebHost
                .CreateDefaultBuilder(null)
                .UseEnvironment("Testing")
                .UseStartup<Startup>()
                .UseUrls(BaseUrl)
                .Build();

           
            webhost.Start();
        }

        public string GetCompleteServerUrl(string route)
        {
            route = route?.TrimStart('/', '\\');
            return $"{BaseUrl}/{route}";
        }
    }




  public class DataHubTest
    {
        private readonly ITestOutputHelper output;
        public DataHubTest( ITestOutputHelper output)
        {
            this.output = output;
        }


        private static async Task<HubConnection> StartConnectionAsync(string hubUrl)
        {
            HubConnection connection = new HubConnectionBuilder()
               .WithUrl(hubUrl)
               .Build();
            
            
            await connection.StartAsync();
            return connection;
        }

        [Fact]
        public async Task HubConnectionEchoTestAsync()
        {
            // Arrange
            var fixture = new AppFixture();
            var url = fixture.GetCompleteServerUrl("/dataHub");
            var connection = await StartConnectionAsync(url); //TODO: User Auth? => Context.User.Identity.Name
           
            // using mock handler to verify return type and value
            var handler = new Mock<Action<string>>();
            connection.On("Echo", handler.Object);
           
            // Act
            await connection.SendAsync("echo", "Hello from testing method");
           

            // Assert
            handler.Verify(x => x(It.Is<string>(n => n == "Hello from testing method")), Times.Once());

        }





   public class DataHub: Hub {
//... skip rest of hub methods
      public override Task OnConnectedAsync()
            {
                string name = Context.User.Identity.Name; // Here .Name is null when I run test
    
                _connections.Add(name, Context.ConnectionId);
                _logger.LogInformation("User: {User} has been connected to hub. ConnID: {connectionId}", name, Context.ConnectionId);
    
                return base.OnConnectedAsync();
            }
    }
2 Answers

Hopefully this will help you

            Mock<HubCallerContext> clientContext = new Mock<HubCallerContext>();
            Mock<IHubCallerClients> clients = new Mock<IHubCallerClients>();
            Mock<IGroupManager> groups = new Mock<IGroupManager>();
            Mock<IClientProxy> proxy = new Mock<IClientProxy>();

            clientContext.Setup(c => c.ConnectionId).Returns(Guid.NewGuid().ToString);
            clientContext.Setup(c => c.User.Identity.Name).Returns("MyUser");
            clients.Setup(clients => clients.Caller).Returns(proxy.Object);

            _hub.Context = clientContext.Object;
            _hub.Groups = groups.Object;
            _hub.Clients = clients.Object;

I modified my setup based on asp.net core custom webhost artictle. Now I've stuck because I don't know how to mock hub services.

namespace IntegrationTests.Hubs
{
    public class DataHubTest : IClassFixture<CustomWebApplicationFactory<Startup>>
    {
        private readonly ITestOutputHelper _output;
        private readonly CustomWebApplicationFactory<Startup> _factory;
        public DataHubTest(CustomWebApplicationFactory<Startup> factory, ITestOutputHelper output)
        {
            _factory = factory;
            _output = output;

        }

        private static async Task<HubConnection> StartConnectionAsync(HttpMessageHandler handler, string hubName)
        {
            var hubConnection = new HubConnectionBuilder()
                .WithUrl($"ws://localhost/" + hubName, o =>
                {
                    o.HttpMessageHandlerFactory = _ => handler;
                })
            .Build();
            await hubConnection.StartAsync();

            return hubConnection;
        }

       
        [Fact]
        public async Task HubConnectionEchoTestAsync()
        {

            // Arrange - mock hub services
                   
            _factory.WithWebHostBuilder(configuration =>
            {
                configuration.ConfigureTestServices(services =>
                {
                    

                    Mock<HubCallerContext> clientContext = new Mock<HubCallerContext>();
                    Mock<IHubCallerClients> clients = new Mock<IHubCallerClients>();
                    Mock<IGroupManager> groups = new Mock<IGroupManager>();
                    Mock<IClientProxy> proxy = new Mock<IClientProxy>();

                    clientContext.Setup(c => c.ConnectionId).Returns(Guid.NewGuid().ToString);
                    clientContext.Setup(c => c.User.Identity.Name).Returns("MyUser");
                    clients.Setup(clients => clients.Caller).Returns(proxy.Object);

                   

                    var sp = services.BuildServiceProvider();
                    var hub = sp.GetService<DataHub>();

                  
                    hub.Context = clientContext.Object;
                    hub.Groups = groups.Object;
                    hub.Clients = clients.Object;    
 // ?? Inject DataHub ?

                });
            });

            // Arrange

            var connection = await StartConnectionAsync(_factory.Server.CreateHandler(), "dataHub"); //TODO: autentykacja użytk. Context.User.Identity.Name
           
            
            // using mock handler to verify return type and value
            var handler = new Mock<Action<string>>();
            connection.On("Echo", handler.Object);
            
           
            // Act
            await connection.SendAsync("echo", "Hello from testing");
           

            // Assert
            handler.Verify(x => x(It.Is<string>(n => n == "Hello from testing")), Times.Once());

        }

    }
}
Related