idubnori
idubnori

Reputation: 1065

Initializing HubConnection from TestServer in ASP.NET Core SignalR

Is is possible to initialize HubConnection from Microsoft.AspNetCore.TestHost.TestServer?

The example below throws HttpRequestException(Not Found) exception at await hubConnection.StartAsync();

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.AspNetCore.TestHost;
using Xunit;

namespace FunctionalTests
{
    public class PubSubScenarios
    {
        [Fact]
        public async Task SignalRHubTest_Foo()
        {
            var webHostBuilder = WebHost.CreateDefaultBuilder().UseStartup<Startup>();

            using (var testServer = new TestServer(webHostBuilder))
            {
                var hubConnection = await StartConnectionAsync(testServer.BaseAddress);                
            }
        }

        private static async Task<HubConnection> StartConnectionAsync(Uri baseUri)
        {
            var hubConnection = new HubConnectionBuilder()
                .WithUrl($"http://{baseUri.Host}/fooHub")
                .WithConsoleLogger()
                .Build();

            await hubConnection.StartAsync();

            return hubConnection;
        }
    }
}

Upvotes: 4

Views: 2929

Answers (2)

Bhargava Mummadireddy
Bhargava Mummadireddy

Reputation: 310

To make this work with Websockets as transport,

public class PingComponentTest
{
    private static WebApplicationFactory<Program> _testWebApplicationFactory = null!;
    private static HubConnection _yourWhateverHubConnection = null!;

    [ClassInitialize]
    public static void ClassInitialize(TestContext context)
    {
        _testWebApplicationFactory = new TestWebApplicationFactory<Program>();
        _yourWhateverHubConnection = new HubConnectionBuilder()
            .WithUrl($"{_testWebApplicationFactory.Server.BaseAddress}YourWhateverHub", options =>
            {
                options.Transports = HttpTransportType.WebSockets;
                options.HttpMessageHandlerFactory = _ => _testWebApplicationFactory.Server.CreateHandler();
                options.WebSocketFactory = (context, cancellationToken) =>
                {
                    var webSocketClient = _testWebApplicationFactory.Server.CreateWebSocketClient();
                    var webSocketTask = webSocketClient.ConnectAsync(context.Uri, cancellationToken);
                    return new ValueTask<WebSocket>(webSocketTask);
                };
            })
            .ConfigureLogging(logging =>
            {
                // See more messages from test server in output to better understand context
                logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Debug);
                logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Debug);
            })
            .Build();
    }

    [ClassCleanup]
    public static void ClassCleanup()
    {
        _yourWhateverHubConnection.DisposeAsync().AsTask().Wait();
        _testWebApplicationFactory.Dispose();
    }

    [TestInitialize]
    public void TestInitialize()
    {
        _yourWhateverHubConnection.StartAsync().Wait();
    }

    [TestCleanup]
    public void TestCleanup()
    {
        _yourWhateverHubConnection.StopAsync().Wait();
    }

    [TestMethod]
    public async Task InvokePing_Should_ReturnPong()
    {
        var invokePingResult = await _yourWhateverHubConnection.InvokeAsync<string>(nameof(YourWhateverHub.InvokePing));
        Assert.IsNotNull(invokePingResult);
        Assert.AreEqual(invokePingResult, "pong");
    }
}

And Hub for this shall be something like

public class YourWhateverHub : Hub
{
    public Task<string> InvokePing()
    {
        return Task.FromResult("pong");
    }
}

Solution inspired from aspnetcore issue #11888

Upvotes: 1

davidfowl
davidfowl

Reputation: 38764

You need to call testServer.CreateHandler() and pass the HttpMessageHandler to WithMessageHandler:

[Fact]
public async Task SignalRHubTest_Foo()
{
    var webHostBuilder = WebHost.CreateDefaultBuilder().UseStartup<Startup>();

    using (var testServer = new TestServer(webHostBuilder))
    {
        var hubConnection = await StartConnectionAsync(testServer.CreateHandler());                
    }
}

private static async Task<HubConnection> StartConnectionAsync(HttpMessageHandler handler)
{
    var hubConnection = new HubConnectionBuilder()
        .WithUrl($"http://test/fooHub", options =>
        {
            options.Transports = HttpTransportType.LongPolling;
            options.HttpMessageHandlerFactory = _ => handler;
        })
        .Build();

    await hubConnection.StartAsync();

    return hubConnection;
}

This won't work for websockets though (I opened an issue for this here https://github.com/aspnet/SignalR/issues/1595

Upvotes: 6

Related Questions