On t'internet!
Selenium Webdriver is a free, open-source software tool for testing web applications. Tests can be written in a number of languages including C# and Java. The tests can be run against most modern web browsers.
See video below of the automated test run using Selenium Webdriver in Chrome:
Selenium was originally developed by Jason Huggins in 2004 as an internal tool at ThoughtWorks. Selenium Remote Control (RC) was then developed by a team at ThoughtWorks and open sourced.
Huggins later joined Google and continued with development of Selenium-RC, at the same time Simon Stewart at ThoughtWorks developed a superior browser automation tool called WebDriver. The projects were merged in 2009 to become Selenium Webdriver.
In 2008, Philippe Hanrigou at ThoughtWorks made Selenium Grid, which provides a hub to allow the running of multiple Selenium tests concurrently on any number of local or remote systems. The ability to run tests on remote browser instances is useful to spread the load of testing across several machines and to run tests in browsers running on different platforms or operating systems. The latter is particularly useful in cases where not all browsers to be used for testing can run on the same platform.
The name Selenium comes from a joke made by Huggins in an email, mocking a competitor named Mercury, saying that you can cure mercury poisoning by taking selenium supplements.
Right click on References and select 'Manage NuGet Packages'. Search for Selenium Webdriver, and Selenium Support
Download Chrome Driver. Drop in solutions folder. Then right click solution and click add -> existing item. Right click on Properties for Chrome.exe. Select 'Copy if newer'
Get Chrome Driver Dynamically
public void Test1() { Driver = GetChromeDriver(); Driver.Manage().Window.Maximize(); Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10); }
public IWebDriver GetChromeDriver() { var outPutDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); return new ChromeDriver(outPutDirectory); }
//Find an element by ID var idElement = driver.FindElement(By.Id("idExample")); idElement.Click(); //Find Element by Class - must check if unique identifier private IWebElement element; element = driver.FindElement(By.ClassName("buttonClassExample")); //Find Element by Name element = driver.FindElement(By.Name("NameExample")): element.Click(); //Find Element by Linktext var linkTextElement = driver.FindElement(By.LinkText("Click me using this link text!")); linkTextElement.Click(); //Find Element by Partial Link Text will tell you it's a link var linkTextElement = driver.FindElement(By.PartialLinkText("")); linkTextElement.Click();
XPath is the standard navigation tool for XML
and an HTML document is also an XML document (xHTML)
Used when no element Id or name. We have to dig down to find the element
Absolute Xpath (Don't use)
Easiest way to identify
Cons: Long, Any change in the structure will break the locator
Relative XPath
Cleaner way to identify elements
More stable
Cons: Takes more time
/ Selects from the root node
// Selects nodes in the document from the current node that match the selection no matter where they are
. Selects the current node
.. Selects the parent of the current node
@ Selects attributes
Example
//input[@value='XPath Button 2']
Using attributes in xpath
//*[@id='']
// - go through all the nodes
* - regardless of the tag (Wild card char - very useful for skipping through code)
@id= - at id attribute equal to
//xpath href attribute
//[@href='/icon-sucess/]
How to identify a button
Using class: //*[@class='cvb '] (may not be unique)
//a[contains(text(),'Click Me')]
xpath function 'contains' and text()
//span[contains(text(), 'Email')]
Include: using System.Reflection; using System.Threading; using System.Windows.Forms;
internal void UploadDocument() { BrowseButton.Click(); string document = $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}\\A Test document.docx"; Thread.Sleep(1000); SendKeys.SendWait(document); SendKeys.SendWait(@"{Enter}"); Thread.Sleep(1500); }
POM is a design pattern, according to which there should be a class for each web page. The class should have 3 regions.
Region 1: All the web elements
Region 2: The actions that can be performed on page
Region 3: All the navigation
#region Webelement
#region Action
#region Navigation
using System; using OpenQA.Selenium; using OpenQA.Selenium.Support.UI; using NUnit.Framework; using System.Threading; namespace FakeTests { internal class LoginPage : BaseTest { #region Webelement public LoginPage(IWebDriver driver) : base(driver) { } private IWebElement Submit => Driver.FindElement(By.XPath("//*[@class='btn btn-login control']")); private IWebElement Password => Driver.FindElement(By.Id("field2")); private IWebElement EmailField => Driver.FindElement(By.Id("field1")); private IWebElement CompanyCode => Driver.FindElement(By.Id("code")); private IWebElement RegisterButton => Driver.FindElement(By.XPath("//*[@ng-click='validateCode()']")); private IWebElement ForgotPasswordLink => Driver.FindElement(By.LinkText("Forgot Your password?")); #endregion #region Action public bool IsVisible { get { return Driver.Title.Contains("Fake Fake Fake"); } set { } } internal CompaniesPage Login(string email, string pass) { try { EmailField.SendKeys(email); Password.SendKeys(pass); Submit.Click(); } catch (Exception e) { Console.WriteLine("Problem logging in" + e.Message); } return new CompaniesPage(Driver); } internal RegistrationPage EnterCompanyCode(string companyCode) { try { CompanyCode.Clear(); CompanyCode.SendKeys(companyCode); RegisterButton.Click(); } catch(Exception e) { Console.WriteLine("Something went wrong entering the company code " + e.Message); Thread.Sleep(1000); } return new RegistrationPage(Driver); } internal ResetPasswordPage ResetPassword() { ForgotPasswordLink.Click(); return new ResetPasswordPage(Driver); } internal Dashboard LoginAsCM(string email, string pass) { WebDriverWait waitForElement = new WebDriverWait(Driver, TimeSpan.FromSeconds(6)); EmailField.SendKeys(email); Password.SendKeys(pass); Submit.Click(); try { waitForElement.Until(ExpectedConditions.ElementIsVisible(By.XPath("//*[@ng-click='ok()']"))); Driver.FindElement(By.XPath("//*[@ng-click='ok()']")).Click(); } catch (Exception e) { Console.WriteLine("No Dialog! Check if first time logging in " + e.Message); } return new Dashboard(Driver); } #endregion #region Navigation internal void GoTo() { Driver.Navigate().GoToUrl("https://test.FakeSite.com"); } #endregion } }
Some pages such as Home Pages or long forms can have a huge number of elements so in order to keep the class size small you can break down the page to smaller size by creating a public property.
using System; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; namespace Test123 { internal class AddDataProcessingActivityPage : BaseApplicationPage { public AddDataProcessingActivityPage(IWebDriver driver) : base(driver) { Activities = new Activities(driver); Data = new Data(driver); ITSystems = new ITSystems(driver); Policies = new Policies(driver); Risks = new Risks(driver); } public Activities Activities { get; set; } public Data Data { get; set; } public ITSystems ITSystems { get; set; } public Policies Policies { get; set; } public Risks Risks { get; set; } //Page Elements private IWebElement SaveButton => Driver.FindElement(By.XPath("//*[@id='frmActivity']/div/div[2]/div/button")); public IWebElement SaveSuccessMessage => Driver.FindElement(By.XPath("//*[@class='alert alert-success alert-dismissible']")); //Page Methods internal void FormSaveSuccessMessage()... internal void VerifyLabelIsShown(string label)... internal void Save()... } }
using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; namespace Fake { internal class Risks : BaseApplicationPage { private IWebDriver driver; public Risks(IWebDriver driver) : base(driver) { this.driver = driver; } //Create Risk button public IWebElement CreateRiskMenuButton => Driver.FindElement(By.XPath("//*[@href='#create-risk']")); public IWebElement RiskDescriptionTextbox => Driver.FindElement(By.Id("risk-desc")); public IWebElement CreateRiskOKButton => Driver.FindElement(By.Id("btnSaveRisk")); //Risk Data public IWebElement Risk => Driver.FindElement(By.XPath("//*[@id='risk_data']/div/div/div[1]")); public IWebElement PolicyRisk => Driver.FindElement(By.XPath("//*[@id='risk_policies']/div/div/div[1]")); public IWebElement DataRisk => Driver.FindElement(By.XPath("//*[@id='risk_data']/div/div[1]/div[1]")); internal void AssertThatPolicyDataRiskMessageIsShown(string risk) { Assert.AreEqual(risk, DataRisk.Text); } internal void SaveCreateRisk()... internal void CreateRiskDescription(string description)... internal void AssertThatPolicyRiskMessageIsShown(string message)... internal void AssertThatRiskMessageIsShown(string riskMessage)... } }
As Selenium can't type into a windows form you can use System.Windows.Forms; The file will be uploaded from the bin folder.
internal void UploadEmployeesAttachFile(string file) { AttachFileButton.Click(); string document = $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}\\" + file + ""; Thread.Sleep(1000); SendKeys.SendWait(document); SendKeys.SendWait(@"{Enter}"); Thread.Sleep(1500); }
In code below a link is clicked that opens a new tab in browser. We want to focues WebDriver on new tab, close the tab and return to original tab
//Wait for the tab to open fully Thread.Sleep(2000); //Switch to the new tab string newTab = Driver.WindowHandles.Last(); Driver.SwitchTo().Window(newTab); Thread.Sleep(500); //Close the current tab Driver.Close(); //Return to the original tab Driver.SwitchTo().Window(Driver.WindowHandles.First()); Thread.Sleep(500);
use the: using OpenQA.Selenium.Interactions; class to move to an element
IWebElement element = Driver.FindElement(By.Id(x)); Actions actions = new Actions(Driver); actions.MoveToElement(element); actions.Perform(); element.Click();
The above will scroll the element into view and then click on the element.
Scrolls the element into view and then applies an offset to ensure navigation pane not blocking element.
try { IJavaScriptExecutor js = (IJavaScriptExecutor)Driver; var elem = Driver.FindElement(By.ID("SomeElement")); ScrollTo(0, elem.Location.Y - 75); // Make sure element is in the view but below the top navigation pane Driver.FindElement(By.XPath("SomeElement")).Click(); }
Scroll To
public void ScrollTo(int xPosition = 0, int yPosition = 0) { IJavaScriptExecutor js = (IJavaScriptExecutor)Driver; var ls = String.Format("window.scrollTo({0}, {1})", xPosition, yPosition); js.ExecuteScript(ls); }
Get the value from a field and assert are equal
ElementValue = SomeElement.GetAttribute(“value”); Assert.AreEqual(Element, ElementValue);
Create a new project in Visual Studio and add a Class Library. Right click on References and Manage NuGet Packages. Install Selenium Support which will also install Selenium Webdriver. Install NUnit (a popular open source test runner).
Then go to Tools -> Extensions and Updates. Search for nunit and install NUnit3 Test Adapter. Check the References for your project, you should see:
Nunit.framework
WebDriver
WebDriver.Support
Write your tests and tag tests with NUnit test attributes.
[TestFixture] – goes over the class
[Parallelizable] – goes over class, needed for running tests in parallel
[SetUp] – Declare driver, runs for each test
[Test] – goes above each test in class file
The framework is then going to know to run the tests in NUnit. Unfortunately tests in the same class cannot be run in parallel, each parallel test will have to be in a separate class file.
using NUnit.Framework; using OpenQA.Selenium.Chrome; using OpenQA.Selenium; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ParellelCourseTests { [TestFixture] [Parallelizable] public class Class1 { private IWebDriver Driver; [SetUp] public void Setup() { Driver = new ChromeDriver(); } [Test] public void LocalTest1() { Driver.Navigate().GoToUrl("https://www.shanecleary.net"); } [Test] public void LocalTest2() { Driver.Navigate().GoToUrl("https://www.shanecleary.net/Bitcoin.html"); } } }
© Shane Cleary