Professional Documents
Culture Documents
Selenium Webdriver Recipes in C Third Edition Courtney Zhan All Chapter
Selenium Webdriver Recipes in C Third Edition Courtney Zhan All Chapter
Author
Courtney Zhan
Author
Courtney Zhan
Fitzgibbon, QLD, Australia
This work is subject to copyright. All rights are solely and exclusively
licensed by the Publisher, whether the whole or part of the material is
concerned, speci ically the rights of translation, reprinting, reuse of
illustrations, recitation, broadcasting, reproduction on micro ilms or in
any other physical way, and transmission or information storage and
retrieval, electronic adaptation, computer software, or by similar or
dissimilar methodology now known or hereafter developed.
The publisher, the authors, and the editors are safe to assume that the
advice and information in this book are believed to be true and
accurate at the date of publication. Neither the publisher nor the
authors or the editors give a warranty, expressed or implied, with
respect to the material contained herein or for any errors or omissions
that may have been made. The publisher remains neutral with regard
to jurisdictional claims in published maps and institutional af iliations.
What’s New
Selenium’s new features and improvements include the following:
DOM shadow roots
Relative locators
Chrome DevTools
New APIs
…and more!
What’s new in this book:
New (about 15% more) and updated recipes
Using Visual Studio Code as the tool for developing/executing test
scripts
Executing Selenium C# tests in a BuildWise continuous testing
server
Send Us Feedback
We appreciate your comments, suggestions, and reports on errors in
the book and the recipe test scripts. You may submit your feedback on
the book site.
Zhimin Zhan
is the founder and CTO of AgileWay Pty
Ltd, Australia. As an advisor and coach,
he helps organizations implement
continuous testing with open
technologies such as Selenium
WebDriver and Appium. Zhimin is the
creator of the TestWise Testing IDE and
international award-winning BuildWise
CT Server and an author.
Footnotes
1 http://zhimin.com/books/selenium-recipes-csharp
© Courtney Zhan, Edited by Zhimin Zhan 2024
Z. Zhan (ed.), Selenium WebDriver Recipes in C#
https://doi.org/10.1007/979-8-8688-0023-8_1
1. Introduction
Courtney Zhan1
(1) Fitzgibbon, QLD, Australia
Selenium WebDriver is a free and open-source library for automating web applications. Basically,
you can use it to drive web apps in a browser and for testing purposes.
Selenium WebDriver
Selenium WebDriver is a merger of Selenium v1 and another automation framework called
WebDriver led by Simon Stewart at Google. (Simon later joined Facebook and Apple.) Selenium 2.0
was released in July 2011. We use “Selenium,” “WebDriver,” and “Selenium WebDriver”
interchangeably in this book. Selenium v3 and v4 were released in October of 2016 and October of
2021, respectively.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
class GoogleSearch
{
static void Main()
{
// Create a new instance of the driver
IWebDriver driver = new ChromeDriver();
Java:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
Python:
driver = webdriver.Firefox()
driver.get("http://www.google.com")
elem = driver.find_element_by_name("q")
elem.send_keys("Hello WebDriver!")
elem.submit()
print(driver.title)
Ruby:
require "selenium-webdriver"
puts driver.title
Prerequisites
Download and install VS Code.
A browser, such as Chrome or Firefox
Have the browser’s driver (such as the ChromeDriver executable) included in the PATH.
Verify it was installed correctly by running this code in a new terminal window:
$ dotnet --version
7.0.202
2.
Install the C# and NuGet package manager extensions into VS Code.
Click the Extensions tab on the left pane in VS Code (or use Command + Shift + X on a Mac
or Control + Shift + X on Windows). Search for C# in the Extension Marketplace. This
will add support for C# in VS Code. Install it (Figure 1-2).
Figure 1-2 Visual Studio Code’s C# extension
Repeat the same process for the NuGet Package Manager, which helps to add libraries, such as
Selenium WebDriver, to test projects (Figure 1-3).
3.
Create a MSTest project folder.
The MSTest framework is a test framework which is included by default with Microsoft Visual
Studio.
From the terminal, run the following command to create a project named
HelloSeleniumTest:
Sample output:
4.
Open the newly created test project folder in VS Code.
Run the (empty) test case by clicking the Run Test link, as indicated in Figure 1-4.
Figure 1-4 How to run an empty test in Visual Studio Code
There is an extra compilation step, which was taken care of by VS Code, for readers who have
only used scripting languages, such as Ruby and Python, as C# is a compiled language. As you can
see, HelloSeleniumTest.dll is the output of the compilation (a.k.a. build).
This empty test case is meaningless, but if it passes, it means your MSTest setup (with SDK) is
correct.
5.
Add the Selenium WebDriver package to the test project.
You want to run a Selenium C# test that drives a Chrome browser (on macOS). There are three
prerequisites:
The Chrome browser
The ChromeDriver for the matching browser version is in the PATH
The Selenium WebDriver library must be installed and con igured in the test project.
The irst two are generic and needed for any WebDriver-powered automation. The third one
you can achieve with the NuGet package manager.
Press Command + Shift + P on Mac (or Control + Shift + P on Windows) to
start the Command Palette, type NuGet, and select the NuGet Package Manager: Add Package
option, as shown in Figure 1-5.
Figure 1-5 Executing NuGet Package Manager’s Add Package command in Visual Studio Code
Then type in Selenium and press the Enter key (Figure 1-6).
Figure 1-6 Adding the package Selenium.WebDriver in Visual Studio Code
Press the Enter key to select Selenium.WebDriver. Then select the latest version (in our case,
4.8.2). See Figure 1-7.
After that, you will see popups at the right bottom of VS Code (Figure 1-8).
namespace HelloSeleniumTest;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
[TestClass]
public class SeleniumLoginOkTest
{
IWebDriver driver = null;
[TestMethod]
public void TestLoginOK()
{
driver = new ChromeDriver();
driver.Navigate().GoToUrl("http://travel.agileway.net");
driver.FindElement(By.Name("username")).SendKeys("agileway");
driver.FindElement(By.Name("password")).SendKeys("testwise");
driver.FindElement(By.Name("password")).Submit();
Assert.IsTrue(driver.PageSource.Contains("Signed in!"));
driver.Quit();
}
}
Run it (as shown earlier in Step 4). You will see a Chrome browser launch and log into the
AgileTravel site.
Cross-Browser Testing
One big advantage of Selenium WebDriver over other web automation frameworks, in our opinions,
is that it supports all major web browsers: Chrome, Safari, Microsoft Edge, and Firefox. To be more
precise, all major browser vendors support WebDriver, a W3C standard.
The browser market nowadays is more diversi ied (based on the StatsCounter2, the usage
shares in May 2023 for Chrome, Safari, Edge, and Firefox are 66.0%, 12.8%, 9.9%, and 5.3%,
respectively). It is logical that all external facing web sites require serious cross-browser testing.
Selenium is a natural choice for this purpose because it far exceeds other web automation tools and
frameworks.
Chrome
To run Selenium tests in Google Chrome, besides the Chrome browser itself, ChromeDriver3 needs
to be installed. Visit https://chromedriver.chromium.org/downloads4 and choose a
version that matches your browser (Figure 1-9).
Download the one for your target platform, unzip it, and put the chromedriver executable in
your PATH. To verify the installation, open a command window, execute the command
chromedriver –version. You should see something like Figure 1-10.
Figure 1-10 Verifying the ChromeDriver version via the command line
The following code launches a new Chrome browser window and closes it one second later:
namespace HelloSeleniumTest;
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
public class HelloSelenium {
public static void main(String[] args) {
IWebDriver driver = new ChromeDriver();
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
}
C# beginners might be confused/frustrated when running this simple code snippet, with issues related
to “project,” “namespace,” and “compiling.” Don't worry. We will cover the test execution shortly. For
readers who have used a good scripting language such as Ruby: Yes, test automation with Ruby is a lot
easier.
Firefox
Selenium for Firefox requires geckodriver5. The following test script will open a web site in a new
Firefox window:
using OpenQA.Selenium.Firefox;
// ...
IWebDriver driver = new FirefoxDriver();
Safari
Safari is the default macOS web browser. While Safari supports WebDriver out of the box, you will
need to do minor setup to enable automation.
1.
Enable Developer Tools in Safari.
Open Safari’s Preferences (Safari ➤ Settings on the top bar or Command + ,).
In the Advanced tab, check the “Show Develop menu in menu bar” box. See Figure 1-11.
You should see the Develop menu in the top bar of Safari now.
2. Enable Remote Automation in Safari.
Under the Develop menu tab, check the “Allow Remote Automation” option (Figure 1-12).
Figure 1-12 Enabling Remote Automation in Safari’s menu bar
using OpenQA.Selenium.Safari;
// ...
IWebDriver driver = new SafariDriver();
Edge
Edge is Microsoft’s new default web browser on Windows 10+. The current Edge is based on
Chromium (the legacy version was deprecated in March 2021). To drive Edge with WebDriver, you
need Microsoft Edge WebDriver6. The installation process is the same as ChromeDriver, and the ile
is msedgedriver.exe.
using System.IO;
using OpenQA.Selenium.Edge;
// ...
IWebDriver driver = new EdgeDriver();
Internet Explorer
Internet Explorer has retired. In a rare case that you need to test a certain website against IE, it is
still possible. Selenium requires IEDriverServer to drive IE browser. Its installation process is very
similar to ChromeDriver. IEDriverServer is available at
https://www.selenium.dev/downloads7. Choose the right one based on your Windows
version (32- or 64-bit).
using OpenQA.Selenium.IE;
//...
IWebDriver driver = new InternetExplorerDriver();
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Safari;
using System.Collections.ObjectModel;
[TestClass]
public class GoogleSearchDifferentBrowsersTest {
[TestMethod]
public void TestInIE() {
IWebDriver driver = new InternetExplorerDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
[TestMethod]
public void TestInFirefox() {
IWebDriver driver = new FirefoxDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
[TestMethod]
public void TestInChrome() {
IWebDriver driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
[TestMethod]
public void TestInSafari() {
IWebDriver driver = new SafariDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
[TestMethod]
public void TestInEdge() {
// Default option, MicrosoftWebDriver.exe must be in PATH
IWebDriver driver = new EdgeDriver();
driver.Navigate().GoToUrl("https://agileway.com.au/demo");
System.Threading.Thread.Sleep(1000);
driver.Quit();
}
}
[TestMethod] annotates a test case below, in a format of TestCapitalCase. You will ind
more about VS Unit Test Framework from its home page8. However, we honestly don’t think it is
necessary. The part used for test scripts is not much and is quite intuitive. After studying and trying
out some examples, you will be quite comfortable with it.
[ClassInitialize]
public static void BeforeAll() {
// run before all test cases
}
[TestInitialize]
public void Before() {
// run before each test case
}
[TestMethod]
public void TestCase1() {
// one test case
}
[TestMethod]
public void TestCase2() {
// another test case
}
[TestCleanup]
public void After() {
// run after each test case
}
[ClassCleanup]
public static void AfterAll() {
// run after all test cases, typically, close browser
}
Please note that [ClassCleanup] does not guarantee all tests from one test class have been
executed before a new test class is initialized. What does this mean for your execution? If you run
one test case or all test cases in a single test class, [ClassCleanup] is invoked, it will run as you
expected. However, when you run several test classes in one go, when [ClassCleanup] is
invoked, it is nondeterministic (which could lead to many opened browser windows). This is not
good. Read this MSDN blog post for explanation: “ClassCleanup May Run Later Than You Think.”9
A pop-up window lists all artifacts (test methods and classes) in the project for your selection.
The search starts as soon as you type, as you can see in Figure 1-14.
Figure 1-14 Visual Studio Code’s Navigate To Symbol search box jumping to a speci ic test
Figure 1-16 shows a screenshot of the execution panel where one test case has passed.
Figure 1-16 Visual Studio Code execution panel after executing a test
Figure 1-17 The Run All Tests (in the current ile) button in Visual Studio Code
Figure 1-18 is a screenshot of the execution panel where all test cases in a test script ile have
passed.
Figure 1-18 Visual Studio Code execution panel after executing tests
cd C:\books\SeleniumRecipes-C#\recipes\SeleniumRecipes
3.
Run all test cases using the dotnet test in one test script (class).
Run all tests in a ile with dotnet test -- ilter NameSpace.TestClass.
4.
Run an individual test case.
To only run a single test case, use NameSpace.TestClass.TestMethod instead. For
example:
To run multiple test cases, use double quotations around the ilter and the OR operator (|) to
separate test classes:
Sample output:
Footnotes
1 https://dotnet.microsoft.com/en-us/download/dotnet/7.0
2 http://gs.statcounter.com/
3 http://chromedriver.storage.googleapis.com/
4 https://chromedriver.chromium.org/downloads
5 https://github.com/mozilla/geckodriver
6 https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
7 www.selenium.dev/downloads
8 http://msdn.microsoft.com/en-us/library/ms243147
9 http://blogs.msdn.com/b/ploeh/archive/2007/01/06/classcleanupmayrunlaterthanyouthink.aspx
10 http://nunit.org/
© Courtney Zhan, Edited by Zhimin Zhan 2024
Z. Zhan (ed.), Selenium WebDriver Recipes in C#
https://doi.org/10.1007/979-8-8688-0023-8_2
As you may have already igured out, to drive an element in a page, you need to ind it irst. Selenium uses
locators to ind and match elements on web page. There are eight locators in Selenium, shown in Table 2-1.
Table 2-1 Selenium Locators
Table Example
ID FindElement(By.Id("user"))
Name FindElement(By.Name("username"))
Link Text FindElement(By.LinkText("Login"))
Partial Link Text FindElement(By.PartialLinkText("Next"))
Xpath FindElement(By.Xpath("//div[@id='login']/input"))
Tag Name FindElement(By.TagName("body"))
Class Name FindElement(By.ClassName("table"))
CSS FindElement(By.CssSelector, "#login > input[type="text"]"))
Selenium WebDriver 4 (released in October 2021) added a new locator type called Relative, which we
don’t ind that useful, yet. We include one simple example use of one relative locator in this chapter and
more in Chapter 25.
You may use any one of the locators to narrow down the element you are looking for.
Start a Browser
Testing websites starts with a browser.
Note: Test Site We prepared the test pages for the recipes. You can download them (in a zip ile) from
the book’s site. Unzip to a local directory and refer to test pages like this:
siteUrl = "file:///Users/courtney/work/books/SeleniumRecipes-
C%23/site";driver.Navigate().GoToUrl(siteUrl + "/button.html");
For beginners, we recommend closing the browser window at the end of a test class.
driver.Quit();
There is also driver.Close(), which closes the browser window with the focus. driver.Quit()
closes all browser windows and ends the WebDriver session.
A simple way to ind the HTML fragment for a web element is to right-click the web element in a Chrome
browser and select Inspect, as shown in Figure 2-1.
Then choose an appropriate Selenium locator based on the HTML fragment. It is not hard, and you don’t
even have to know HTML well. After some trial and error (like the recipes in this book), you will get it quite
quickly. For example, for the text box in Figure 2-1, name="comment" can be used to locate it.
Find an Element by ID
Using an ID is the easiest and safest way to locate an element in HTML, if it exists. For a web page that is
W3C HTML compliant1, the IDs should be unique and identi ied in web controls. In comparison to texts, test
scripts that use IDs are less prone to application changes (e.g., developers may decide to change the label,
but are less likely to change the ID).
driver.FindElement(By.Id("submit_btn")).Click(); // Button
driver.FindElement(By.Id("cancel_link")).Click(); // Link
driver.FindElement(By.Id("username")).SendKeys("agileway"); // Textfield
driver.FindElement(By.Id("alert_div")).getText(); // HTML Div element
driver.Navigate().GoToUrl(TestHelper.SiteUrl() + "/locators.html");
driver.FindElement(By.Name("comment")).SendKeys("Selenium Cool");
driver.FindElement(By.LinkText("Cancel")).Click();
Some testers feel intimidated by the complexity of XPath. However, in practice, there is only a limited
scope of XPath to master for testers.
The copied XPath for the second Click here link in the example is as follows:
//*[@id="container"]/div[3]/div[2]/a
It works. However, we do not recommend this approach because the test script is fragile. If a
developer later adds another div under <div id="container">, the copied XPath will no longer be
correct for the element, while //div[contains(text(), "Second")]/a[text()="Click
here"] still works.
In summary, XPath is a very powerful way to locate web elements when Id, Name, or LinkText are
not applicable. Try to use an XPath expression that is less vulnerable to structure changes around the
web element.
driver.FindElement(By.TagName("body")).Text;
The above test statement returns the text view of a web page. This is a very useful one because Selenium
WebDriver does not have a built-in method to return the text of a web page.
// the below will return error "Compound class names not permitted"
// driver.FindElement((By.ClassName("btn btn-deault btn-primary")).Click();
The ClassName locator is convenient for testing JavaScript/CSS libraries (such as TinyMCE), which
typically use a set of de ined class names.
// inline editing
driver.FindElement(By.Id("client_notes")).Click();
System.Threading.Thread.Sleep(500);
driver.FindElement(By.ClassName("editable-textarea")).SendKeys("inline
notes");
System.Threading.Thread.Sleep(500);
driver.FindElement(By.ClassName("editable-submit")).Click();
driver.FindElement(By.CssSelector("#div2 >
input[type='checkbox']")).Click();
However, the use of a CSS selector is generally more prone to structure changes of a web page.
You will ind more examples of using this new relative locators in Chapter 25.
There is another way: you can chain several FindElement instances to ind a child element.
driver.FindElement(By.Id("div2")).FindElement(By.Name("same")).Click();
ReadOnlyCollection<IWebElement> checkbox_elems =
driver.FindElements(By.XPath("//div[@id='container']//input[@type='checkbox']"
System.Console.WriteLine(checkbox_elems); // => 2
checkbox_elems[1].Click();
Sometimes FindElement fails due to multiple matching elements on a page, which you were not
aware of. FindElements will come in handy to ind them out.
2. One simple workaround (more like a hack, but it works) is to type the following in the browser console:
Footnotes
1 https://zhiminzhan.medium.com/too-many-failed-javascript-test-automation-frameworks-fed006da05ec
2 https://testguild.com/course/relative-locators
© Courtney Zhan, Edited by Zhimin Zhan 2024
Z. Zhan (ed.), Selenium WebDriver Recipes in C#
https://doi.org/10.1007/979-8-8688-0023-8_3
3. Hyperlink
Courtney Zhan1
(1) Fitzgibbon, QLD, Australia
Hyperlinks (or links) are fundamental elements of web pages. As a matter of fact, hyperlinks make the World
Wide Web possible. A sample link is shown in Figure 3-1 and then we show the HTML source.
HTML source:
driver.FindElement(By.LinkText("Recommend Selenium")).Click();
Please note that using CSS (e.g. text-transform: uppercase;) might change the text view from
its HTML source. In this case, use the text as you see.
Click a Link by ID
driver.FindElement(By.Id("recommend_selenium_link")).Click();
Furthermore, if you are testing a web site with multiple languages, using IDs is probably the most
favorable option. You do not want to write test scripts like the following:
if (isItalian()) {
driver.FindElement(By.LinkText("Accedi")).Click();
} else if (isChinese()) { // a helper function determines the locale
driver.FindElement(By.LinkText, "登录").Click();
} else {
driver.FindElement(By.LinkText("Sign in")).Click();
}
1.D. The copyright laws of the place where you are located also
govern what you can do with this work. Copyright laws in most
countries are in a constant state of change. If you are outside
the United States, check the laws of your country in addition to
the terms of this agreement before downloading, copying,
displaying, performing, distributing or creating derivative works
based on this work or any other Project Gutenberg™ work. The
Foundation makes no representations concerning the copyright
status of any work in any country other than the United States.
1.E.6. You may convert to and distribute this work in any binary,
compressed, marked up, nonproprietary or proprietary form,
including any word processing or hypertext form. However, if
you provide access to or distribute copies of a Project
Gutenberg™ work in a format other than “Plain Vanilla ASCII” or
other format used in the official version posted on the official
Project Gutenberg™ website (www.gutenberg.org), you must, at
no additional cost, fee or expense to the user, provide a copy, a
means of exporting a copy, or a means of obtaining a copy upon
request, of the work in its original “Plain Vanilla ASCII” or other
form. Any alternate format must include the full Project
Gutenberg™ License as specified in paragraph 1.E.1.
• You pay a royalty fee of 20% of the gross profits you derive from
the use of Project Gutenberg™ works calculated using the
method you already use to calculate your applicable taxes. The
fee is owed to the owner of the Project Gutenberg™ trademark,
but he has agreed to donate royalties under this paragraph to
the Project Gutenberg Literary Archive Foundation. Royalty
payments must be paid within 60 days following each date on
which you prepare (or are legally required to prepare) your
periodic tax returns. Royalty payments should be clearly marked
as such and sent to the Project Gutenberg Literary Archive
Foundation at the address specified in Section 4, “Information
about donations to the Project Gutenberg Literary Archive
Foundation.”
• You comply with all other terms of this agreement for free
distribution of Project Gutenberg™ works.
1.F.
Most people start at our website which has the main PG search
facility: www.gutenberg.org.