Explicit Wait Vs Implicit Wait. Waity w Selenium WebDriver

Jedną z kluczowych umiejętności podczas programowania testów jest prawidłowe stosowanie metod klasy Wait. Dzieje się tak dlatego, iż to właśnie za ich pomocą, jesteśmy w stanie poradzić sobie z często występującymi problemami natury wszelkich opóźnień (w tym sieciowych).

Podczas wykonywania skryptów automatycznych, aplikacja oraz jej poszczególne elementy mogą odpowiadać w różnym czasie. W zależności od wielu różnych sytuacji (np. obciążenie ruchu sieciowego, moc zasięgu internetowego, nieoptymalne mechanizmy aplikacyjne) poszczególne elementy np. dymek komunikatu, czy tabela może zostać wyświetlona/wczytana na stronie o różnym czasie. Rodzi to oczywiście pewne komplikacje, gdyż mogą wydarzyć się nieoczekiwane sytuacje. Wyobraźmy sobie, iż nasz skrypt testowy został zaprogramowany do kliknięcia odpowiedniego przycisku – a elementu po prostu jeszcze nie ma wyświetlonego na stronie, gdyż aplikacja np. jest w tej chwili bardziej przeciążona niż zwykle i nie zdążyła jeszcze załadować wszystkich elementów na stronę.

Tego typu problemy możemy rozwiązać poprzez zastosowanie klasy Wait i jej metod. Poprawne wykorzystanie klasy Wait zapewni, że przed wykonaniem kolejnej operacji – Selenium WebDriver będzie czekać, aż aplikacja osiągnie gotowość do jej wykonania.

 

Klasa Wait. Co to takiego?

 

Klasa Wait wchodzi w skład biblioteki Selenium WebDriver – jest jej integralną częścią. Metody tej klasy są stosowane bardzo często podczas pracy nad programowaniem skryptów testowych. Mówiąc w dużym uproszczeniu, konkretne metody klasy wait – wstrzymują na określoną jednostkę czasu wykonywanie skryptu, zanim skrypt przejdzie do wykonywania dalszych kroków. Dzięki takiemu wstrzymaniu skryptu, dajemy czas danemu elementowi na jego pojawienie się, zaistnienie  w modelu DOM, pełne wykonanie np. możemy poczekać określoną jednostkę czasu na załadowanie się danych do tabeli (często w przypadku, gdy tabela ma wiele tysięcy danych, czas ładowania/wyrenderowania na stronie to kilka sekund). Dlaczego tak postępujemy? Dlatego, iż często zdarza się, że weryfikujemy dany element na stronie, który jeszcze jest w trakcie „Ładowania/renderowania”  i skrypt testowy wyrzuci błąd, uznając, że np. danego elementu nie ma w ogóle w modelu DOM strony internetowej.

 

Musimy wiedzieć, że czas ładowania elementów na stronie, może być różny, dlatego metody klasy Wait są tak szeroko wykorzystywane. Większość aplikacji internetowych wykorzystuje technologię Ajax czy JavaScript co tylko potęguje sytuację, w której elementy mają różne czasy ładowania.

 

 

Co w przypadku, gdy element nie zostanie zlokalizowany na stronie?

 

Jeśli element nie zostanie zlokalizowany na stronie, otrzymamy konkretny wyjątek błędu:

 

NoSuchElementException

 

 

Selenium WebDriver udostępnia trzy instrukcje do oczekiwania w testach:

 

 ImplicitWait – niejawny czas oczekiwania

• ExplicitWait – jawny czas oczekiwania

• FluentWait – płynne oczekiwanie (z dodatkowymi parametrami)

 

 

Niejawne oczekiwanie – Implicit Wait

 

