【问题标题】:Using dependency injection in SpecFlow step-file在 SpecFlow 步骤文件中使用依赖注入
【发布时间】:2018-03-03 15:26:14
【问题描述】:

我们使用 Unity 作为我们的依赖注入框架。

我想创建一个验收测试并需要一个 DossierService 实例。
不幸的是,我得到了以下异常:

BoDi.ObjectContainerException: '接口无法解析 [...]'

[Binding]
public class DossierServiceSteps : BaseSteps
{
    private IDossierService dossierService;

    public DossierServiceSteps(IDossierService dossierService)
    {
        this.dossierService = dossierService;
    }
}
  • 究竟什么是博迪?我找不到任何有用的信息..
  • 如何让 SpecFlow 使用普通的 Unity 容器?

提前致谢

编辑: 我试过像这样使用SpecFlow.Unity

public static class TestDependencies
{
    [ScenarioDependencies]
    public static IUnityContainer CreateContainer()
    {
        var container = UnityConfig.GetConfiguredContainer();

        container.RegisterTypes(typeof(TestDependencies).Assembly.GetTypes().Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))),
            WithMappings.FromMatchingInterface,
            WithName.Default,
            WithLifetime.ContainerControlled);

        return container;
    }
}

UnityConfig 中,类型已正确注册

container.RegisterType<IDossierService, DossierService>(new InjectionConstructor(typeof(IDataService), typeof(IDossierRepository), typeof(IDbContext), true));

但我仍然遇到同样的异常。当我在TestDependenciesCreateContainer() 方法的开头放置断点时,它不会中断...

【问题讨论】:

    标签: c# .net unity-container specflow acceptance-testing


    【解决方案1】:

    我们通过实现SpecFlow RuntimePlugin 解决了这个问题。在我们的例子中,它是 Castle.Windsor,但原理是一样的。首先定义覆盖默认 SpecFlow Instance Resolver 的插件:

    public class CastleWindsorPlugin : IRuntimePlugin
    {
        public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters)
        {
            runtimePluginEvents.CustomizeScenarioDependencies += (sender, args) =>
            {
                args.ObjectContainer.RegisterTypeAs<CastleWindsorBindingInstanceResolver, IBindingInstanceResolver>();
            };
        }
    }
    

    CastleWindsorBindingInstanceResolver 中,我们需要实现单个方法:object ResolveBindingInstance(Type bindingType, IObjectContainer scenarioContainer);。该类包含容器和解析(在您的情况下为IUnityContainer 的实例。我建议注入到self 的容器实例中,以便您可以将IUnityContainer 的实例注入到SpecFlow 绑定类)

    这个插件需要在单独的程序集中,然后你将它加载到你的测试项目中,比如像这样调整app.config

    <specFlow>
        <plugins>
          <add name="PluginAssemblyName" path="." type="Runtime" />
        </plugins>
    ...
    </specFlow>
    

    【讨论】:

      【解决方案2】:

      究竟什么是博迪?我找不到任何有用的信息..

      BoDI 是 Specflow 中附带的一个非常基本的依赖注入框架。你可以找到它的代码仓库here

      请参阅 SpecFlow 的创建者 Gáspár Nagy 的博客中的 this entry(重点是我的):

      SpecFlow 使用称为 BoDi 的特殊依赖注入框架来处理这些任务。 BoDi 是一个可在 GitHub 上获得的嵌入式开源迷你 DI 框架。虽然它是一个通用的 DI,但它的设计和功能是由 SpecFlow 的需求驱动的。到 SpecFlow 项目启动时,NuGet 还不存在,因此库是通过 zip 下载分发的,并且必须手动引用程序集。因此,我们希望将 SpecFlow 运行时保留为单个程序集库。为此,我们需要一个小型且可以作为源代码嵌入的 DI 框架。另外,我们不想使用知名的 DI 框架,因为它可能会导致与您自己项目使用的框架版本冲突。这促使我创建了 BoDi。

      您可以在BoDIhere中找到如何注册类型和接口的示例:

      [Binding]
      public class DependencyConfiguration
      {
          private IObjectContainer objectContainer;
      
          public DependencyConfiguration(IObjectContainer objectContainer)
          {
              this.objectContainer = objectContainer;
          }
      
          [BeforeScenario(Order = 0)]
          public void ConfigureDependencies()
          {
              if (...)
                  objectContainer.RegisterTypeAs<RealDbDriver, IControllerDriver>();
              else
                  objectContainer.RegisterTypeAs<StubDbDriver, IControllerDriver>();
          }
      }
      

      但是,请注意(用 Gáspár Nagy 的话):

      虽然可以自定义 SpecFlow 使用的依赖注入,但它已经突破了 BoDi 的能力边界。更好的选择是为此使用更复杂的 DI 框架。

      【讨论】:

        【解决方案3】:

        对于在 Specflow 项目中寻找支持 DI 的可用插件/库的任何人:https://docs.specflow.org/projects/specflow/en/latest/Extend/Available-Plugins.html#plugins-for-di-container

        我更喜欢 - https://github.com/solidtoken/SpecFlow.DependencyInjection

        例子

        创建 DI 容器:

        [ScenarioDependencies]
        public static IServiceCollection CreateServices()
        {
            var services = new ServiceCollection();
        
            Config config = JObject.Parse(File.ReadAllText("config.json")).ToObject<Config>();
            
            services.AddSingleton(config);
            services.AddScoped<DbConnections>();
            services.AddScoped<ApiClients>();
        
            return services;
        }
        

        使用依赖项(通过参数化构造函数):

        [Binding]
        public sealed class CalculatorStepDefinitions
        {
            private readonly DbConnections dbConnections;
        
            public CalculatorStepDefinitions(DbConnections dbConnections) => this.dbConnections = dbConnections;
        
            ...
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-08-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多