2017-06-09 46 views
2

我试图嘲弄SignalR广播目前在ApiController(的WebAPI),但无法完成测试的情况下,下面是我的代码嘲讽ApiController SignalR广播

SignalRHub

public class HubServer : Hub { } 

ApiControllerWithHub

public abstract class ApiControllerWithHubController<THub> : ApiController where THub : IHub 
{ 
    Lazy<IHubContext> hub = new Lazy<IHubContext>(() => GlobalHost.ConnectionManager.GetHubContext<THub>()); 

    protected IHubContext Hub 
    { 
     get { return hub.Value; } 
    } 
} 

控制器(模拟方法)

public class NotificationController : ApiControllerWithHubController<HubServer> 
{ 
    [HttpPost] 
    public HttpResponseMessage SendNotification(NotificationInput notification) 
    { 
     Hub.Clients.Group("GroupName").BroadcastCustomerGreeting("notification"); 
    } 
} 

我正在写下面的单元测试,在Mock SignalR Post的帮助下,我在这里卡住了,因为这是来自SignalR Hub的控制器的SignalR调用。

MockTest

public interface IClientContract 
{ 
    void BroadcastCustomerGreeting(string message); 
} 

[TestMethod] 
public void SendNotificationTest() 
{ 
    NotificationInput notificationInput = new NotificationInput(); 
    notificationInput.CId = "CUST001"; 
    notificationInput.CName = "Toney"; 

    // Arrange 
    var mockClients = new Mock<IHubConnectionContext<dynamic>>(); 
    var mockGroups = new Mock<IClientContract>(); 

    // Act. 
    mockGroups.Setup(_ => _.BroadcastCustomerGreeting("notification")).Verifiable(); 
    mockClients.Setup(_ => _.Group("GroupName")).Returns(mockGroups.Object); 

    // I'm stuck here 
    var controller = new NotificationController(); 

    // Act 
    HttpResponseMessage actionResult = controller.SendNotification(notificationInput); 
} 

任何帮助表示赞赏,完成/更正这个单元测试。

回答

3

需要重新设计。基地ApiController紧密耦合到集线器上下文的静态访问者。这需要重构到自己的服务中,以便通过构造函数注入来获得更大的灵活性。

​​

控制器现在需要重构以显式公开其依赖关系。

public abstract class ApiControllerWithHubController<THub> : ApiController where THub : IHub { 

    private readonly IHubContext hub; 

    public ApiControllerWithHubController(IHubContextProvider context) { 
     this.hub = context.Hub; 
    } 

    protected IHubContext Hub { 
     get { return hub; } 
    } 
} 


public class NotificationController : ApiControllerWithHubController<HubServer> { 

    public NotificationController(IHubContextProvider context) 
     : base(context) { 

    } 

    [HttpPost] 
    public IHttpActionResult SendNotification(NotificationInput notification) { 
     Hub.Clients.Group("GroupName").BroadcastCustomerGreeting("notification"); 
     return Ok(); 
    } 
} 

测试现在可以用必要的模拟依赖来执行。

[TestMethod] 
public void _SendNotificationTest() { 

    // Arrange 
    var notificationInput = new NotificationInput(); 
    notificationInput.CId = "CUST001"; 
    notificationInput.CName = "Toney"; 
    var groupName = "GroupName"; 
    var message = "notification"; 

    var mockGroups = new Mock<IClientContract>(); 
    mockGroups.Setup(_ => _.BroadcastCustomerGreeting(message)).Verifiable(); 

    var mockClients = new Mock<IHubConnectionContext<dynamic>>(); 
    mockClients.Setup(_ => _.Group(groupName)).Returns(mockGroups.Object).Verifiable(); 

    var mockHub = new Mock<IHubContext>(); 
    mockHub.Setup(_ => _.Clients).Returns(mockClients.Object).Verifiable(); 

    var mockHubProvider = new Mock<IHubContextProvider>(); 
    mockHubProvider.Setup(_ => _.Hub).Returns(mockHub.Object); 

    var controller = new NotificationController(mockHubProvider.Object); 

    // Act 
    var actionResult = controller.SendNotification(notificationInput); 

    //Assert 
    mockClients.Verify(); 
    mockGroups.Verify(); 
    mockHub.Verify(); 
} 

只要确保注册与DI容器的新服务,以便它可以注入到依赖控制器。

随着重新设计,基本控制器可以一起拆除,并直接使用中枢供应商。这是假设没有任何其他理由拥有基本控制器。