Wprowadzenie do Selenium dla testerów automatyzujących

Autor: Michał Dobrzycki

Selenium Teoria

Dokumentacja Selenium

Czym jest Selenium WebDriver?

To narzędzie do automatyzacji pracy z przeglądarką.

Narzędzie dojrzałe, aktualnie mamy wersję 4.1 (a na rynku jest od 2004 roku)

Pozwala pisać testy w większości języków programowania.

Składa się z paru komponentów

Czym nie jest Selenium WebDriver?

Kompletnym narzędziem do testowania.

Rozwiązaniem wszystkich naszych bolączek.

Czy Selenium WebDriver umie sterować bezpośrednio przeglądarką?

Selenium do pracy potrzebuje driverów -> aplikacji tworzonych przez producentów przeglądarki.

Większość producentów dostarcza drivery do swoich przeglądarek.

Selenium konfiguracja

Selenium Getting Started

Paczki do zainstalowania nugetem

  • Paczka Selenium.WebDriver
  • Paczka NUnit
  • NUnit3TestAdapter

Będziemy jeszcze potrzebować drivera w wersji kompatybilnej z przeglądarką na naszym komputerze.

Nasz pierwszy skrypt

Sklonujcie projekt i robimy zadanie 1

Repo projektu

Wyszukiwanie i interakcje z elementami

Metody IWebDriver

  • Url - property nie funkcja
  • Navigate()
  • Manage()
  • FindElement()
  • Quit()
IWebDriver driver = new ChromeDriver;
driver.Url="https://www.bing.com";
driver.Quit();

Szukanie elementów po:

  • By.Id()
  • By.Name()
  • By.XPath()
  • By.CssSelector()
IWebElement loginButton = driver.FindElement(By.Id("login"));

Css Selector, szukanie oparte na klasach css (stylowanie)

Ćwiczenia CSS Selektory

XPath, szukanie oparte na strukturze HTML

Ćwiczenia XPath

Metody IWebElement:

  • FindElement()
  • FindElements()
  • Click()
  • Clear()
  • SendKeys()
  • GetAttribute()
  • Submit()

Dobre rady:

  • SetUp - startujemy i konfigurujemy przeglądarkę
  • TearDown - zamykamy przeglądarkę (pamiętajcie o driver.Quit()
  • FindElement - zwraca IWebElement, więc zapisujmy to co znajdziemy
  • Nazywajcie dobrze zmienne (element1, element2 to NIE są dobre nazwy zmiennych)

Zadanie 2 do zrobienia

Repo projektu

.FindElements()

Możemy zwracać listę (tablicę na sterydach) pasujących WebElementów

IList <IWebElement> elements = driver.FindElements(By.CssSelector("p"));
foreach (IWebElement element in elements) {
	Console.WriteLine(element.Text);
}					

Konsola i walidacja selektorów w konsoli przeglądarki

CSS Selector:

$$(".inventory_item_name")

XPath:

$x("//a")

Obydwie funkcje mają overload z $0 (szukamy w aktualnie zaznaczonym elemencie)

$$(".inventory_item_name", $0)

Property IWebElement:

  • Text
  • TagName
  • Selected

Zadanie 4 do zrobienia

Repo projektu

Specjalny element, czyli SelectElement

Wymaga instalacji paczki nuget: Selenium.Support, pozwala na

  • .SelectByValue()
  • .SelectByIndex()
  • .SelectByText()

Przykład

IWebElement sortDropdownElement = driver.FindElement(By.Id("..."));
var selectDropdown = new SelectElement(sortDropdownElement);
selectDropdown.SelectByValue("...")

Zadanie 4 i 5 do zrobienia

Repo projektu

Waity

Dokumentacja Waitów

Implicit Wait

Nie zalecane, ponieważ czeka na KAŻDY element określoną ilość czasu

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);

Explicit Wait

Nie mieszamy ich z Implicit Wait, bo czasy zaczynają nam się sumować

var foo = new WebDriverWait(driver, TimeSpan.FromSeconds(3))
    .Until(drv => drv.FindElement(By.Name("q")));

ExpectedConditions (wymaga Selenium 3.141 - porzucono w Selenium 4)

new WebDriverWait(driver, TimeSpan.FromSeconds(3))
	.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//a/h3")));
					

Fluent Wait

