blog.testowka.pl

Kurs Selenium

Kurs Selenium część 7 – Asynchronicznie doładowywane elementy na stronach

opublikowany przez 26, Sie, 2013, w kategoriach Kurs Selenium

Całość kursu dostępna tutaj.

Kilka razy zgłaszaliście się już do mnie z pytaniami odnośnie problemu z asynchronicznym doładowywaniem poszczególnych elementów na stronach internetowych, które testujecie.

Problem ten objawia się często niedeterministycznym zachowaniem testów, które raz na jakiś czas nie przechodzą. Oczywiście może to być objaw także wielu innych problemów, niemniej jednak najczęściej spowodowane jest to właśnie tym, że niektóre elementy na stronie są doładowywane asynchronicznie już po załadowaniu całej strony.

Jak wcześniej pisałem WebDriver opiera się na działaniu silnika przeglądarki. Przeglądarki internetowe po załadowaniu i wyświetleniu kodu strony (kodu html) otrzymują od serwera status „200 OK”. To właśnie na podstawie tej informacji Selenium uznaje, że strona się już załadowała więc pora na rozpoczęcie kolejnych akcji.

Coraz częściej jednak załadowanie samego kodu html z serwera to dopiero początek renderowania strony internetowej. Po potwierdzeniu statusu od serwera następuje jeszcze szereg asynchronicznych wywołań, które doładowują do strony poszczególne elementy. Często też różne efekty graficzne jak płynne wyłanianie się poszczególnych elementów, dzieje się już po załadowaniu strony, a elementy te nie są jeszcze przez chwile widoczne.

Podsumowując czasem Selenium zobaczy taki element (jeśli wyświetli się on dostateczni szybko) a czasem nie. Brak deterministycznego zachowania testów zasadniczo obniża ich jakość i wartość.

Oczywiście można próbować wstawiać różnego rodzaju pauzy w naszych testach – niestety takie podejście po pierwsze brzydko wygląda a po drugie zasadniczo spowalnia nasze testy.

