Automatyzacja testów przy użyciu biblioteki WatiN .NET

Witajcie. Dawno nic ciekawego dla Was nie pisałem, ale ostatnio ze względu na sporą ilość pytań oraz problemów związanych z Selenium Web Driver chciałbym przedstawić Wam pewną alternatywę. Selenium nie jest jedynym narzędziem do tworzenia testów automatycznych interfejsu użytkownika, w internecie znajdziecie zapewne wiele bibliotek które pełnią podobne zastosowania.

Na samym początku z samym projektem oraz jego dokumentacją możecie zapoznać się tutaj: http://watin.org/. Znajdziecie tam wszelkie informacje odnośnie implementacji oraz metod które ta bibliotek Nam oferuje. Moje pierwsze wrażenia podczas startu były bardzo pozytywne, WatiN jest dużo bardziej prostszy w implementacji, nie wymaga praktycznie żadnej konfiguracji oprócz pobranie go z NuGeta.

watin1

My na potrzeby samego wprowadzenia wykorzystamy tutaj standardową aplikacje konsolową. To nam całkowicie wystarczy. Po instalacji biblioteki nad klasą Main() musimy dodać dyrektywę [STAThread] abyśmy mogli korzystać z komponentów COM oraz ich wątków.

Uwaga! Jeśli otrzymamy komunikat błędu o brakującym pliku Interop.SHDocVw.dll to należy pobrać go z oficjalnej strony WatiNa i dodać do projektu.

[STAThread] 
static void Main(string[] args) 
{ 
    IE _ieDriver = new IE(); 
}

Czy można korzystać z innych wersji przeglądarek? Tak, ale niestety ich liczba ogranicza się tylko dla dwóch czyli IE oraz Firefox które WatiN oficjalnie wspiera.

Okej, ale teraz przejdźmy do przykładu. Nasze dzisiejsze zadanie będzie proste, szybkie i przyjemne. Stworzymy sobie aplikację która będzie automatyzować Nam rejestrację kont użytkownika na pewnej stronie. Jest to strona https://www.eversource.com/content/ którą wybrałem ze względu na to, że formularz rejestracyjny nie posiada CAPTCHY 🙂

A więc teraz chcielibyśmy otworzyć okno przeglądarki i przejść do tej właśnie strony. A na końcu okno przeglądarki zmaksymalizować. Wystarczą Nam do tego 3 linie kodu:


[STAThread]
static void Main(string[] args)
{
IE _ieDriver = new IE();
_ieDriver.GoTo("https://www.eversource.com/security/Account/Register");
_ieDriver.WaitForComplete();
_ieDriver.ShowWindow(WatiN.Core.Native.Windows.NativeMethods.WindowShowStyle.Maximize);
}

Wchodzimy tutaj właściwie już prosto do panelu rejestracyjnego. Następnie musimy zastanowić się jakie pola będą wymagane i jakich parametrów użyjemy aby je odnaleźć. Najczęściej będzie to ID, Class, Value czy Text. W WatiNie podoba mi się to, że posiada całkiem sporo gotowych metod oraz elementów które możemy wykorzystać i nie muszą być przez Nas kodowane samemu. A więc mamy tutaj kilka pól tekstowych, jedną drop down liste, oraz jednego check boxa i przycisk do przesłania formularza.

10442015004456

Oczywiście mam nadzieje, że nie muszę przypominać Wam, że będziemy musieli badać te elementy jeden po drugim co często jest żmudną pracą i nie zawsze zadziała. Bardzo często struktura strony jest na tyle skomplikowana, że nie wystarczy Nam podanie klasy czy ID elementu.

Zanim jednak zaczniemy automatyzować, warto przygotować sobie klasę, zawierającą metody pomocnicze, które będą generowały jakieś losowe ciągi znaków i liczb o odpowiedniej długości abyśmy nie musieli sami pisać tego z palca 🙂

