简单注射器,可以';t覆盖现有注册

本文关键字:覆盖 注册 注射器 可以 简单 | 更新日期: 2023-09-27 18:27:14

我目前是第一次使用Simple Injector。在我的.NET项目中,我运行测试和模拟从web服务返回的数据,并像一样将对象注册到容器中

 _container.Register<IWebServiceOrder>(() => mock.Object, Lifestyle.Transient);

这很好用。但在我的测试中,我想在对包含更新数据的web服务的第二次调用中测试系统的行为,因此需要更新mock对象。

默认情况下,Simple Injector不允许覆盖现有的注册,但官方网站表示可以更改这种行为,如下所示。

https://simpleinjector.readthedocs.org/en/latest/howto.html#override-现有注册

container.Options.AllowOverridingRegistrations = true;

不幸的是,即使使用上面的代码,当我第二次尝试注册对象时,我仍然会遇到错误。

在第一次调用GetInstance、GetAllInstances和Verify 后,无法更改容器

你知道为什么会这样吗?

简单注射器,可以';t覆盖现有注册

在使用容器后替换现有注册几乎不会有您所期望的行为(这适用于所有DI库),这就是Simple Injector容器被锁定的原因。这里将对此进行更详细的描述(正如@qujck已经指出的那样)。

首先,如果您正在编写单元测试,则根本不应该使用容器。单元测试应该自己创建被测试的类,或者将这个逻辑提取到一个方便的工厂方法中,例如:

private static MailNotifier CreateMailNotifier(
    IDeposit deposit = null, ISendMail mailSender = null, ILog logger = null)
{
  return new MailNotifier(
      deposit ?? Substitute.For<IDeposit>(),
      mailSender ?? Substitute.For<ISendMail>(),
      logger ?? Substitute.For<ILog>());
}

此工厂方法是测试数据生成器模式的变体。

通过使用可选参数,它允许单元测试只指定测试期间所需的伪实现:

public void Notify_WithValidUser_LogsAMessage()
{
    // Arrange
    var user = new User();
    var logger = new FakeLogger();
    MailNotifier sut = CreateMailNotifier(logger: logger);
    // Act
    sut.Notify(user);
    // Assert
    Assert.AreEqual(expected: 1, actual: logger.LoggedMessages.Count);
}

如果您使用容器,因为手工创建被测类太麻烦,这表明被测类存在问题(很可能是违反了单一责任原则)。防止使用工具解决设计中的问题您的代码正在与您对话

然而,对于集成测试,使用容器要常见得多,但在这种情况下,您只需为每个集成测试创建一个新容器。通过这种方式,您可以毫无问题地添加或替换IWebServiceOrder