Implementując ImplicitWait do skryptu, dodajemy timeout, który zostanie wykorzystany w przypadku, gdy element którego poszukuje WebDriver – nie jest widoczny od razu. W takim przypadku, gdy Webdrivier nie znajdzie szukanego elementu – poczeka zdefiniowaną w implementacji ImplicitWait jednostkę czasu. Po przekroczeniu tego czasu, zostanie wyrzucony wyjątek: NoSuchElementException – co formalnie oznacza, że poszukiwany element nie został odnaleziony na stronie internetowej.

Definicja: 

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

 

Aby korzystać z instrukcji niejawnego oczekiwania, musimy zaimportować następujący pakiet:

import java.util.concurrent.TimeUnit;

 

Wynik:

W prezentowanym przypadku, metoda implicitlyWait() będzie czekała 10 sekund

(pierwszy parametr metody to konkretna liczba: 10, drugi parametr to jednostka czasu:  sekundy )

 

 

Największą zaletą tego rozwiązania jest szybkość oraz prostota implementacji. Jak mogliśmy zobaczyć wyżej, wystarczy tylko jedna linijka kodu, aby instrukcje zostały objęte  mechanizmem oczekiwania.

 

 

 

 

Niestety niejawne oczekiwanie ma sporą wadę, która  uwidacznia się wraz ze zwiększaniem ilość kodu. Otóż niejawne oczekiwanie sztucznie  i znacznie wydłuża czas wykonywania skryptu testowego. 

Kolejną dużą wadą tego mechanizmu jest to, iż implicitlyWait() działa prawidłowo tylko z  metodą findElement(By by) (zaprezentuję to w warsztacie). Na późniejszych etapach kursu, doświadczycie, że jest to w wielu sytuacjach po prostu niewystarczalne.

 

 

 

Dlatego zalecam ograniczenie używania mechanizmu implicitlyWait(), a nawet skłaniam się ku wyeliminowaniu niejawnego czasu oczekiwania –  na rzecz niżej prezentowanych mechanizmów.

 

Przykład zastosowania:

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class WaitTest {

    private WebDriver driver;
    private String baseUrl;
    private WebElement element;

    @BeforeMethod
    public void setUp() throws Exception {
        System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
        driver = new ChromeDriver();
        baseUrl = "http://www.selenium-shop.pl/";

        driver.get(baseUrl);
        driver.manage().window().maximize();

        //ImplicitlyWait - oczekiwanie niejawne
        driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
    }

    @Test
    public void oczekiwanie_Test() {
        String correctTitle = "Moje konto – Selenium Shop Automatyzacja Testów";

        WebElement mojeKontoMenu = driver.findElement(By.linkText("MOJE KONTO"));
        mojeKontoMenu.click();

        Assert.assertEquals(driver.getTitle(), correctTitle);
    }

    @AfterMethod
    public void tearDown() throws Exception {
        driver.quit();
    }
}

 

 

 

Jawne oczekiwanie – Explicit Wait

 

Znacznie lepszym pomysłem od ImplicitWait jest stosowanie jawnego oczekiwania – ExplicitWait.

W odróżnieniu do oczekiwania niejawnego, możemy stosować predefiniowane, niestandardowe  warunki wznawiania działania kodu.

ExplicitWait posiada inną, nieco bardziej rozbudowaną implementację. Najpierw musimy utworzyć obiekt klasy WebDriverWait. Następnie przekazujemy WebDrivera wraz ze zdefiniowaną wartością timeoutu. Dopiero po tych czynnościach możemy wywołać na niej metodę until, w której wskazujemy warunek, który musi być spełniony ,aby skrypt kontynoował pracę.

Jawne oczekiwanie jest bardziej inteligentne, ale można je zastosować tylko do określonych elementów.

Za pomocą jawnego oczekiwania – wydajemy polecenie dla Webdrivera, któremu mówimy, aby oczekiwał na wystąpienie określonego warunku przed wykonaniem kodu.

Selenium WebDriver dostarcza klasy WebDriverWait ExpectedConditions, które będziemy wykorzystywać do efektywnego stosowana jawnego oczekiwania.

