Specflow 步骤定义 - 涉及页面对象时跨功能共享
Specflow Step Definition - sharing across features when page objects involved
我有许多功能文件需要在网页上断言预填充数据 - 每个功能文件都有自己的步骤定义文件
例如在客户功能步骤定义文件中:
[Then(@"I expect all fields on the screen populated with Customer Details from the Database")]
public void PopulatedWithCustomerDetailsFromTheDatabase ()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.CustomerPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
在公司步骤定义文件中:
[Then(@"I expect all fields on the screen populated with Company Details from the Database")]
public void PopulatedWithCompanyDetailsFromTheDatabase()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.CompanyDetailsPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
两者都使用相关的页面对象 class 从页面获取文本并断言其正确性。
建议我不要像这样共享步骤定义,因为它们使用我同意的不同页面对象。问题是我需要在另外 50 多个页面上执行此操作,CustomerPage、CompanyDetailsPage、StockPage、DeliveryPage 等...
关于我如何构造它以便我在步骤定义中有更多共性的任何建议? - 必须有更好的方法,我只是在复制代码,但努力从架构的角度可视化解决方案。
许多页面共有的动作应该封装成自己的页面模型,页面模型可以由多个页面模型组成——如何实现?
为了阐明每个页面都是不同的,所以除了上述步骤定义之外,对于地址页面功能,我还有一个步骤定义:
[Then(@"I expect all fields on the screen populated with AddressDetails from the Database")]
public void PopulatedWithAddressDetailsFromTheDatabase()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.AddressDetailsPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
其中包含地址行 1、地址行 2 等字段....
以及具有步骤定义的 Stock Page 功能:
[Then(@"I expect all fields on the screen populated with Stock from the Database")]
public void PopulatedWithStockFromTheDatabase()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.StockPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
其中有库存列表字段等...50 个不同的页面
您可以参数化页面名称并保留页面对象字典。
Then I should see the following details on the "Stock" page:
| Field | Value |
| ... | ... |
Then I should see the following details on the "Company Details" page:
| Field | Value |
| ... | ... |
您需要每个页面对象实现相同的接口,因此您可以将它们存储在字典中并在步骤定义中对它们调用 GetText
方法:
public interface IReadOnlyPageModel
{
string GetText(string key);
}
然后在你的页面模型中实现这个接口:
public class StockDetailsPage : IReadOnlyPageModel
{
// stuff specific to the stock details page
public string GetText(string key)
{
// based on key, return text
}
}
public class CompanyDetailsPage : IReadOnlyPageModel
{
// stuff specific to the company details page
public string GetText(string key)
{
// based on key, return text
}
}
那么你的步骤定义中需要一个字典class,当然还有步骤定义:
[Binding]
public class StepDefinitions
{
private readonly Dictionary<string, IReadOnlyPageModel> detailsPages;
public StepDefinitions(IWebDriver driver)
{
// Or however you get access to the IWebDriver object
detailsPages = = new Dictionary<string, IReadOnlyPageModel>()
{
{ "Stock", new StockDetailsPage(driver) },
{ "Company", new CompanyDetailsPage(driver) }
};
}
[Then(@"Then I should see the following details on the ""(.*)"" page:")]
public void ThenIShouldSeeTheFollowingDetailsOnThePage(string pageName, Table table)
{
var page = detailsPages[pageName];
foreach (var row in table.Rows)
{
Assert.That(page.GetText(row["Field"]), Is.EqualTo(row["Value"]));
}
}
}
如果您注意到您的元素定位器似乎都遵循某种模式,您可以创建一个通用页面模型:
public class GenericPageModel : IReadOnlyPageModel
{
private readonly IWebDriver driver;
public GenericPageModel(IWebDriver driver)
{
this.driver = driver;
}
public string GetText(string key)
{
return driver.FindElement(By.XPath($"//caption(contains(., 'Details')]/..//tr[contains(., '{key}')]/td[2]")).Text;
}
}
如果在 detailsPages
字段中找不到条目,则修改步骤定义以使用通用页面模型。
var page = detailsPages.ContainsKey(pageName)
? detailsPages[pageName]
: new GenericPageModel(driver); // Or however you get access to your IWebDriver object
我有许多功能文件需要在网页上断言预填充数据 - 每个功能文件都有自己的步骤定义文件
例如在客户功能步骤定义文件中:
[Then(@"I expect all fields on the screen populated with Customer Details from the Database")]
public void PopulatedWithCustomerDetailsFromTheDatabase ()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.CustomerPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
在公司步骤定义文件中:
[Then(@"I expect all fields on the screen populated with Company Details from the Database")]
public void PopulatedWithCompanyDetailsFromTheDatabase()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.CompanyDetailsPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
两者都使用相关的页面对象 class 从页面获取文本并断言其正确性。 建议我不要像这样共享步骤定义,因为它们使用我同意的不同页面对象。问题是我需要在另外 50 多个页面上执行此操作,CustomerPage、CompanyDetailsPage、StockPage、DeliveryPage 等...
关于我如何构造它以便我在步骤定义中有更多共性的任何建议? - 必须有更好的方法,我只是在复制代码,但努力从架构的角度可视化解决方案。
许多页面共有的动作应该封装成自己的页面模型,页面模型可以由多个页面模型组成——如何实现?
为了阐明每个页面都是不同的,所以除了上述步骤定义之外,对于地址页面功能,我还有一个步骤定义:
[Then(@"I expect all fields on the screen populated with AddressDetails from the Database")]
public void PopulatedWithAddressDetailsFromTheDatabase()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.AddressDetailsPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
其中包含地址行 1、地址行 2 等字段....
以及具有步骤定义的 Stock Page 功能:
[Then(@"I expect all fields on the screen populated with Stock from the Database")]
public void PopulatedWithStockFromTheDatabase()
{
foreach (var entry in dataDictionary)
{
Assert.That(pages.StockPage.GetText(entry.Key), Is.EqualTo(entry.Value));
}
}
其中有库存列表字段等...50 个不同的页面
您可以参数化页面名称并保留页面对象字典。
Then I should see the following details on the "Stock" page:
| Field | Value |
| ... | ... |
Then I should see the following details on the "Company Details" page:
| Field | Value |
| ... | ... |
您需要每个页面对象实现相同的接口,因此您可以将它们存储在字典中并在步骤定义中对它们调用 GetText
方法:
public interface IReadOnlyPageModel
{
string GetText(string key);
}
然后在你的页面模型中实现这个接口:
public class StockDetailsPage : IReadOnlyPageModel
{
// stuff specific to the stock details page
public string GetText(string key)
{
// based on key, return text
}
}
public class CompanyDetailsPage : IReadOnlyPageModel
{
// stuff specific to the company details page
public string GetText(string key)
{
// based on key, return text
}
}
那么你的步骤定义中需要一个字典class,当然还有步骤定义:
[Binding]
public class StepDefinitions
{
private readonly Dictionary<string, IReadOnlyPageModel> detailsPages;
public StepDefinitions(IWebDriver driver)
{
// Or however you get access to the IWebDriver object
detailsPages = = new Dictionary<string, IReadOnlyPageModel>()
{
{ "Stock", new StockDetailsPage(driver) },
{ "Company", new CompanyDetailsPage(driver) }
};
}
[Then(@"Then I should see the following details on the ""(.*)"" page:")]
public void ThenIShouldSeeTheFollowingDetailsOnThePage(string pageName, Table table)
{
var page = detailsPages[pageName];
foreach (var row in table.Rows)
{
Assert.That(page.GetText(row["Field"]), Is.EqualTo(row["Value"]));
}
}
}
如果您注意到您的元素定位器似乎都遵循某种模式,您可以创建一个通用页面模型:
public class GenericPageModel : IReadOnlyPageModel
{
private readonly IWebDriver driver;
public GenericPageModel(IWebDriver driver)
{
this.driver = driver;
}
public string GetText(string key)
{
return driver.FindElement(By.XPath($"//caption(contains(., 'Details')]/..//tr[contains(., '{key}')]/td[2]")).Text;
}
}
如果在 detailsPages
字段中找不到条目,则修改步骤定义以使用通用页面模型。
var page = detailsPages.ContainsKey(pageName)
? detailsPages[pageName]
: new GenericPageModel(driver); // Or however you get access to your IWebDriver object