BDD - SpecFlow Context Injection 上下文依赖注入

引言

BDD SpecFlow Scenario 在整个执行周期,Steps 之间需要共享一些数据,例如 WebDriver,或则其它共享数据等,如何实现呢?SpecFlow 默认是支持 Contex Injection ,也就是 DI 依赖注入,可以轻松解决数据共享问题。

Context Injection

SpecFlow 支持一种非常简单的依赖框架,能够为 Scenarios 初始化并注入共享的类的实例。这使得我们可以将需要共享的数据封装在不同的 context 类中,然后注入到每个需要访问这些数据中的 Binding 类中。

如何使用 Context Injection

  1. 创建 POCOs(plain old CLR object), 简单的 .NET 类,呈现共享的数据
  2. 在每个需要访问共享数据的 Binding 类中,将创建的共享类的实例作为构造函数的参数
  3. Binding 类中,用实例字段将构造函数中的参数保存起来,这样就可以在 Step Definition 中使用共享数据了。

Context Injection 规则

  1. 被注入对象的生命周期局限于 Scenario 的执行周期
  2. 如果被注对象实现了 IDisposable,他们在 Scenario 执行后会被 Dispose
  3. 当然注入解析是可以递归的,也就是说被注入的类也可以有依赖的。
  4. 注入对象的解析仅用于 public 的构造函数
  5. 如果有多个 public 的构造函数,SpecFlow 会选择第一个

举例 1 共享字段

我们定义一个 PersonData 类来表示共享数据,分别在 MyStepDefs Binding 类 (定义 Given Step),OtherStepDefs Binding 类 (定义 Then Step) 中通过public 构造函数将 PersonData 做为参数来注入 PersonData 类的实例,并定义 Binding 类的的实例成员来保存 PersonData 类的实例,使得在 Step 中可以访问共享数据。

所以在这个 Scenrio 中,Given Step 是用来写入共享数据,Then Step 用来读共享数据,在同一个 Scenatio 中,Given 和 Then steps 中访问的 PersonData 实例是同一个对象。

public class PersonData // the POCO for sharing person data
{ 
  public string FirstName;
  public string LastName;
}

[Binding]
public class MyStepDefs
{
  private readonly PersonData personData;
  public MyStepDefs(PersonData personData) // use it as ctor parameter
  { 
    this.personData = personData;
  }
  
  [Given] 
  public void The_person_FIRSTNAME_LASTNAME(string firstName, string lastName) 
  {
    personData.FirstName = firstName; // write into the shared data
    personData.LastName = lastName;
    //... do other things you need
  }
}

[Binding]
public class OtherStepDefs // another binding class needing the person
{ 
  private readonly PersonData personData;
  public OtherStepDefs(PersonData personData) // ctor parameter here too
  { 
    this.personData = personData;
  }
  
  [Then] 
  public void The_person_data_is_properly_displayed() 
  {
    var displayedData = ... // get the displayed data from the app
      // read from shared data, to perform assertions
    Assert.AreEqual(personData.FirstName + " " + personData.LastName, 
      displayedData, "Person name was not displayed properly");
  }
}

举例 2 共享引用类

这个例子中,我们定义 CatalogContext 用来存储一个引用类,这个 CatalogContext 可以注入到不同的 Binding 类中。

public class CatalogContext
{
    public CatalogContext()
    {
        ReferenceBooks = new ReferenceBookList();
    }

    public ReferenceBookList ReferenceBooks { get; set; }
}

[Binding]
public class BookSteps
{
    private readonly CatalogContext _catalogContext;

    public BookSteps(CatalogContext catalogContext)
    {
        _catalogContext = catalogContext;
    }

    [Given(@"the following books")]
    public void GivenTheFollowingBooks(Table table)
    {
        foreach (var book in table.CreateSet<Book>())
        {
            SaveBook(book);
            _catalogContext.ReferenceBooks.Add(book.Id, book);
        }
    }
}

自定义 Injection Framework 注入框架

前面例子中,我们都是利用 SpecFlow 默认的注入框架 IObjectContainer,这是多数 Scenarios 推荐的依赖注入框架。当然你也可以自定义自己喜欢或则需要的依赖注入框架。

SpecFlow 支持的 DI Container 插件
在这里插入图片描述
为了使用这些 plugins,我们需要在配置文件中 specflow 部分添加这个 plugin

<specFlow>
  <plugins>
    <add name="SpecFlow.Autofac" type="Runtime" />
  </plugins>
  <!-- Anything else -->
</specFlow>

这样是为了告诉 SpecFlow 去加载 runtime plugin,允许创建一个入口去使用这个功能,参考 autofac example. 一旦建立起来,依赖将被注入到 steps 和 binding的容器中,就像 SpecFlow 默认的注入容器 IObjectContainer,当需要使用依赖时,就会从 IObjectContainer 取出。

自定义 Container

SpecFlow 使用的 Container 是可以自定义的,例如,可以注册已经创建实例对象,或则更改解析规则。

可以在 Scenario hook 前自定义 Container,自定义注入规则的类必须获取一个 Scenario 执行的 Container(BoDi.IObjectContainer 也就是 SpecFlow 默认使用的 Container 的一个实例),这可以通过构造函数来完成。

例如下面这个例子,定义一个 WebDriverSupport 类来进行自定义 Container,通过构造函数来获取 IObjectContainer 的实例。BeforeScenario hook 函数中来注册需要共享的 IWebDriver 类实例,所以其它 Binding 类就可以使用访问这个共享的依赖了,只需在 public 构造函数定义一个 IWebDriver 类的参数。

Scenario 执行前就会执行 BeforeScenario hook 中的 InitializeWebDriver 函数,注册一个 IWebDriver 实例到 IObjectContainer 中,然后执行 Step,Step binding 类中的构造函数中有 IWebDriver 类型参数,就会从 IObjectContainer 取出前面注册的 IWebDriver 实例,只要其它 Step binding 类中有指定 IWebDriver 这个依赖,
在整个 Senario 执行过程中,只会是共享同一个 IWebDriver 实例。

[Binding]
public class WebDriverSupport
{
  private readonly IObjectContainer objectContainer;

  public WebDriverSupport(IObjectContainer objectContainer)
  {
    this.objectContainer = objectContainer;
  }

  [BeforeScenario]
  public void InitializeWebDriver()
  {
    var webDriver = new FirefoxDriver();
    objectContainer.RegisterInstanceAs<IWebDriver>(webDriver);
  }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值