使用 HubContext 进行单元测试 .net core 2.2 SignalR hub 问题

Gat*_*man 3 moq xunit signalr-hub asp.net-core

我有一个 SignalR 集线器,我希望对其进行单元测试。该集线器非常简单,只是它在构造函数中包含一个 IHubContext 参数。我遇到的问题是,任何对 UpdateData 方法进行单元测试以便我可以验证 DataModel 数据属性是否已更新的尝试都会因空引用异常而失败。到目前为止,我只能验证该方法是否被调用。是不是我没有正确模拟 HubContext 依赖项?

任何人都可以提供如何解决此问题的详细信息吗?

using Microsoft.AspNetCore.SignalR;
using Moq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Tests
{
    public class DataModel
    {
        public string Name { get; set; }
        public string Job { get; set; }

    }

    public class DataHub : Hub
    {
        private readonly IHubContext<DataHub> _hubContext;

        private DataModel data;

        public DataHub(IHubContext<DataHub> context)
        {
            _hubContext = context;
        }

        public override async Task OnConnectedAsync()
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, "DataGroup");
        }

        public void UpdateData(DataModel update)
        {
            data = update;
            _hubContext.Clients.Group("DataGroup").SendAsync("Data", data);
        }

    }

    public class DataHubTests
    {

        private DataModel update;
        private DataHub hub;

        Mock<IClientProxy> mockClientProxy = new Mock<IClientProxy>();
        Mock<IHubClients> mockClients = new Mock<IHubClients>();
        Mock<IHubContext<DataHub>> mockHubContext = new Mock<IHubContext<DataHub>>();
        Mock<IGroupManager> mockGroups = new Mock<IGroupManager>();

        public DataHubTests()
        {
            SetupData();

            mockClients.Setup(clients => clients.All).Returns(mockClientProxy.Object);

            CancellationToken cancellationToken = default(CancellationToken);
            mockGroups.Object.AddToGroupAsync("1234", "DataGroup", cancellationToken);

            mockHubContext.Setup(x => x.Clients.All).Returns(mockClientProxy.Object);            
            mockHubContext.Setup(groups => groups.Groups).Returns(mockGroups.Object);

            hub = new DataHub(mockHubContext.Object);


        }

        private void SetupData()
        {
            update = new DataModel();
            update.Name = "Bob";
            update.Job = "Builder";
        }

        [Fact]
        public void DataHubIsAThing()
        {
            Assert.NotNull(hub);
        }

        [Fact]        
        public void UpdateDataIsCalled()
        {
            // act
            hub.UpdateData(update);
            // assert
            mockClients.Verify(clients => clients.All, Times.Once);
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

小智 7

我也有同样的问题。IHubContext 被注入到我的 Hub 类的 .ctor 中,以便访问发送消息的客户端列表,但是在单元测试时,您必须模拟 IHubContext 以便 SignalR 在测试中工作。

我的中心服务 .ctor

public MyHubService(IHubContext<MyHubService> context, MyDbContext db)
{
    _context = context;
    _db = db;
}
Run Code Online (Sandbox Code Playgroud)

我的中心服务方法

public async Task SendNotification(string message)
{
    await _context.Clients.All.SendAsync("Notification", message);
}
Run Code Online (Sandbox Code Playgroud)

我的单元测试

[Fact]
public async Task SendNotification()
{
    Mock<IHubClients> mockClients = new Mock<IHubClients>();
    Mock<IClientProxy> mockClientProxy = new Mock<IClientProxy>();
    mockClients.Setup(clients => clients.All).Returns(mockClientProxy.Object);

    var hubContext = new Mock<IHubContext<MyHubService>>();
    hubContext.Setup(x => x.Clients).Returns(() => mockClients.Object);

    var db = MyDBMock.GetMock();
    MyHubService hub = new MyHubService(hubContext.Object, db);

    await hub.SendNotification("Yo! This is the unit test.");

    mockClients.Verify(clients => clients.All, Times.Once);
}
Run Code Online (Sandbox Code Playgroud)