BDD - SpecFlow Driver Pattern 驱动模式
- 引言
- Driver Pattern 的优势
- 举例
- 不用 Driver Pattern
- 运用 Driver Pattern
引言
前面 《 BDD - SpecFlow Page Object Model POM 》介绍了 POM 模式,用于提取 Web UI 元素封装成 Page Object 类,今天介绍另外一种 Driver Pattern 驱动模式,这两种模式都是为了提高 Automation 的维护性,可读性,有利于Automation 框架设计。
Driver Pattern 的优势
Driver 模式只不过是 Step Definitions 和 Automation Code 之间附加层。
多年实践经验表明,组织好 Bindings 类和 Automation Code,保持 Bindings 类中的 Code 非常短(一个 Step definition 不超过 10 行 Code),这样可读性强。
Driver Pattern 具有下列优势:
-
易维护 Autoamtion Code
将 Code 分成多个部分,使得 Code 方便维护 -
不同的 Step Definitions 方便重用方法,或则合并多个 Steps 成单个的 Step
我们经常看到一组 Steps 适用于多个 Scenarios,将 Automation Code 开成不同的类,更方便调用这些方法。 -
Step Definitions 易读
使得没有 Coding 技术的人也有理解一个 Step Definiton 的实现作用。特别是大的项目,没有人想了解每单个 Step 的实现。
当然 Driver Patter 利用 Context Injection 将多个类联系在一起,详情请参考 《 BDD - SpecFlow Context Injection 上下文依赖注入 》
举例
这个例子,对比运用 Driver Patern 前后的变化。
不用 Driver Pattern
这是一些 Automation Code 利用 Page Object Model 来检查一些 WebElements 是否存在。Step Definition 中大量的 code 行,不够简短,可读性不强。
[Then(@"it is possible to enter a '(.*)' with label '(.*)'")]
public void ThenItIsPossibleToEnterAWithLabel(string inputType, string expectedLabel)
{
var submissionPageObject = new SubmissionPageObject(webDriverDriver);
switch (inputType.ToUpper())
{
case "URL":
submissionPageObject.UrlWebElement.Should().NotBeNull();
submissionPageObject.UrlLabel.Should().Be(expectedLabel);
break;
case "TYPE":
submissionPageObject.TypeWebElement.Should().NotBeNull();
submissionPageObject.TypeLabel.Should().Be(expectedLabel);
break;
default:
throw new NotImplementedException(inputType + " not implemented");
}
}
运用 Driver Pattern
我们将上面 Step Definition 中大量的 Code 移到一个 Driver 类SubmissionPageDriver 中,这样就能减少 Step Definition 中的 Code 行数到一行。我们将命名一个 CheckExistenceOfInputElement 方法,团队中每个成员都能从名字上理解这个方法的作用。
为了在 Step Definition 中调用 Driver 类中的封装的方法 SubmissionSteps,我们利用 SpecFlow 自带支持的 Context Inject 依赖注入,在 public 构造函数中定义 Driver 类的实例做为参数。
[Binding]
public class SubmissionSteps
{
private readonly SubmissionPageDriver submissionPageDriver;
public SubmissionSteps(SubmissionPageDriver submissionPageDriver)
{
this.submissionPageDriver = submissionPageDriver;
}
[Then(@"it is possible to enter a '(.*)' with label '(.*)'")]
public void ThenItIsPossibleToEnterAWithLabel(string inputType, string expectedLabel)
{
submissionPageDriver.CheckExistenceOfInputElement(inputType, expectedLabel);
}
// ...
public class SubmissionPageDriver
{
// ...
public void CheckExistenceOfInputElement(string inputType, string expectedLabel)
{
var submissionPageObject = new SubmissionPageObject(webDriverDriver);
switch (inputType.ToUpper())
{
case "URL":
submissionPageObject.UrlWebElement.Should().NotBeNull();
submissionPageObject.UrlLabel.Should().Be(expectedLabel);
break;
case "TYPE":
submissionPageObject.TypeWebElement.Should().NotBeNull();
submissionPageObject.TypeLabel.Should().Be(expectedLabel);
break;
default:
throw new NotImplementedException(inputType + " not implemented");
}
}
// ...