Selenium oprócz metod wyszukujących elementy czy metody wykonujące jakieś akcje na tych elementach, takie jak klikanie przyciski, wpisywanie tekstu w pola itp. ma dostępne również inne metody np.  do zarządzania przeglądarką czy do obsługi ciasteczek. Dlatego pamiętajmy że Implicity Wait nie współpracuje z  takimi metodami! Jeśli chcielibyśmy jakoś wymusić oczekiwanie za pomocą Implicity Wait to musielibyśmy tak naprawdę zastosować Implicity Wait na jakimś webelemencie poprzedzającym daną operacje – no ale to jak sami widzicie bardzo „chałupnicze” rozwiązanie.

Klasa ExpectedConditions oferuje zbiór predefiniowanych warunków oczekiwania na wznowienie działania kodu. Poniżej prezentuję warunki, które są najczęściej używane w codziennej pracy:

 

 

alertIsPresent();  
elementSelectionStateToBe();
elementToBeClickable();
elementToBeSelected();
frameToBeAvaliableAndSwitchToIt();
invisibilityOfTheElementLocated();
invisibilityOfElementWithText();
senceOfAllElementsLocatedBy();
senceOfElementLocated();
textToBePresentInElement();
textToBePresentInElementLocated();
textToBePresentInElementValue();
titleIs();
titleContains(); 
visibilityOf();
visibilityOfAllElements();
visibilityOfAllElementsLocatedBy();
visibilityOfElementLocated();

 

Aby korzystać z instrukcji jawnego oczekiwania musimy zaimportować następujący pakiety:

 

import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

 

Zaś sama inicjalizacja obiektu wait klasy WebDriverWait wygląda następująco:

 

WebDriverWait wait = new WebDriverWait(driver,30);

 

Podajemy dwa argumenty: 

driver – reprezentacja webdrivera

liczba (czas) –  w naszym przypadku 30 – jest to maksymalny czas oczkiwania (wyrażony w sekundach).

 

 

//explicit wiat - deklaracja, inicjalizacja
WebDriverWait wait = new WebDriverWait(driver,30);

Przykład:

//warunkowe oczekiwanie na widoczność elementu button
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//button[@id='Zaloguj sie']");
//kliknięcie w button, ta akcja wydarzy się tylko, gdy element button jest widoczny na stronie
driver.findElement(By.xpath("//button[@id='Zaloguj sie']")).click();

 

Implementacja powyższego kodu spowoduje, że Selenium webdriver będzie czekał maksymalnie 30 sekund przed zgłoszeniem wyjątku. Jeśli element zostanie znaleziony szybciej niż zadeklarowany maksymalny czas, wówczas od razu dynamicznie przejdzie do wykonywani kolejnych instrukcji.

Podkreślam słowo dynamicznie. Chodzi o to, że gdy element zostanie znaleziony np. w ciągu 5 sekund, wówczas oczekiwanie będzie wynosiło dokładnie 5 sekund. Jeśli element zostanie znaleziony w 1 sekundę wówczas program będzie oczekiwał tylko 1 sekundę – i po tym czasie przejdzie do wykonywania dalszych instrukcji.

Przykład zastosowania:

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class WaitTest {

    private WebDriver driver;
    private String baseUrl;
    private WebElement element;

    @BeforeMethod
    public void setUp() throws Exception {
        System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
        driver = new ChromeDriver();
        baseUrl = "http://www.selenium-shop.pl/";

        driver.get(baseUrl);
        driver.manage().window().maximize();
    }

    @Test
    public void oczekiwanie_Test() {
        //explicitly wait - jawne oczekiwanie - deklaracja
        WebDriverWait wait = new WebDriverWait(driver,30);

        String correctTitle = "Moje konto – Selenium Shop Automatyzacja Testów";

        //explicitly wait - jawne oczekiwanie - implementacja
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.linkText("MOJE KONTO"))).click();

        Assert.assertEquals(driver.getTitle(), correctTitle);
    }

    @AfterMethod
    public void tearDown() throws Exception {
        driver.quit();
    }
}

 

