C# 模拟服务提供者GetServices

C# 模拟服务提供者GetServices,c#,asp.net-core,dependency-injection,moq,C#,Asp.net Core,Dependency Injection,Moq,我很难测试一个工厂,它使用.net核心ServiceProvider返回给定逻辑的特定实现 using (var scope = _serviceProvider.CreateScope()) { var services = scope.ServiceProvider.GetServices<IUrlProcessor>(); } 使用(var scope=\u serviceProvider.CreateScope()) { var services=scope.Ser

我很难测试一个工厂,它使用.net核心
ServiceProvider
返回给定逻辑的特定实现

using (var scope = _serviceProvider.CreateScope())
{
    var services = scope.ServiceProvider.GetServices<IUrlProcessor>();
}
使用(var scope=\u serviceProvider.CreateScope())
{
var services=scope.ServiceProvider.GetServices();
}
我有点担心

var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(m => m.GetService(typeof(IEnumerable<IUrlProcessor>)))
    .Returns(new List<IUrlProcessor>() {
        new PassthruProcessor()
    });
var serviceProvider=new Mock();
serviceProvider.Setup(m=>m.GetService(typeof(IEnumerable)))
.Returns(新列表(){
新的直通处理器()
});

GetServices
似乎可以工作,但是
CreateScope
调用刚好通过一个异常。这是一个扩展方法,我不知道我应该模拟什么样的类,以便
CreateScope
调用可以工作。

在这种情况下,创建返回更多模拟的模拟可能没有帮助。您的类依赖于
IServiceProvider
,您需要调用
CreateScope()

模拟IServiceProvider以返回另一个模拟在功能上与使用真实的
服务提供者
并将其配置为返回模拟相同。不同之处在于,如果使用真正的
服务提供商
,则不必模拟
CreateScope

(我完全回避了关于何时何地依赖IServiceProvider的问题。)

下面是一个大大简化的示例:

依赖于IServiceProvider的类:

public class ScopedFooFactory : IFooFactory
{
    private readonly IServiceProvider _serviceProvider;

    public ScopedFooFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IFoo CreateFoo()
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            return scope.ServiceProvider.GetService<IFoo>();
        }
    }
}
公共类作用域FooFactory:IFooFactory
{
私有只读服务器ViceProvider\u服务提供商;
公共范围的食品工厂(IServiceProvider服务提供商)
{
_服务提供者=服务提供者;
}
公共ifoocreatefoo()
{
使用(var scope=\u serviceProvider.CreateScope())
{
返回scope.ServiceProvider.GetService();
}
}
}
…和一些单元测试代码:

var serviceCollection = new ServiceCollection();
var fooMock = new Mock<IFoo>();
serviceCollection.AddScoped<IFoo>(provider => fooMock.Object);
var serviceProvider = serviceCollection.BuildServiceProvider();
var subject = new ScopedFooFactory(serviceProvider);
var foo = subject.CreateFoo();
Assert.AreSame(fooMock.Object, foo);
var servicecolection=new servicecolection();
var fooMock=new Mock();
serviceCollection.AddScoped(provider=>fooMock.Object);
var serviceProvider=servicecolection.BuildServiceProvider();
var subject=新的ScopedFoodFactory(服务提供商);
var foo=subject.CreateFoo();
Assert.arame(fooMock.Object,foo);

对我来说,这比创建返回更多模拟的模拟更简单、更容易。
IServiceProvider
上的
CreateScope
是一种扩展方法,可以解析
IServiceScopeFactory
并调用
CreateScope
。您可以注册一个
IServiceScopeFactory
mock object.FWIW,创建一个返回
mock
mock在功能上与创建一个返回mock的真实
ServiceProvider
相同。我想是6/5打。简而言之,这就是为什么服务定位器是一种反模式。当不注入依赖项时,就不能对它们进行抽象,从而使代码更难测试。有了这样一个解耦的服务集合,这样做比像以前那样模拟iSeries Provider要简单得多。哦,我有一个小毛病,与一般答案无关。在测试的示例类中,您从使用范围内返回
IFoo
。这不会导致
IFoo
在返回时超出范围时也被处理。它并没有带走您的总体答案,只是好奇您是如何看待使用范围中的流程的。在这种情况下,
IFoo
不是一次性的,但即使是一次性的,也不会在单元测试中处理。当我写下这个答案的时候,我发现有一些关于我所掩盖的范围的细节。我的怀疑是,就单元测试而言,只要创建了所需的对象,范围就没有那么重要。我以前没有注意代码中的这一点。是的,这是个问题。您不能创建一个作用域,然后返回一个超出该作用域的服务。一旦using语句超出范围,从中创建的任何内容都将超出范围。基本上,你最终会得到一个空引用。我不怀疑你-我很确定我是那个遗漏了什么的人-但是为什么测试通过了。。。我认为在这种情况下,只要
IFoo
是被嘲笑的对象,我们就没事,因为我们并不在乎它是否被处置。但我明白这一点。如果OP真的这样做了,那么问题将出现在“真正的”使用中,而不是单元测试中。我会修正我的例子。