【问题标题】:How to reset autofac container?如何重置 autofac 容器?
【发布时间】:2013-09-11 13:43:23
【问题描述】:

在我的测试中,我设置了一个 autofac 容器,它返回了一些真实的实现和一些模拟(数据库、外部系统)。

问题是每次测试后我都会处理容器并创建一个新容器: Autofac.IContainer.Dispose()Container = builder.Build(); 已经注册的实例还在。

如何重置容器,使其再次“像新的一样”?

我想做的原因是 - 我想用另一个替换一个模拟实例。它正在注册为 Singleton。

---- 编辑

感谢您的回答。我决定添加更多代码并描述我真正想要实现的目标。但这实际上是另一个话题(可能已经回答了问题 - 单元测试 CQRS)。

我的应用包含静态 IContainer 属性:

public static IContainer Container { get; private set; }

在每次测试执行后,我通过调用这两个方法再次创建它:

public static ContainerBuilder Compose(IEnumerable<DependencyProvider> dependencyProviders)
{
    var collection = dependencyProviders as List<DependencyProvider> ?? dependencyProviders.ToList();
    var included = new HashSet<DependencyProvider>(collection);
    var includedTypes = new HashSet<Type>(collection.Select(x => x.GetType()));
    var currentWorkingSet = new List<DependencyProvider>(collection);

    while (true)
    {
        var candidates = currentWorkingSet.SelectMany(x => x.GetDependencies());
        var typesToBeAdded = candidates.Where(x => !includedTypes.Contains(x)).Distinct().ToList();
        if (typesToBeAdded.Any() == false)
            break;

       currentWorkingSet.Clear();
       foreach (var type in typesToBeAdded)
       {
            includedTypes.Add(type);
            var instance = CreateInstance(type);
            included.Add(instance);
            currentWorkingSet.Add(instance);
       }
    }

return BuildContainer(included);
}

TestDependencyProvider dependencyProvider = new TestDependencyProvider()
var builder = Compose(new[] { dependencyProvider });
Container = builder.Build();

TestDependencyProvider 是为每个测试创建的,并包含 moqed 实例。它注册这些模拟,x.GetDependencies() 使用原始容器注册,即container.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsClosedTypesOf(typeof(IAggregateBusinessRuleForEvent&lt;,&gt;));

我最感兴趣的类型是IAggregateBusinessRuleForEvent&lt;,&gt;) 的实现之一。

public class RuleA: IAggregateBusinessRuleForEvent<AEvent, Something>
{
    private readonly IDependency _dependency;

    public RejectCompanyNameRule(IDependency dependency)
    {
         _dependency = dependency;
    }
}

因此,即使我再次创建此容器,RuleA 仍然存在,并且我的所有测试都使用具有相同 _dependency 的相同实例:/

目前还不清楚为什么代码看起来是这样的,我正在尝试通过添加测试来理解它......

------- 编辑 2

按照 Jimmy 的建议,我使用 Updateme 实现了一个示例

public interface IExample<T>
{
    void Hello();
}

public interface IExampleDependecy
{
    void SaySomething();
}

public class Example : IExample<string>
{
    private IExampleDependecy _dependecy;

    public Example(IExampleDependecy dependecy)
    {
        _dependecy = dependecy;
    }

    public void Hello()
    {
        Console.WriteLine("Hello");
        _dependecy.SaySomething();
    }
}

[TestMethod]
public void T()
{
    // first test
    var builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsClosedTypesOf(typeof(IExample<>));
    var mockA = new Moq.Mock<IExampleDependecy>();
    mockA.Setup(d => d.SaySomething()).Callback(() => Console.WriteLine("A"));

    builder.RegisterInstance(mockA.Object).SingleInstance();
    var container = builder.Build();

    var sample1 = container.Resolve<IExample<string>>();
    sample1.Hello();



    // new test using same container
    var updater = new ContainerBuilder();
    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsClosedTypesOf(typeof(IExample<>));
    var mockB = new Moq.Mock<IExampleDependecy>();
    mockB.Setup(d => d.SaySomething()).Callback(() => Console.WriteLine("B"));
    builder.RegisterInstance(mockB.Object).SingleInstance();
    updater.Update(container); // overwrites existing registrations

    var sample2 = container.Resolve<IExample<string>>();
    sample2.Hello();
}

结果是:

Hello
A
Hello
A

【问题讨论】:

  • 为什么在测试中使用 DI 容器而不是 Mocking 框架(例如:Moq)?
  • 我不是 100% 确定我做得对,但这里的 scnario 是 CQRS,事件溯源 MVC 应用程序。我的测试场景是执行一个命令,并且正确执行了断言天气 - 调用了 Moq 对象。该命令由处理程序处理,处理程序在 IoC 中注册,它的依赖项由构造函数注入。这里的问题是,即使在 Dispose 之后,处理程序也不会再次创建,而是仍然存在于容器中。
  • 在单元测试中使用 DI 容器可能有一些正当理由,但这不是“正确”的做法。 stackoverflow.com/questions/2305893/…
  • 是的。你是绝对正确的。这些是真正的集成测试,使用 CQRS 框架在许多类中执行代码。我可以轻松地对我的RulaA 类进行单元测试,但我只是看不到重点:/ 这些类中的所有方法都有一两个语句 - 是否需要对它们进行单元测试? stackoverflow.com/questions/4869633/…

标签: unit-testing autofac


【解决方案1】:

默认情况下,Autofac 会用后续的注册覆盖之前的注册。这意味着除了使用新实例更新容器之外,您无需执行任何特殊操作:

var builder = new ContainerBuilder();
builder.RegisterInstance(new Sample("A")).SingleInstance();
var container = builder.Build();
var sample = container.Resolve<Sample>();
// do your test

...

// new test using same container
var updater = new ContainerBuilder();
updater.RegisterInstance(new Sample("B")).SingleInstance();
updater.Update(container); // overwrites existing registrations
var sample = container.Resolve<Sample>(); // returns "B"

【讨论】:

  • 我写了一个示例,但它不起作用:/我已将测试和结果添加到问题中。
  • @Karpik:它确实有效——在您的代码的// new test ... 部分,您需要在updater 上调用.Register 方法,不在 builder ,就像我在示例中所做的那样。
猜你喜欢
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
相关资源
最近更新 更多