I właśnie tutaj uwidacznia się zdecydowana wyższość rozwiązania ExplicitWait nad implicitWait – nie marnujemy czasu na sztuczne wstrzymywanie skryptów.

 

FluentWait – płynny czas oczekiwania z dodatkowymi parametrami

 

Za pomocą klasy FluentWait możemy zdefiniować maksymalną ilość czasu oczekiwania na element lub warunek, a także częstotliwość sprawdzania warunku. Można również podczas wyszukiwania elementu skonfigurować konkretne typy wyjątków, takie jak wyjątek NoSuchElementException.

Mechanizm FluentWait jest podobny do jawnego czasu oczekiwania (ExplicitWait). Za jego pomocą określamy również maksymalny czas oczekiwania Webdrivewra na wypełnienie konkretnego warunku. Dodatkowo jednak określamy również częstotliwość, z jaką WebDriver sprawdzi, czy warunek został spełniony przed zgłoszeniem wyjątku / błędu .

Innymi słowy, FluentWait może wielokrotnie odpytać element o zdefiniowany warunek  w regularnych odstępach czasu, aż upłynie limit czasu lub obiekt zostanie znaleziony.

Fluent wait  jest najbardziej przydatne podczas interakcji z elementami, których załadowanie może czasem zająć więcej niż zwykle oczekujemy. Takie sytuacje są częste w w aplikacjach opartych na mechanizmach Ajax czy Javascript.

 

 

Podczas korzystania z Fluent Wait możliwe jest ustawienie domyślnego okresu odpytywania (sprawdzania warunku).

Wait wait = new FluentWait(driver)
.withTimeout(15, TimeUnit.SECONDS)
.pollingevery(3, TimeUnit.SECONDS);

 

Jak widzimy na załączonym fragmencie kodu, mamy zaimplementowane dwie dodatkowe funkcje

  • withTimeout(15, TimeUnit.SECONDS)
  • pollingevery(3, TimeUnit.SECONDS)

Odpowiadają one odpowiednio za: ustawienie wartości limitu czasu i częstotliwości odpytywania.

 

Powyższy kod określa wartość limitu czasu na 15 sekund, a częstotliwość odpytywania na 3 sekundy.

WebDriver będzie czekać maksymalnie 15 sekund na sprawdzenie określonego warunku. Jeśli warunek zostanie spełniony w ciągu 15 sekund, wykona następny krok w skrypcie testowym. Jeśli nie, wygeneruje „ ElementNotVisibleException ”. Skoro ustawiliśmy czas odpytywania na 3 sekundy to warunek zostanie odpytany dokładnie 5 razy.

 

 


 

Różnica między ImplicitWait a ExplicitWait

ImplicitWaitExplicitWait
Zdefiniowany czas oczekiwania jest stosowany do wszystkich elementów skryptuCzas oczekiwania dotyczy tylko tych elementów, które są przez nas zamierzone. Tworzymy instrukcje indywidualnie dla konkretnych elementów
Nie określamy  warunków („ExpectedConditions”)Określamy  warunki („ExpectedConditions”) dla elementu, który ma zostać zlokalizowany
Zaleca się używać, gdy elementy znajdują się w ramach czasowych określonych w  oczekiwaniuZaleca się używać, gdy ładowanie elementów zajmuje dużo czasu, a także do sprawdzania właściwości elementu, np. takich jak: (visibilityOfElementLocated, elementToBeClickable, elementToBeSelected)

 

Co zatem wybrać?

