C# 我应该如何在控制器中模拟SignalR HubContext进行单元测试?

C# 我应该如何在控制器中模拟SignalR HubContext进行单元测试?,c#,unit-testing,signalr,moq,asp.net-mvc-5,C#,Unit Testing,Signalr,Moq,Asp.net Mvc 5,我正在MVC5项目中使用信号器。我在控制器内进行如下呼叫: private Microsoft.AspNet.SignalR.IHubContext blogHubContext = Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<BlogHub>(); blogHubContext.Clients.All.addNewBlogToPage(RenderPartialViewToString("B

我正在MVC5项目中使用信号器。我在控制器内进行如下呼叫:

private Microsoft.AspNet.SignalR.IHubContext blogHubContext = Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<BlogHub>();
blogHubContext.Clients.All.addNewBlogToPage(RenderPartialViewToString("Blog", model));
private Microsoft.AspNet.signal.IHubContext blogHubContext=Microsoft.AspNet.signal.GlobalHost.ConnectionManager.GetHubContext();
blogHubContext.Clients.All.addNewBlogToPage(renderPartialViewToSturing(“Blog”,model));
我正在尝试在此控制器内对操作进行单元测试。在我添加信号器功能之前,单元测试一直运行良好。现在,我正试图弄清楚如何模拟Hubbon的上下文。我有两种可能

  • 我在构造函数中设置了集线器,因此有如下内容:

    private Microsoft.AspNet.SignalR.IHubContext blogHubContext;
    public BlogController(Microsoft.AspNet.SignalR.IHubContext topicHub = null){
         blogHubContext = (blogHub != null) ? blogHub : Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<BlogHub>();
    }
    
    私有Microsoft.AspNet.signal.IHubContext blogHubContext;
    公共BlogController(Microsoft.AspNet.signal.IHubContext topicHub=null){
    blogHubContext=(blogHub!=null)?blogHub:Microsoft.AspNet.signar.GlobalHost.ConnectionManager.GetHubContext();
    }
    
    然后,当我在单元测试中创建HubContext时,我可以以某种方式模拟它并将其发送给控制器。但到目前为止,我只有以下几点:

    Mock<IHubContext> blogHub = new Mock<IHubContext>();
    
    Mock blogHub=new Mock();
    
    (注意:我简化了所有内容,只关注信号器方面的内容。控制器中还使用了存储库等)

  • 或者,我考虑创建另一个类来包装集线器,然后从中调用函数来调用集线器。我认为这对于我的单元测试来说更容易模仿,但不确定这是否是个好主意


  • 感谢您的指导。或者这两种方法都可以接受?谢谢。

    更新,请查看此代码,我基于默认的MVC模板。不需要包装器类

    public class HomeController : Controller
    {
       private readonly IHomeHub _hub;
    
       public HomeController(IHomeHub hub)
       {
          _hub = hub;
       }
    
       public ActionResult Index()
       {
          _hub.Hello();
          return View();
       }
    }
    
    public interface IHomeHub
    {
       void Hello();
    }
    
    public class HomeHub : Hub, IHomeHub
    {
       public void Hello()
       {
          Clients.All.hello();
       }
    }
    
    对于单元测试:

    [TestMethod]
    public void Index()
    {
       var mockHub = new Mock<IHomeHub>();
       // Arrange
       HomeController controller = new HomeController(mockHub.Object);
    
       // Act
       ViewResult result = controller.Index() as ViewResult;
    
       // Assert
       Assert.IsNotNull(result);
       mockHub.Verify(h=>h.Hello(), Times.Once);
    }
    
    [TestMethod]
    公共空间索引()
    {
    var mockHub=new Mock();
    //安排
    HomeController=新的HomeController(mockHub.Object);
    //表演
    ViewResult=controller.Index()作为ViewResult;
    //断言
    Assert.IsNotNull(结果);
    验证(h=>h.Hello(),Times.Once);
    }
    
    我更喜欢第一个选项,因为您不需要另一层包装类,因为它不添加任何功能。@AD.Net谢谢。在这种情况下,我有什么想法可以去模仿我正在使用的功能中心。我一直在努力想如何做到这一点,这让我想到了第二个选择。你对嘲弄熟悉吗?模拟信号集线器似乎和模拟其他任何东西一样。@BenAaronson由于addNewBlogToPage函数实际上是一个由信号器调用的JS函数,我不知道如何模拟它。不过,现在就按照AD.Net的建议去做,可能会把事情重新安排到一个更好的结构中。@AndrewPolland,js函数将基于你的.Net代码,所以它对js代理的生成不会有多大影响。这看起来非常好。但我无法让它工作,因为只有在函数是静态的情况下才能从集线器(在控制器内)调用函数,而从接口实现时就不能。我的理解是这样的。抱歉,不清楚,方法Hello在Hub类上不是静态的,您可以从控制器调用它。抱歉,部分问题是我的目的。静电并不像我想的那样需要。但是,当从控制器调用方法Hello时,无法按您的方式调用客户机。它会产生以下错误:不支持使用不是由HubPipeline创建的Hub实例。我已对您的答案进行了编辑,以便它能够正常工作。希望您同意。抱歉,编辑被拒绝。但本质上不是客户端。All.hello();我必须使用var hubContext=GlobalHost.ConnectionManager.GetHubContext();hubContext.Clients.All.hello();