我正在尝试模拟ApiController
(WebApi)中存在的SignalR广播,但无法完成测试用例,下面是我的代码
public class HubServer : Hub { }
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调用。
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);
}
感谢任何帮助以完成/更正此单元测试。
答案 0 :(得分:4)
需要重新设计。 Base ApiController
紧密耦合到集线器上下文的静态访问器。这需要重构为自己的服务,以通过构造函数注入提供更大的灵活性。
public interface IHubContextProvider {
IHubContext Hub { get; }
}
public class HubContextProvider<THub> : IHubContextProvider where THub : IHub {
Lazy<IHubContext> hub = new Lazy<IHubContext>(() => GlobalHost.ConnectionManager.GetHubContext<THub>());
public IHubContext Hub {
get { return hub.Value; }
}
}
现在需要重构控制器以显式公开其依赖项。
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容器注册新服务,以便将其注入相关控制器。
通过重新设计,可以一起删除基本控制器,并直接使用集线器提供商。这假设没有任何其他理由拥有基本控制器。