(如何)使用Autofac作为IoC容器运行Specflow测试用例?

PhD*_*PhD 3 c# ioc-container autofac specflow

我正在尝试在我的组织中采用BDD,因为C#.Net是我们的主要开发模式,Specflow是我们对"任何黄瓜"的最佳选择.

但是,我过去曾是一名Spring爱好者,但在我的公司,我们正在将Autofac用于应用程序的各个部分.但是,我找不到任何解释"如何"的资源,可以使用Autofac"触发"Specflow的BDD测试并提供必要的依赖连接.

我计划让Autofac负责实例化,连接和执行所有内容而不是执行Specflow,并且让调用Autofac的方法遍布各处,即使用Autofac作为服务定位器而不是DI/IoC容器.甚至可以这样做,还是我以错误的方式看待它并且有更好的方法来实现同样的目标?或者我应该完全依赖Specflow的"内部容器"进行DI并完全忘记Autofac?

可能的方法:

  1. 让Autofac实例化并连接所有内容并运行Specflow测试(不确定这是否可行/推荐).
  2. 让Specflow全局实例化Autofac,它与其余代码的必要依赖关系相连.可能的步骤定义可能会使用Autofac作为工厂来获得他们需要的东西.

优点缺点:

  1. 第一种方法是理想的,因为它可以防止从Specflow到Autofac的任何依赖.前者忘记了后者.完全透明.首选,但不知道如何去做.
  2. 如果Autofac可以由Specflow全局实例化一次以供稍后使用,则后一种方法"可以"工作.但这会导致在将两个库耦合在一起的步骤定义中对Autofac进行大量调用.不太可取.

我不确定如何实现它们中的任何一个,如果最好让Specflow处理DI并忘记Autofac或让Autofac解雇所有内容或者是否存在中间地带?

当前的BDD设置:Specflow,Selenium/PhantomJS,Xunit.希望与Autofac结合使用.

Max*_*uez 10

我计划让Autofac负责实例化,连接和执行所有内容,而不是执行Specflow,并且让调用Autofac的方法无处不在.

  • 我真的没有看到你将如何实现这一点,仍然能够运行你的测试Visual StudioReSharper(我假设你不想失去它).

让Specflow全局实例化Autofac,它与其余代码的必要依赖关系相连.可能的步骤定义可能会使用Autofac作为工厂来获得他们需要的东西.

  • 这正是我正在做的和我推荐的.

但这会导致在将两个库耦合在一起的步骤定义中对Autofac进行大量调用.

  • 我不建议AutoFac直接调用's方法来解析Step Definition类中所需的类型.我会在基类中创建一个方法,或者将一个对象绑定到具有所述方法的specflow的迷你DI容器.

我要做的是创建一个名为的服务定位器接口IServiceLocator(是的,我知道服务定位器是一个反模式)

public interface IServiceLocator
{
    T Get<T>();
}
Run Code Online (Sandbox Code Playgroud)

然后我们将使用创建此接口的实现Autofac(请记住,您可以将其替换为另一个实现)

public class AutoFacServiceLocator: IServiceLocator
{
    private readonly IContainer _container;

    public AutoFacServiceLocator(IContainer container)
    {
        _container = container;
    }

    public T Get<T>()
    {
        //Here you add your resolution logic
    }
}
Run Code Online (Sandbox Code Playgroud)

对于每一个场景中,我们需要的实例的事实IServiceLocator,我们希望能够通过得到它SpecflowContext Injection.

[Binding]
public class Hooks
{
    private readonly IObjectContainer _objectContainer;

    public Hooks(IObjectContainer objectContainer)
    {
        _objectContainer = objectContainer;
    }

    [BeforeScenario]
    public void RegisterServiceLocator()
    {
        var container = CreateContainer();

        var serviceLocator = new AutoFacServiceLocator(container);

        _objectContainer.RegisterInstanceAs<IServiceLocator>(serviceLocator);
    }

    private IContainer CreateContainer() { /*Create your container*/}
}
Run Code Online (Sandbox Code Playgroud)

最后这是用法

[Binding]
public class Steps
{
    private readonly IServiceLocator _serviceLocator;

    public Steps(IServiceLocator serviceLocator)
    {
        _serviceLocator = serviceLocator;
    }

    [Given(@"I have entered (.*) into the calculator")]
    public void GivenIHaveEnteredIntoTheCalculator(int p0)
    {
        Foo foo = _serviceLocator.Get<Foo>();
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:

travis-illig在下面的评论中说

如果您使用服务位置,请尝试使用CommonServiceLocator而不是创建自己的界面

我真的不认为有必要使用CommonServiceLocator.对服务定位器的调用应与控制器中的构造函数注入匹配,这意味着该方法应涵盖这些调用Get<T>().

引用 ctavares,其中一位CommonServiceLocator开发人员

我应该将此库用于我的应用程序吗?

通常,这个问题的答案是否定的.一旦你决定了一个适合你项目的容器,以一种可以切换容器的方式编写整个应用程序并没有太大的好处.对于必须适应其他生态系统并与其他库很好地兼容的库,它是一个重要的特性,但对于应用程序来说,额外的抽象层实际上并没有给你带来太多帮助.

CommonServiceLocator是为了图书馆和框架,因为博士的测试项目是他和他的团队,我不建议引入更多的依赖项.