使用Cucumber和Selenium可能会有些棘手。我开发了一种模式,涉及到扩展方法到Selenium的IWebDriver
接口,允许我使用页面对象导航到特定页面。我使用SpecFlow依赖注入框架注册IWebDriver
对象,然后我的步骤定义类可以自由地初始化它们需要的任何页面对象。
将Selenium Web Driver注册到SpecFlow
您只需要插入到before/after场景钩子中来管理Web Driver对象:
[Binding]
public class WebDriverFactory
{
private readonly IObjectContainer container;
public WebDriverFactory(IObjectContainer container)
{
this.container = container;
}
[BeforeScenario]
public void CreateWebDriver()
{
var driver = new ChromeDriver(...);
container.RegisterInstanceAs<IWebDriver>(driver);
}
[AfterScenario]
public void DestroyWebDriver()
{
var driver = container.Resolve<IWebDriver>();
if (driver == null)
return;
driver.Quit();
driver.Dispose();
}
}
然后,就是使用IWebDriver
接口的一些扩展将步骤定义和页面对象粘合在一起的问题。
Selenium页面对象
保持您的页面对象相互导航。例如,HomePage允许您导航到“创建博客文章”页面,并返回该页面的页面对象:
public class HomePage
{
private readonly IWebDriver driver;
private readonly WebDriverWait wait;
private IWebElement CreatePostLink => driver.FindElement(By.LinkText("Create New Blog Post"));
public HomePage(IWebDriver driver)
{
this.driver = driver;
wait = new WebDriverWait(driver, 30);
}
public AddEditBlogPostPage ClickCreatePostLink()
{
CreatePostLink.Click();
wait.Until(d => d.Title.Contains("Create new blog post"));
return new AddEditBlogPostPage(driver);
}
}
随后,当您创建新的博客文章时,AddEditBlogPostPage会返回BlogPostListingPage:
public class AddEditBlogPostPage
{
private readonly IWebDriver driver;
private IWebElement Title => driver.FindElement(By.Id("Title"));
private IWebElement PostDate => driver.FindElement(By.Id("Date"));
private IWebElement Body => driver.FindElement(By.Id("BodyText"));
private IWebElement SaveButton => driver.FindElement(By.XPath("//button[contains(., 'Save Blog Post')]"));
public AddEditBlogPostPage(IWebDriver driver)
{
this.driver = driver;
}
public BlogPostListingPage CreateBlogPost(BlogPostDataRow data)
{
Title.SendKeys(data.Title);
PostDate.SendKeys(data.Date.ToShortDateString());
Body.SendKeys(data.Body);
SaveButton.Click();
return new BlogPostListingPage(driver);
}
}
步骤定义将事物粘合在一起
步骤:
When I create a new blog post:
| Field | Value |
| Title | Selenium Page Objects and Cucumber |
| Date | 11/1/2019 |
| Body | ... |
将会有这个定义:
[Binding]
public class BlogPostSteps
{
private readonly IWebDriver driver;
public BlogPostSteps(IWebDriver driver)
{
this.driver = driver;
}
[When(@"I add a new blog post:")]
public GivenIAmAddingANewBlogPost(Table table)
{
var addBlogPostPage = driver.GoToCreateBlogPostPage();
var blogPostData = table.CreateInstance<BlogPostDataRow>();
addBlogPostPage.CreateBlogPost(blogPostData);
}
}
driver.GoToCreateBlogPostPage();
是一个对IWebDriver
的扩展方法,它用于从一个页面对象导航到另一个页面对象:
public static class SeleniumPageNavigationExtensions
{
public static AddEditBlogPostPage GoToCreateBlogPostPage(this IWebDriver driver)
{
var homePage = new HomePage(driver);
return homePage.ClickCreatePostLink();
}
}
这样做可以让您的页面对象保持“纯净”,避免使用SpecFlow、Cucumber和Gherkin。 您可以在其他不使用Gherkin或行为驱动开发的测试中使用这些扩展方法和页面对象。 这允许轻松重用测试类。 您的测试项目应该与实际应用程序测试一样有目的地进行架构设计。