Ja stworzyłem klasę DataRandomizer.cs zawierającą następujące metody


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WatiN.Helpers
{
public static class DataRandomizer
{
// Randomize first name
public static string CreateFakeFirstName()
{
string randName = Guid.NewGuid().ToString();
string tmp = randName.Replace("-", "");
string finalName = tmp.Substring(18);
return finalName;
}

// Randomize last name
public static string CreateFakeLastName()
{
string randName = Guid.NewGuid().ToString();
string tmp = randName.Replace("-", "");
string finalName = tmp.Substring(18);
return finalName;
}

// Randomize fake User ID
public static string CreateFakeUserID()
{
string randName = Guid.NewGuid().ToString();
string tmp = randName.Replace("-", "");
string finalName = tmp.Substring(18);
return finalName;
}

// Randomize fake email
public static string CreateFakeEmail()
{
string randEmail = Guid.NewGuid().ToString();
string tmp = randEmail.Replace("-", "");
string finalEmail = tmp.Substring(18);
return finalEmail + "@gmail.com";
}

// Randomize fake password
public static string CreateFakePassword()
{
string randPasswsd = Guid.NewGuid().ToString();
string tmp = randPasswsd.Replace("-", "");
string finalPass = tmp.Substring(18);
return finalPass;
}

// Randomize fake secret answer
public static string CreateFakeSecretAnswer()
{
string randPasswsd = Guid.NewGuid().ToString();
string tmp = randPasswsd.Replace("-", "");
string finalPass = tmp.Substring(18);
return finalPass;
}

// Randomize fake year of birth
public static string CreateFakeYear()
{
Random rand = new Random();
string tmp = rand.Next(1000, 9999).ToString();
return tmp;
}

public static string PickZIP()
{
Random rand = new Random();
var zipArray = new List<string>()
{
"06901",
"06101",
"06859",
"03101",
"02090",
"02115",
"01603",
"01101"
};
return zipArray[rand.Next(8)];
}
}
}

Następnie musimy znaleźć wszystkie elementy potrzebne do rejestracji na testowanej stronie. Zasada jest podobna jak w przypadku Selenium:


_driver.rodzajElementu(Find.BySomething("thisSomething")).Action(np.Click)

Dlatego ja już sobie wzystkie te elementy zakodowałem aby nie przedłużać i wygląda to mniej więcej tak:


 class Program
 {
 [STAThread] // Must be set for apartent state STA
 static void Main(string[] args)
 {
 
 IE _ieDriver = new IE();
 
 _ieDriver.GoTo("https://www.eversource.com/security/Account/Register");

 _ieDriver.WaitForComplete();
 _ieDriver.ShowWindow(WatiN.Core.Native.Windows.NativeMethods.WindowShowStyle.Maximize);

 _ieDriver.CaptureWebPageToFile(DateTime.Now.ToString("ddmmyyyyHHmmss") + ".png");
 _ieDriver.WaitForComplete();

 _ieDriver.TextField(Find.ByName("FirstName")).TypeText(DataRandomizer.CreateFakeFirstName());
 _ieDriver.TextField(Find.ByName("LastName")).TypeText(DataRandomizer.CreateFakeLastName());

 string tmpEmail = DataRandomizer.CreateFakeEmail();
 _ieDriver.TextField(Find.ByName("Email")).TypeText(tmpEmail);
 _ieDriver.TextField(Find.ByName("ConfirmEmail")).TypeText(tmpEmail);

 _ieDriver.TextField(Find.ByName("WebId")).TypeText(DataRandomizer.CreateFakeUserID());

 string tmpPasswords = DataRandomizer.CreateFakePassword();
 _ieDriver.TextField(Find.ByName("Password")).TypeText(tmpPasswords);
 _ieDriver.TextField(Find.ByName("ConfirmPassword")).TypeText(tmpPasswords);

 _ieDriver.Span(Find.ByText("What was your favorite place to visit as a child?")).Click();
 _ieDriver.TextField(Find.ByName("SecretAnswer")).TypeText(DataRandomizer.CreateFakeSecretAnswer());

 _ieDriver.CheckBox(Find.ByName("AcceptTerms")).Click();

 _ieDriver.Button(Find.ByValue("Register")).Click();

 _ieDriver.WaitForComplete();

 _ieDriver.TextField(Find.ById("txtZip")).TypeText(DataRandomizer.PickZIP());
 _ieDriver.Button(Find.ByValue("Enter")).Click();

 _ieDriver.WaitForComplete();
 _ieDriver.CaptureWebPageToFile(DateTime.Now.ToString("ddmmyyyyHHmmss") +".png");
 _ieDriver.WaitForComplete();
 Thread.Sleep(2000);

 _ieDriver.Close();
 }
 }

