Reputation: 113
I'm using SeleniumExtras.PageObjects
and therefore developed a dedicated Class for different pages in the application.
As a result, if a certain test involves more than one page, I need to include 3 separate page initialisation statements at the beginning of the test:
var pageOne = new PageOne(driver);
PageFactory.InitElements(driver, pageOne);
var pageTwo = new PageTwo(driver);
PageFactory.InitElements(driver, pageTwo);
var pageTherr = new PageThree(driver);
PageFactory.InitElements(driver, pageThree);
Test starts...
Which makes the code cluttered..Is there an elegant way around it have a single statement in the code that calls all aval page objects so they be used in the test ?
Thanks.
Upvotes: 1
Views: 903
Reputation: 18783
You shouldn't be initializing three page objects before their use. Each page object should call PageFactory.InitElements(...)
inside its constructor. Your page objects should have methods on them that transition you to the next page, and return the page object for the next page. The fix is really 2 parts:
Initialize elements in page object constructors
Add methods to the page objects that return the next page's page object.
This first part is basically already answered on this question.
public class PageOne
{
public PageOne(IWebDriver driver)
{
PageFactory.InitElements(driver, this);
}
}
public class PageTwo
{
public PageTwo(IWebDriver driver)
{
PageFactory.InitElements(driver, this);
}
}
public class PageThree
{
public PageThree(IWebDriver driver)
{
PageFactory.InitElements(driver, this);
}
}
From your code, page 1 leads to page 2, which leads to page 3. The PageOne class needs a method that performs an action and returns an instance of PageTwo. Since the names you provided are vague, I'll use creating a new blog post as an example:
public class AddEditBlogPostPageObject
{
[FindsBy(How = How.Id, Using = "Title")]
public IWebElement TitleField { get; set; }
[FindsBy(How = How.Id, Using = "PostDate")]
public IWebElement DateField { get; set; }
[FindsBy(How = How.Id, Using = "BodyText")]
public IWebElement BodyTextField { get; set; }
[FindsBy(How = How.XPath, Using = "//button[contains(., 'Save Blog Post')]")]
public IWebElement SaveButton { get; set; }
private readonly IWebDriver driver;
public AddEditBlogPostPageObject(IWebDriver driver)
{
this.driver = driver;
PageFactory.InitElements(driver, this);
}
public ViewBlogPostPageObject CreateNewPost(string title, DateTime blogPostDate, string bodyText)
{
TitleField.SendKeys(title);
DateField.SendKeys(blogPostDate.ToShortDateString());
BodyTextField.SendKeys(bodyText);
SaveButton.Click();
return new ViewBlogPostPageObject(driver);
}
}
And to use it:
var addBlogPostPage = new AddEditBlogPostPageObject(driver);
var viewBlogPostPage = addBlogPostPage.CreateNewPost(
"How to use Selenium Page Objects",
DateTime.Today,
"All about how to use the page object pattern with Selenium.");
// Use viewBlogPostPage to assert something, or navigate to another page
This way you do not need to initialize all of the page objects before their use. You initialize one page object, call a method on it, which returns the next page object. This makes refactoring your tests to match the application flow easier, since you only need to refactor the methods on your page objects that return the "next page" in the workflow.
Upvotes: 2
Reputation: 50854
You can add it in a base page object
public abstract class AbstractPage
{
public AbstractPage(IWebDriver driver)
{
PageFactory.InitElements(driver, this);
}
}
public class PageOne : AbstractPage
{
public PageOne(IWebDriver driver) : base(driver)
{
}
}
Upvotes: 0