Jest na to lepszy sposób. Selenium (od niedawna0 dostarcza wbudowanej funkcji, która oczekuje na pojawienie się elementu:

WebElement myDynamicElement = (new WebDriverWait(driver, 10))
  .until(ExpectedConditions.presenceOfElementLocated(By.id("login")));

Powyższe sprawdza, czy element jest widoczny w strukturze html oraz widoczny na stronie (to nie zawsze to samo).

Możemy też sprawdzić, czy element jest na przykład klikalny:

WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit")));

Dzięki takiemu podejściu unikamy brzydkich opóźnień w naszych testach. Powyższe kawałki kodu oznaczają, że test będzie czekał maksymalnie 10 sekund na to aż element się pojawi, sprawdzając co kilka milisekund czy być może się już pojawił. Gdy tylko element stanie się widoczny skrypt oczekujący przerwie czekanie i nasze testy ruszą dalej.

Pozostaje pytanie czy zawsze stosować tego typu podejście? Możemy nawet ten kawałek kodu dodać bezpośrednio do metod typu „insertText” czy „click”, które sobie stworzymy. Należy jednak pamiętać, że tego typu podejście będzie o 10 sekund opóźniało wykonanie każdego testu zanim test nie przejdzie, jeśli jakiś element faktycznie się nie wyświetla.

17 komentarzy więcej...

Kurs Selenium część 6 – parametryzacja testów

opublikowany przez 21, Sty, 2013, w kategoriach Kurs Selenium

Całość kursu dostępna tutaj.

W poprzedniej części kursu testowaliśmy formularz logowania. Przetestowaliśmy jedynie pozytywny scenariusz a co z negatywnymi – co z walidacją?
Pierwszy pomysł jaki się nasuwa to, że trzeba skopiować powyższy test i zmieniać w kopiach dane wejściowe. Niestety tworzymy w ten sposób wiele duplikacji, które utrudnią dalsze utrzymywanie testów – co na przykład jeśli zmieni się formularz logowania, albo w ogóle sposób logowania?

Jest lepsze – bardziej generyczne rozwiązanie.

Wystarczy naszym testom nadać parametry – użyjemy w tym celo adnotacji @DataProvider.

Najpierw potrzebujemy wprowadzić drobne zmiany w naszej metodzie logIn() tak by przyjmowała parametry:

private void logIn(String login, String password) {
	WebElement loginField = driver.findElement(By.id("login"));
	WebElement passwordField = driver.findElement(By.id("password"));
	WebElement commit = driver.findElement(By.name("commit"));

	loginField.sendKeys(login);
	passwordField.sendKeys(password);
	commit.click();
}

Zalecam przeciążenie tej metody – czyli utworzenie dwóch metod logIn() oraz logIn(String login, String password) – dzięki takiemu podejściu nasz poprzedni test nadal będzie działał ale co najważniejsze metoda logIn() bez parametrów będzie mogła być użyta w wielu testach bez obaw o ewentualne zmiany. Przykładowo gdybyśmy zawsze używali w każdym teście logIn(„admin”, „password”) i nagle z jakiś powodów – np. polityki dotyczącej haseł, zmienia się hasło w naszych danych testowych, albo w ogóle nie logujemy się już przy użyciu hasła tylko np. przez facebooka. W takiej sytuacji dzięki przeciążonej metodzie logIn() możemy zmienić sposób logowania tylko w jednym miejscu i wszystko nadal będzie działać.

private void logIn() {
	logIn("admin", "password");
}

Dobrze, skoro mamy już metodę służącą do logowania i przyjmującą parametry to teraz należy przygotować nasz test tak by parametry przyjmował.

Robi się to w następujący sposób. W adnotacji @Test przekazujemy dataProvider – wskazujemy nazwę providera. Poniżej definiujemy wspomniany provider przy użyciu adnotacji @DataProvider.

W metodzie testu przekazujemy parametry shouldNotBePossibleToLogInWithIncorrectCredentials(String login, String password)

import org.testng.annotations.DataProvider;

@Test(dataProvider = "wrongLoginCredentials")
public void shouldNotBePossibleToLogInWithIncorrectCredentials(
	String login, String password) {
	// GIVEN
	openLoginPage();
	// WHEN
	logIn(login, password);
	// THEN
	checkIfLogedIn();
}

@DataProvider
public Object[][] wrongLoginCredentials() {
	return new Object[][] { { "admi", "password" }, { "admin", "passwor" },
			{ "admi", "passwor" }, { "admi", "" }, { "", "password" }, { "", "" }, };
}

Zastosowań @DataProvider może być wiele – odsyłam do dokumentacji.

11 komentarzy więcej...

Kurs Selenium część 5 – Ładne testy

opublikowany przez 17, Sty, 2013, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj

Stwórzmy troszeczkę bardziej skomplikowany test. Spróbujmy się zalogować do http://demo.bananascrum.com

@Test
public void test1() {
	driver.get("http://demo.bananascrum.com/login");
	WebElement a = driver.findElement(By.id("login")); //pole z loginem
	a.sendKeys("admin"); //wpisuje login
	WebElement b = driver.findElement(By.id("password"));  //pole z haslem
	b.sendKeys("password");//wpisuje haslo
	WebElement c = driver.findElement(By.name("commit")); //klikam zatwierdz
    c.click();
	driver.findElement(By.id("admin")).isDisplayed(); //Sprawdź czy zalogowany
}

Czy coś z tym testem jest nie tak?
Przecież działa…

Przede wszystkim problemem są w nim komentarze. Jeśli nasz kod potrzebuje komentarzy to znaczy, że nie jest wystarczająco przejrzysty. I tak własnie jest w przypadku tego kodu.

Zanim zabiorę się do komentarzy wypadało by się zastanowić co chcemy przetestować – z tego testu nie wynika to wprost.

Przede wszystkim należało by zmienić nazwę.

@Test
public void shouldBePossibleToLogInWithCorrectData() {
...

Używam konwencji z „should” na początku – dzięki temu nazwa testu zawsze wskazuje na to co chcemy przetestować.
Nazwa ma dodatkowo bardzo istotne znaczenie – raczej staramy się by nasze testy były jak najbardziej atomowe i testowały zawsze jedną rzecz. Dzięki takiemu podejściu informacja jaką nam dają jest zawsze przejrzysta – dokładnie wiemy co się zepsuło i gdzie szukać błędów.

Jeśli w nazwie swojego testu wpisujesz „and” albo „or” to wiedz, że coś się dzieje…

Komentarze…

Z komentarzami główny problem jest taki, że przeważnie są nieaktualne i mylące – o ile w momencie gdy piszemy kod bywają pomocne to z czasem tylko przeszkadzają. Pamiętajmy o tym, że nasz kod żyje swoim życiem i cały czas się rozwija.

Jak się ich pozbyć? Można na przykład stosować odpowiednie nazwy metod.

Krok pierwszy – Extract Method.

@Test
public void shouldBePossibleToLogInWithCorrectCredentials() {
	openLoginPage();
	logIn();
	checkIfLogedIn();
}

private void checkIfLogedIn() {
	driver.findElement(By.id("admin")).isDisplayed();
}

private void logIn() {
	WebElement a = driver.findElement(By.id("login")); //pole z loginem
	a.sendKeys("admin"); //wpisuje login
	WebElement b = driver.findElement(By.id("password"));  //pole z haslem
	b.sendKeys("password");//wpisuje haslo
	WebElement c = driver.findElement(By.name("commit")); //klikam zatwierdz
    c.click();
}

Polecam używać skrótu klawiszowego ALT+SHIFT+M. Zaznaczamy kawałek kodu i wciskamy skrót – pozostaje podanie nazwy metody.

Wytworzone w ten sposób metody pozostawiają wiele do życzenia.

Zwłaszcza metoda logIn() nadal wymaga komentarzy…

private void logIn() {
	WebElement login = driver.findElement(By.id("login"));
	WebElement password = driver.findElement(By.id("password"));
	WebElement commit = driver.findElement(By.name("commit")); 

	login.sendKeys("admin");
	password.sendKeys("password");
	commit.click();
}

Teraz wygląda to trochę lepiej. Nie jest jeszcze perfekcyjnie ale do tego przejdziemy później.

Krok 2 – struktura testów.

@Test
public void shouldBePossibleToLogInWithCorrectCredentials() {
	openLoginPage();
	logIn();
	checkIfLogedIn();
}

Powyższy test wygląda już całkiem nieźle.

Widać w nim pewną strukturę:
– najpierw przygotowujemy sobie środowisko testowe (otwieramy testowaną stronę)
– następnie wykonujemy testowaną akcję
– i weryfikujemy czy akacja przyniosła oczekiwany efekt.

Powyższą strukturę można opisać jako Given, When, Then (konwencja znana praktykom BDD). Given – kontekst testu, When – akcja do wykonania (zawsze jedna), Then – sprawdzenie poprawności wykonywanej akcji, asercje.

@Test
public void shouldBePossibleToLogInWithCorrectCredentials() {
	// GIVEN
	openLoginPage();
	// WHEN
	logIn();
	// THEN
	checkIfLogedIn();
}

To by było na tyle jak na początek. Więcej w praniu…
Polecam książkę Clean Code – Robert C. Martin.
Czystość i przejrzystość testów to podstawa jeśli zależy nam na ich utrzymywalności i rozwijalności w przyszłości. A zależy nam na tym zawsze – inaczej nie było by sensu pisać testów.

W następnych częściach kursu będzie o tym jak parametryzować testy oraz rozpoczniemy przygodę z wzorcami projektowymi. Nasze testy nadal nie są obiektowe – a warto by było wykorzystać w pełni możliwości języków obiektowych takich jak Java.

Dodaj komentarz więcej...

Kurs Selenium część 4 – Pierwszy Test

opublikowany przez 22, Lis, 2012, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj
Potrzebujemy zaimportować odpowiednie zależności:

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.Test;

Powyższe powinno wystarczyć
Aby nasze testy mogły korzystać z selenium driver potrzebujemy zainicjować obiekt WebDriver oraz potrzebujemy adnotacji @Test.

@Test
public AppTest(String testName) {
	WebDriver driver = new FirefoxDriver();
	driver.get("http://blog.testowka.pl");
        driver.close();
}

Teraz wystarczy trochę posprzątać. Przede wszystkim wyciągamy inicjację drivera do metody setUp() oznaczoną adnotacją @BeforeClass.

@BeforeClass
public void setUp() {
	driver = new FirefoxDriver();
}

Następnie zamykanie przeglądarki do metody tearDown() oznaczonej adnotacją @AfterClass

@AfterClass
public static void tearDown() {
	driver.close();
}

Nasz test powinien wyglądać tak:

import org.testng.annotations.AfterClass;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class AppTest {

	static WebDriver driver;

	@BeforeClass
	public void setUp() {
		driver = new FirefoxDriver();
	}

	@Test
	public void ShouldTestowkaPlPageBeOpenable() {
		driver.get("http://blog.testowka.pl");
	}

	@AfterClass
	public static void tearDown() {
		driver.close();
	}
}

Jak zapewne zauważyliście nasz test jeszcze niczego nie testuje, gdyż nie ma w nim asercji. Dodajemy więc asercje sprawdzającą czy tytuł strony jest poprawny.

Importujemy bibliotekę z asercjami:

import static org.testng.Assert.*;

Dodajemy asercje do testu:

@Test
public void ShouldTestowkaPlPageBeOpenable() {
	driver.get("http://blog.testowka.pl");
	assertEquals(driver.getTitle(), "blog.testowka.pl");
}

 

6 komentarzy więcej...

Kurs Selenium część 3 – Operacje na elementach stron WWW

opublikowany przez 19, Lis, 2012, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj

Selenium WebDriver jest biblioteką w pełni obiektową. Zgodnie z założeniem wszystko w tym także elementy na stronie są obiektami. Dzięki takiemu podejściu możemy elementy wyszukiwać i zapisywać w pamięci jako obiekty, po czym wykonywać akcje na obiektach.

Model obiektowy przydaje się także podczas modelowania struktury naszych testów, ale o tym będzie w kolejnych rozdziałach.

Najpierw inicjalizujemy obiekt typu WebElement

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

 

WebElement linkToAgilePage = driver.findElement(By.linkText("Agile"));

Istnieje wiele różnych sposobów na wyszukiwanie elementów na stronie.
Aby sprawdzić czego możemy użyć wpisujemy w Eclipse:

driver.findElement(By.

i wciskamy [SPACJA]. IDE podpowie nam czego możemy użyć.

Najczęściej używane (przeze mnie) to:

By.id();
By.name();
By.linkText();
By.xpath();

Dzieki takiej różnorodności możemy wybrać metodę poszukiwania elementów na stronie, która będzie najmniej ryzykowna. Chodzi tutaj o ryzyko tego, że dany element wkrótce może się zmienić albo nie będzie unikatowy.

Następnie możemy wykonywać już akcje na wyszukanych obiektach.

linkToAgilePage.click();

Przykładowy test z kilkoma akcjami wykonanymi na elementach strony znajdziecie poniżej. W następnym rozdziale zajmiemy się czyszczeniem naszych testów tak by były bardziej fczytelne.

package pl.testowka;

import static org.testng.Assert.*;
import org.testng.annotations.AfterClass;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class TestowkaTest {

	static WebDriver driver;

	@BeforeClass
	public void setUp() {
		driver = new FirefoxDriver();
	}

	@Test
	public void ShouldBePosibleToSearchTestowkaAtGoogle()
			throws InterruptedException {
		driver.get("http://google.pl");
		WebElement searchField = driver.findElement(By.name("q"));
		searchField.sendKeys("testowka.pl");
		WebElement searchButton = driver.findElement(By
				.xpath("//*[@id=\"gbqfb\"]/span"));
		searchButton.click();
		for (int second = 0;; second++) {
			if (second >= 60)
				fail("timeout");
			try {
				if (driver.findElement(By.linkText("Testowka.pl"))
						.isDisplayed()) {
					break;
				}
			} catch (Exception e) {
			}
			Thread.sleep(1000);
		}
		WebElement linkToTestowka = driver.findElement(By
				.linkText("Testowka.pl"));
		linkToTestowka.click();
	}

	@AfterClass
	public static void tearDown() {
		driver.close();
	}

}

 

5 komentarzy więcej...

Kurs Selenium część 2 – Maven i zależności

opublikowany przez 13, Lis, 2012, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj

Aby utworzyć nowy projekt przy użyciu Maven wchodzimy do katalogu workspace wskazanego w eclipse i w konsoli wpisujemy:

mvn archetype:generate

Kolejno odpowiadamy na pytania:
Pierwsze zostawiam default: ENTER
Wersję maven:  zostawiam domyślnie  ENTER
GroupID : wpisuję nazwę np. „testowka”
ArchetypeID: wpisuję np. „pl”
Wersję projektu:  zostawiam domyślnie ENTER
Package: zostawiam domyślnie jako „testowka”

Następnie sprawdzamy czy projekt się kompiluje.

mvn clean install

Otwieramy Eclipse klikamy prawym przyciskiem myszy na drzewie projektów i wybieramy Import -> Maven Project -> wybieramy katalog utworzonego projektu.

Dla pewności w Eclipse odpalamy Run As -> Maven Install.

W eclipse klikamy prawym na plik pom.xml i wybieramy Maven -> Add dependency. Wpisujemy w wyszukiwarkę TestNG wybieramy org.testng testng, następnie to samo dla Firefox org.seleniumhq.selenium selenium-firefox-driver.

Teraz już możemy używać selenium WebDriver w naszych testach odpalanych przy pomocy TestNG.

Nasz plik pom.xml powinien wyglądać mniej więcej tak:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>testowka</groupId>
  <artifactId>pl</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>test</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>

    <dependency>
    	<groupId>org.testng</groupId>
    	<artifactId>testng</artifactId>
    	<version>6.8</version>
    </dependency>
    <dependency>
    	<groupId>org.seleniumhq.selenium</groupId>
    	<artifactId>selenium-chrome-driver</artifactId>
    	<version>2.42.2</version>
    </dependency>
    <dependency>
    	<groupId>org.seleniumhq.selenium</groupId>
    	<artifactId>selenium-firefox-driver</artifactId>
    	<version>2.42.2</version>
    </dependency>
  </dependencies>
</project>
12 komentarzy więcej...

Kurs selenium część 1 – instalacja i setup środowiska

opublikowany przez 09, Lis, 2012, w kategoriach Automatyzacja, Kurs Selenium, Testowanie

Całość kursu dostępna tutaj

Początki są podobno najtrudniejsze…

Poniżej krótki opis tego co będziemy potrzebować by rozpocząć pracę z testami selenium (W środowisku Java).

Co będziemy potrzebować:

  • Java (JDK 1.7)
  • Eclipse
  • Maven
  • Firefox
  • Pluginy do Eclipse

Poniżej krótka instrukcja instalacji poszczególnych narzędzi.

JDK 1.7

Pobieramy najnowsza wersję Java SE JDK ze strony http://www.oracle.com/technetwork/java/javase/downloads/index.html i instalujemy za pomocą instalatora.

Eclipse lub IntelliJ

Pobieramy najnowszą wersję Eclipse IDE for Java EE z http://www.eclipse.org/downloads/. Rozpakowujemy archiwum do jakiegoś katalogu. Uruchamiamy Eclipse poprzez kliknięcie w ikonę eclipse.exe (Windows) lub z konsoli (Linux).

Lub alternatywnie IntlliJ http://www.jetbrains.com/idea/download/ wtedy mamy natywne wsparcie dla Mavena, Gita i innych przydatnych później rzeczy.

Maven

Pobieramy najnowszą wersję Maven z http://maven.apache.org/download.html. Rozpakowujemy archiwum w wybranym katalogu.

Maven Konfiguracja (Windows)

Wchodzimy w Panel Sterowania -> System i Zabezpieczenia -> System – Zaawansowane Ustawienia Systemu -> Zaawansowane ->Zmienne Środowiskowe.

Dodajemy/edytujemy zmienne systemowe :

JAVA_HOME = (Ścieżka do katalogu gdzie zainstalowane jest JDK)

MAVEN_HOME = (Ścieżka do katalogu z Mavenem)

M2 = %MAVEN_HOME%\bin

PATH = %M2%; %JAVA_HOME%\bin (UWAGA! Jeśli zmienna PATH istnieje to nie usuwamy jej zawartości tylko dodajemy to co po lewej na końcu po średniku)

Otwieramy konsolę i sprawdzamy czy Maven działa poprzez wpisanie

 mvn --version 

Firefox

No bez przesady… Dacie radę sami…

Zalecam jednak by wersja przeglądarki była tak 1-2 wersje do tyłu. Twórcy Selenium mają pewne opóźnienie w aktualizowaniu wersji drivera więc może się okazać (i często się okazuje), że nasze testy nie będą działać z najnowszą przeglądarką.

Pluginy do Eclipse

Wchodzimy w Help->Eclipse Market

Wyszukujemy i instaluje kolejno (za każdym razem restartując Eclipse):

– TestNG plugin

TestNG for Eclipse

http://marketplace.eclipse.org/content/testng-eclipse

6 komentarzy więcej...