Wiecie już jakie są różnice między jawnym a niejawnym mechanizmem oczekiwania. Znacie również mocne i słabsze strony każdego z rozwiązań, więc myślę, że dojdziemy do wspólnej konkluzji. Tak jak wspomniałem już wcześniej: lepiej używać bardziej elastycznego mechanizmu jakim niewątpliwie jest Explicit Wait. Ale czy to jest jedyny i słuszny wybór? Myślę, że nie do końca, gdyż mając na uwadze wszystkie dobre i złe strony Implicity Wait, jestem w stanie przywołać sytuację, w której ten mechanizm sprawdziłby się lepiej, a na pewno szybciej. Otóż kiedy mamy do czynienia z malutkimi projektami, które musimy pisać na szybko, to należałoby rozważyć użycie właśnie Implicity Waita, z racji na jego szybką implementacje oraz w przypadku małych projektów – znikome sztuczne wydłużenie czasu działania testów.


 

ZAPAMIĘTAJ!

 

Klasa Wait – wchodzi w skład biblioteki Selenium WebDriver – jest jej integralną częścią. Metody tej klasy są stosowane bardzo często podczas pracy nad programowaniem skryptów testowych. Mówiąc w dużym uproszczeniu, określone metody klasy Wait – wstrzymują na określoną jednostkę czasu wykonywanie skryptu, zanim skrypt przejdzie do wykonywania dalszych kroków.

Selenium WebDriver udostępnia trzy instrukcje do oczekiwania w testach:

 ImplicitWait – niejawny czas oczekiwania

• ExplicitWait – jawny czas oczekiwania

• FluentWait – płynne oczekiwanie (z dodatkowymi parametrami)

Niejawne oczekiwanie – ImplicityWait

Największą zaletą tego rozwiązania jest szybkość oraz prostota implementacji.

Niestety niejawne oczekiwanie ma sporą wadę, która  uwidacznia się wraz z zwiększaniem ilość kodu. Otóż niejawne oczekiwanie sztucznie  i znacznie wydłuża czas wykonywania skryptu testowego.

Jawne oczekiwanie – ExplicitWait

W odróżnieniu do oczekiwania niejawnego, możemy stosować predefiniowane, niestandardowe  warunki wznawiania działania kodu.

ExplicitWait posiada inną, nieco bardziej rozbudowaną implementację. Najpierw musimy utworzyć obiekt klasy WebDriverWait. Następnie przekazujemy WebDrivera wraz ze zdefiniowaną wartością timeoutu. Dopiero po tych czynnościach możemy wywołać na niej metodę until, w której wskazujemy warunek, który musi być spełniony, aby skrypt kontynuował pracę.

FluentWait – płynny czas oczekiwania z dodatkowymi parametrami

Za pomocą klasy FluentWait możemy zdefiniować maksymalną ilość czasu oczekiwania na element lub warunek, a także częstotliwość sprawdzania warunku. Można również podczas wyszukiwania elementu skonfigurować konkretne typy wyjątków, takie jak wyjątek NoSuchElementException.

Klasa ExpectedConditions –oferuje zbiór predefiniowanych warunków oczekiwania na wznowienie działania kodu.

Notatka o autorze:

Zajmuję się testowaniem, zabezpieczaniem i zapewnianiem jakości oprogramowania od ponad 13 lat. Rozpocząłem swoją karierę od testów manualnych i analizy biznesowo-technicznej. Obecnie prowadzę firmę Quality Island, która zajmuje się szeroko pojętym testowaniem oprogramowania oraz szkoleniami dla przyszłych i obecnych testerów oprogramowania. Moją specjalnością są testy automatyczne aplikacji webowych oraz budowa procesów automatyzacji i robotyzacji. Od 8 lat prowadzę aktywnie szkolenia oraz konsultacje z tych tematów i wykonuję zlecenia dla firm trzecich jako konsultant, ekspert oraz audytor. Współpracuję również z firmami jako osoba do rekrutacji i weryfikacji technicznych. Interesują mnie głównie tematy związane z architekturą IT oraz zagadnienia DevOps/TestOps, ponieważ ściśle wiążą się z zapewnianiem jakości oprogramowania.

 

Tomasz Stelmach

CEO&Founder

 

0 komentarzy

Wyślij komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *