Signalr 如何测试一个注入了生命周期镜的信号R集线器

Signalr 如何测试一个注入了生命周期镜的信号R集线器,signalr,moq,autofac,xunit,Signalr,Moq,Autofac,Xunit,如何编写单元测试来测试集线器 这是我的集线器类: public class MyHub : Hub { private readonly ILifetimeScope _scope; private readonly IMyProvider _provider; public MyHub(ILifetimeScope scope) { scope = _scope; _provider = _scope.Resolve<IM

如何编写单元测试来测试集线器

这是我的集线器类:

public class MyHub : Hub
{
    private readonly ILifetimeScope _scope;
    private readonly IMyProvider _provider;

    public MyHub(ILifetimeScope scope)
    {
        scope = _scope;
        _provider = _scope.Resolve<IMyProvider>();
    }

    public void BroadcastMessage(int messageId)
    {
        var myMessage = _provider.GetFromDb(messageId);
        Clients.All.broadcastMessage(myMessage);
    }
}
公共类MyHub:Hub
{
私有只读ILifetimeScope\u范围;
私有只读IMyProvider\u提供程序;
公共MyHub(ILifetimeScope范围)
{
范围=_范围;
_provider=_scope.Resolve();
}
公共无效广播消息(int messageId)
{
var myMessage=_provider.GetFromDb(messageId);
Clients.All.broadcastMessage(myMessage);
}
}
我正在使用moq和xunit,我尝试过这样的方法:

//arrange
var messageId = 1;
var message = "test";
var providerMock = new Mock<IMyProvider>();
providerMock.Setup(x => x.GetFromDb(messageId).Returns(test);
var scopeMock = new Mock<ILifetimeScope>();
scopeMock.Setup(x => x.Resolve<IMyProvider>()).Returns(providerMock.Object);
var hub = new MyHub(scopeMock.Object);

//act
hub.BroadcastMessage(messageId);

//assert
providerMock.Verify(x => x.GetFromDb(messageId), Times.Once());
//排列
var-messageId=1;
var message=“test”;
var providerMock=new Mock();
Setup(x=>x.GetFromDb(messageId).Returns(test);
var scopeMock=new Mock();
scopeMock.Setup(x=>x.Resolve()).Returns(providerMock.Object);
var hub=新的MyHub(scopeMock.Object);
//表演
hub.BroadcastMessage(messageId);
//断言
Verify(x=>x.GetFromDb(messageId),Times.Once());
但这会导致一个错误:

System.NotSupportedException:不支持的表达式:x=>x.Resolve() 在设置/验证表达式中不能使用扩展方法(此处:ResolutionExtensions.Resolve)

我找到了这个答案:这意味着我可以做

using (var mock = AutoMock.GetLoose()){
    var providerMock = new Mock<IMyPRovider>();
    providerMock.Setup(x=>x.GetFromDb(messageId)).Returns(message);
    mock.Provide(providerMock.Object);
    var lifetime = mock.Create<ILifetimeScope>();
    using (var scope = lifetimeScope.BeginLifetimeScope()){
        var innerMockProvider = scope.Resolve<IMyProvider>();
        //rest of test
    }
}
使用(var mock=AutoMock.GetLoose()){
var providerMock=new Mock();
providerMock.Setup(x=>x.GetFromDb(messageId)).Returns(message);
mock.Provide(providerMock.Object);
var lifety=mock.Create();
使用(var scope=lifetimeScope.BeginLifetimeScope()){
var innerMockProvider=scope.Resolve();
//剩下的测试
}
}

但是没有定义
AutoMock.GetLoose().Provide()
这可能不是您想要的答案。但是解决方法不是模拟lifetimescope,而是简单地设置一个autofac容器以在这些测试中使用。
第二,您是否需要直接在类中注入lifetimescope?也许可以使用装饰器模式,让装饰器创建lifetimescope并解析您的类并调用它。在myhub类中去掉lifetimescope将使您的生活更轻松。让它成为其他类控制lifetimescope的工作。否则,您将将需要在所有其他类中重复此模式。您应该改为注入IMyProvider。

这就是我解决此问题的方法:

如果我将集线器更改为以下内容:

public class MyHub : Hub
{

    private readonly <Func>IMyProvider _provider;

    public MyHub(<Func>IMyProvider provider)
    {
        _provider = provider;
    }

    public void BroadcastMessage(int messageId)
    {
        var provider = _provider();
        var myMessage = provider.GetFromDb(messageId);
        Clients.All.broadcastMessage(myMessage);
    }
}
公共类MyHub:Hub
{
私有只读IMyProvider\u提供程序;
公共MyHub(IMyProvider提供程序)
{
_提供者=提供者;
}
公共无效广播消息(int messageId)
{
变量提供程序=_提供程序();
var myMessage=provider.GetFromDb(messageId);
Clients.All.broadcastMessage(myMessage);
}
}

现在我可以模拟
Func
来返回我需要的内容并忽略生存期范围

感谢您的回复。我遵循Autofac/Signal R推荐的指导原则:尽管如此,我在上面更新了一个不同的模式,请查看我的回答。如果您只注入IMyProvider,并去掉Func的内容,那就更简单了@ChristianJohansen感谢您的评论。我遇到了一个问题,MyProvider是一个空引用——依赖项链中有后续代码要求(不是我的调用)我们管理支持的signal r对象的生存期依赖项,因此注入到中心或链中的所有内容都标记为.ExternallyOwned()在我的注册中