📜 ⬆️ ⬇️

Automated testing of a web application (MS Unit Testing Framework + Selenium WebDriver C #). Part 3: WebPages - describing pages

Webpages
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


Links

Part 1: Introduction
Part 2.1: Selenium API wrapper - Browser
Part 2.2: Selenium API wrapper - WebElement
Part 3: WebPages - describing pages
Part 4: Finally writing tests
Framework Publishing

Class PageBase

It is logical to start with the design of the base class. What will be common to our pages? Least:

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.

Source: https://habr.com/ru/post/180705/


All Articles