Nazwy metod są na prawdę przejrzyste i jest ich na tyle duże, że niczego nie powinno Nam do swoich testów zabraknąć. Ale teraz omówmy sobie po kolei co się tutaj dzieje:

Metoda WaitForComplete() czeka aż wcześniejsze operacje zakończą się i dopiero wtedy przechodzi do kolejnych. Przydaje się bardzo często 🙂

_ieDriver.WaitForComplete();
TypeText wpisuje Nam stringa do elementu TextField którego wyszukujemy po nazwie FirstName
_ieDriver.TextField(Find.ByName("FirstName")).TypeText(DataRandomizer.CreateFakeFirstName())

Natomiast wpisywanie w pola tekstowe nie sprawia zwykle większych problemów. Gorzej jest w przypadku dropdownów lub elementów generowanych dynamicznie.

W Naszym przypadku fragment kodu:

_ieDriver.Span(Find.ByText("What was your favorite place to visit as a child?")).Click();

Oznacza, że chcemy znaleźć element SPAN po wybranym tekscie a następnie wybrać go aby wypełnić listę gdyż bez tego Nasz formularz nie przejdzie walidacji. Dokładnie ta lista wygląda tak:

watin2

Podobnie sprawa ma sie z CheckBoxem który musimy zaznaczyć aby zaakceptować warunki umowy i ukończyć rejestrację pomyślnie. Aby to zrobić wystarczy wpisać:

_ieDriver.CheckBox(Find.ByName("AcceptTerms")).Click();

Jak widzicie wskazujemy tutaj na konkretny typ jakim jest CheckBox a nie wpisujemy Element.

watin3
Ostatnią sprawą jest wciśnięcie klawisz REGISTER aby przesłać nasz formularz i zakończyć rejestrację. To jest proste gdyż wskazujemy na typ elementu jakim jest Button i szukamy go tym razem po atrybucie Value.

_ieDriver.Button(Find.ByValue("Register")).Click();

Po przesłaniu formularza wyskoczy Nam popup w którym musimy podać kod pocztowy. Nie wiem dlaczego ale w przypadku tej strony dozwolone są tylko kody które zahardkodowaliśmy w metodzie:

 public static string PickZIP()
 {
 Random rand = new Random();
 var zipArray = new List<string>()
 {
 "06901",
 "06101",
 "06859",
 "03101",
 "02090",
 "02115",
 "01603",
 "01101"
 };
 return zipArray[rand.Next(8)];
 }

W której wylosujemy sobie po prostu jeden z 8 elementów tablicy dozwolonych ZIPów 🙂

Ostatnią rzeczą o której chciałbym Wam powiedzieć to robienie zrzutów ekranu. Często przydaje się w przypadku gdy chcemy uchwycić moment np. gdy wystąpi jakiś wyjątek. Tutaj wystarczy to zrobić tak:


_ieDriver.CaptureWebPageToFile(DateTime.Now.ToString("ddmmyyyyHHmmss") +".png");

Właściwie to zrobimy screen okna przeglądarki a nie całego pulpitu i zapiszemy go z aktualną datą w formacie png w głównym katalogu aplikacji.

Jedna rzecz która delikatnie mnie martwi to performance tego narzędzia. Szybkość działania Selenium była dużo lepsza. Ale może  coś za coś ?

Jeśli chciałbyś pobrać gotową solucje to znajduje się ona tutaj: http://1drv.ms/1jgh2s7



Więcej w Automatyzacja, C#, Testy automatyczne
Niech ktoś posprząta mój kod – PHP-CS-Fixer w akcji

Chcesz aby Twój kod był zgodny z obowiązującymi standardami ? Sprawdźmy w akcji, jak działa narzędzie, które próbuje automatycznie dopasować do...

Zamknij