
Introduction
Greetings to all whom the previous articles were helpful or were just interesting. In the last part we dealt with Selenium WebDriver, and now, before we start writing tests, we need to describe the pages of the application under test. In the preview list the main points
- Descriptions of all pages will be in a separate project, I called it Autotests.WebPages
- For each page a separate class will be created, located in a separate file.
- The names of the classes will coincide with the names of the web pages, for example, the description of the MySite / Home / Help page will be stored in a class called Help
- The structure of the location of these files is hierarchical and will repeat the tree of web pages. For example, the Help page will have the path Root \ Home \ Help.cs relative to the project and be located in the namespace (namespace) of Autotests.WebPages.Root.Home
- To access the pages will be developed some facade (Facade), a static class Pages
- In practice, pages can have a lot in common, so it is quite logical to use inheritance and putting some functionality into separate helpers.
Links
Part 1: IntroductionPart 2.1: Selenium API wrapper - BrowserPart 2.2: Selenium API wrapper - WebElementPart 3: WebPages - describing pagesPart 4: Finally writing testsFramework PublishingClass PageBase
It is logical to start with the design of the base class. What will be common to our pages? Least:
- getting url page
- page opening
- getting page name
- link navigation
In addition to this, the PageBase class will contain methods common to all pages, which depend on the particular application. For example, if on all pages of the site can pop up a custom pop-up window, then we write the ClosePopup () method. Or, for example, on all pages there is a site menu. Then this menu can be described in the base class. In the example I will omit it.
namespace Autotests.WebPages { public abstract class PageBase { public Uri Url { get { Browser.WaitAjax(); return GetUriByRelativePath(RelativePath); } } public void Open() { Contract.Requires(Url != null); Contract.Ensures(Browser.Url == Url, string.Format("{0} != {1}", Browser.Url, Url)); Browser.WaitAjax(); if (Browser.Url == Url) return; Browser.Navigate(Url); } public Type PageName() { return GetType(); } protected void Navigate(Uri url) { Contract.Requires(url != null); Browser.Navigate(url); } protected static Uri GetUriByRelativePath(string relativePath) { Contract.Requires(!string.IsNullOrEmpty(relativePath)); return new Uri(string.Format("{0}{1}", SharedSettings.TestEnvironmentUrl, relativePath)); } private string RelativePath { get { const string rootNamespaceName = "Autotests.WebPages.Root"; const string stringToDelete = "Page"; var fullName = GetType().FullName; Contract.Assume(fullName != null); return fullName.Replace(rootNamespaceName, "").Replace(".", "/").Replace(stringToDelete, ""); } } } }
RelativePath gets the relative path to the web page based on the name and location of the class that describes it. Deleting the magic word “Page” is a crutch for the case when the name of the page of the site coincides with the reserved or occupied names. In this case, the word “Page” is simply assigned to the name of the page description class.
SharedSettings.TestEnvironmentUrl - the address of the site under test. It can be stored in constants or in project settings.
')
LogOn page description
Now, to get to the site, we need to pass authorization (by the way, everyone knows how authorization differs from authentication?).
We describe the LogOn page:
public class LogOn : PageBase { #region Elements private static readonly WebElement LoginEdit = new WebElement().ById("Login"); private static readonly WebElement PasswordEdit = new WebElement().ById("Password"); private static readonly WebElement RememberMeCheckbox = new WebElement().ById("Remember"); private static readonly WebElement ValidationSummary = new WebElement().ByClass("ValidationSummary"); #endregion public void DoLogOn(UserLoginInfo userLoginInfo) { LoginEdit.Text = userLoginInfo.Login; PasswordEdit.Text = userLoginInfo.Password; RememberMeCheckbox.SetCheck(userLoginInfo.RememberMe); Browser.ExecuteJavaScript("$('#LogOnForm').submit()"); if (Browser.Url == Main.Url) return; if (ValidationSummary.Exists()) throw new LogOnValidationException(); throw new Exception("Unknown logon exception."); } }
So, on the page we have two input fields (username and password), the “Remember me” checkbox and the “Enter” button. I deliberately did not begin to describe this button, but click on it through the Browser.ExecuteJavaScript to show this way of manipulating elements.
The DoLogOn method accepts some data structure, fills in and submits the form. If after this the main Browser.Url == Main.Url page is displayed, the method successfully completes its work. If the input failed, and a ValidationSummary.Exists () error is shown, the method throws a LogOnValidationException exception. In all other (unforeseen) cases, an exception is thrown with the message "Unknown logon exception.".
The LogOn class inherited from PageBase some methods and properties, for example, the Open () method. There are situations when the page needs to be opened with some parameters. To do this, you need to write in the LogOn class an overload for Open:
public void Open(int userId) { var url = new Uri(string.Format("{0}?userId={1}", Url, userId)); Navigate(url); }
In this method, we create a new Url (add user id) and call the base class Navigate method.
Note that there is no hidden initialization (remember PageFactory), the elements will be searched when they are accessed.
Facade Pages
In principle, you can not use the facade. I was so comfortable because the classes of the pages are not static, and I need to create instances. For example (see picture above), the Pages class will look like this:
namespace Autotests.WebPages { public static class Pages { public static LogOn LogOn = new LogOn(); public static class Home { public static Help Help = new Help (); public static Main MainLogOut = new MainLogOut (); } } }
Thus, from the tests we will be able to work with the pages as follows:
Pages.LogOn.DoLogOn(...); Pages.Home.Help.Open();
This is very convenient, the page hierarchy repeats the site hierarchy.
Conclusion
Fuf, this part was pretty easy for me, and there was a bit of code. As usual, write any questions / suggestions in a personal or in the comments. In the next part, finally, we will write autotests =) In it, I will give answers to the most frequent questions and give a link to the working version of the framework.