Pozwala określić jak często bedziemy próbowali znaleźć ponownie element

Może być dobre do zredukowania ilości zapytań, jeżeli wiemy że element pojawi się późno.

Przykład czekania aż element zniknie:

WebDriverWait wait = new WebDriverWait(driver, timeout: TimeSpan.FromSeconds(10))
{
	PollingInterval = TimeSpan.FromSeconds(1),
};

wait.Until(driver =>
	!driver.FindElement(By.Id("ajaxBusy")).Displayed);

Przykład Actions (using OpenQA.Selenium.Interactions;)

IWebElement expandingDiv = driver.FindElement(By.CssSelector(".expand"));
Actions action = new Actions(driver);
action.MoveToElement(expandingDiv);
action.Perform();

Praca z plikami

Upload plików

Dodajemy folder files do którego wrzucamy plik kotek.jpg, ustawiając mu property Copy always

string pathToFile = @".\files\kotek.jpg";
fileInput.SendKeys(Path.GetFullPath(pathToFile));

Pobieranie plików

Musimy ustawić opcje ChromeDrivera (pamiętajcie o nie zamykaniu przeglądarki zanim nie skończymy pobierać pliku)

ChromeOptions options = new ChromeOptions();
options.AddUserProfilePreference("download.default_directory", @"C:\downloads\");
options.AddUserProfilePreference("disable-popup-blocking", "true");
options.AddUserProfilePreference("download.prompt_for_download", false);
driver = new ChromeDriver(options);
					

Page Object Pattern

Założenia:

  • Oddzielenie kodu testów od kodu strony
  • Łatwiej zapanować nad zmianami UI
  • Każda widoczna część strony (lub całosć) jest obiektem
  • Wszystkie lokatory WebElementów są prywatne
  • Nie używamy w Page Objectach asercji
  • Nie używamy lokatorów w testach

Przykład testu:

LoginPage loginPage = new LoginPage(driver);
loginPage.loginAs("user@firma.pl", "Password1");
Assert.assertEquals("User FullName", loginPage.getLoggedUsername());

Teraz zostało tylko napisać LoginPage, prywatne lokatory elementów i jej dwie publiczne metody

Linki warte przeczytania:

BDD i SpecFlow

  1. Instalujemy extension SpecFlow
  2. Restartujemy Visual Studio

Dokumentacja SpecFlow

Piszemy w pliku .feature

When I search for '.NET BDD framework'

A w klasie która implementuje kroki

[When(@"I search for '(.*)'"]
public void WhenIPerformASimpleSearchOn(string searchTerm) 
{ 
	//kod testujący aplikację
}

Przykładowy Framework

Repo

Do obsługi plików JSON polecam:

Newtonsoft.JSON

Pozwala na serializację i deserializację plików JSON (json -> obiekt -> json)

Tworzymy .json z konfiguracją frameworka

{
"Browser": "Chrome",
"DownloadFolder": "TestResult",
"RunAsHeadless": false,
"ImplicitWaitTimeout": 10
}

Tworzymy klasę która będzie zamieniać jsona na obiekt

[JsonProperty("Browser", Required = Required.Always)]
[JsonConverter(typeof(StringEnumConverter))]
public readonly BrowserType CurrentBrowser;

Przed uruchomieniem odczytujemy plik, tworzymy obiekt i przekazujemy jako parametr do tworzenia Drivera

string settingsJson = File.ReadAllText(
	Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestSettings.json"));
TestSettings settings = JsonConvert.DeserializeObject<TestSettings>(settingsJson);
Driver.Initialize(settings);

Selenium Grid

Uruchomcie docker desktop i wrzućcie komendy do cmd.exe / powershella

docker pull selenium/hub:4.1.2-20220208
docker pull selenium/node-chrome:4.1.2-20220208
docker pull selenium/node-edge:4.1.2-20220208
docker pull selenium/node-firefox:4.1.2-20220208

Repozytorium z projektem

Włączamy docker desktop

Odpalamy komendę z cmd.exe (tam gdzie jest plik docker-compose.yml)

docker-compose up

Odpalamy przeglądarkę na http://localhost:4444

Analizujemy kod projektu, uruchamiamy testy i podglądamy http://localhost:4444.

Dalsza nauka własna

Darmowe źródła:

Źródła warte zainwestowania