Teste automate UI de întreținere

Cu câțiva ani în urmă, am fost foarte sceptic cu privire la testarea automată a UI și acest scepticism sa născut din câteva încercări eșuate. V-aș scrie niște teste automate UI pentru aplicații desktop sau web și câteva săptămâni mai târziu i-aș elimina din codul de bază, deoarece costul menținerii lor era prea mare. Așa că m-am gândit că testarea UI a fost greu și că, deși a oferit multă beneficii, cel mai bine ar fi să-l păstrați la minim și să testați cele mai complexe fluxuri de lucru dintr-un sistem prin testarea UI și să lăsați restul la teste unitare. Îmi amintesc spunând echipei mele despre piramida de testare a lui Mike Cohn și că într-un sistem tipic peste 70% dintre teste ar trebui să fie teste unitare, aproximativ 5% teste UI și teste de integrare restul.

Așa că m-am gândit că testarea UI a fost greu și că, deși a oferit o mulțime de beneficii, cel mai bine ar fi să păstreze la minimum ...

Am gresit! Sigur, testarea UI poate fi dificila. Este nevoie de un pic de timp pentru a scrie testele UI în mod corespunzător. Acestea sunt mult mai lente și mai fragile decât testele pe unități, deoarece acestea traversează limitele clasei și proceselor, ating browserul, implică elemente ale UI (de exemplu, HTML, JavaScript) care se schimbă în mod constant, lovesc baza de date, sistemul de fișiere și serviciile de rețea. Dacă oricare dintre aceste părți în mișcare nu se joacă frumos, aveți un test rupt; dar asta este și frumusețea testelor UI: testează sistemul dvs. de la sfârșitul la sfârșit. Nici un alt test nu vă oferă atât o acoperire cât și o acoperire completă. Testele UI automate, dacă se fac bine, ar putea fi cele mai bune elemente din suita dvs. de regresie.

Deci, în ultimele proiecte, testele mele UI s-au format peste 80% din testele mele! Ar trebui să menționez, de asemenea, că aceste proiecte au fost, în cea mai mare parte, aplicații CRUD cu logică foarte mică de afaceri și să ne confruntăm cu ea - marea majoritate a proiectelor software intră în această categorie. Logica de afaceri ar trebui să fie în continuare testată pe unitate; dar restul aplicației pot fi testate temeinic prin automatizarea UI.


Testarea UI a dispărut greșit

Aș dori să mă ating de ceea ce am greșit, ceea ce pare, de asemenea, să fie foarte tipic printre dezvoltatori și testere, începând cu automatizarea UI.

Deci, ce se întâmplă și de ce? Multe echipe încep automatizarea UI cu înregistratoare de ecran. Dacă faci automatizarea web cu Selenium, probabil că ai folosit Selenium IDE. De la pagina de pornire IDE Selenium:

Selenium-IDE (Mediul Integrat de Dezvoltare) este instrumentul pe care îl utilizați pentru a vă dezvolta cazurile de testare a seleniului.

Acesta este, de fapt, unul dintre motivele pentru care testarea UI se transformă într-o experiență oribilă: descărcați și aprindeți un înregistrator de ecran și navigați la site-ul dvs. Web și faceți clic pe, faceți clic pe, tastați, faceți clic pe, tastați, file, tastați, tastați, faceți clic și afirma. Apoi repetați înregistrarea și funcționează. Dulce!! Deci, exportați acțiunile ca un script de testare, puneți-l în codul dvs., împachetați-l într-un test și executați testul și vedeți browserul să vină în viață înainte de ochii dvs. și testele dvs. funcționează foarte ușor. Ești foarte entuziasmat, împărtăși constatările tale cu colegii tăi și le arăți șefului tău și ei sunt foarte entuziasmați și merg: "Automatizați toate lucrurile"

O săptămână mai târziu și aveți 10 teste automate UI și totul pare grozav. Apoi afacerea vă cere să înlocuiți numele de utilizator cu adresa de e-mail, deoarece a provocat o anumită confuzie în rândul utilizatorilor, așa că faceți acest lucru. Apoi, ca orice alt programator excelent, rulați suita dvs. de test UI, doar pentru a găsi 90% din testele dvs. sunt rupte, deoarece pentru fiecare test se înregistrează utilizatorul cu numele de utilizator și numele câmpului sa schimbat și vă durează două ore pentru a înlocui toate referințele la nume de utilizator în testele dvs. cu e-mail și pentru a obține din nou testele verzi. Același lucru se întâmplă mereu și la un moment dat vă aflați petrecând ore pe zi pentru a stabili testele rupte: teste care nu s-au rupt deoarece ceva a mers prost în codul dvs.; ci pentru că ați schimbat un nume de domeniu în baza de date / model sau ați restructurat ușor pagina dvs. Câteva săptămâni mai târziu, vă opriți să efectuați testele dvs. din cauza acestui cost uriaș de întreținere și ați ajuns la concluzia că testarea UI este eșuată.

NU trebuie să folosiți Selenium IDE sau orice alt dispozitiv de înregistrare a ecranului pentru a vă dezvolta cazurile de testare. Acestea fiind spuse, nu este chiar recorder-ul de ecran care duce la o suită de test friabil; este codul pe care îl generează și care are probleme de mentenabilitate inerente. Mulți dezvoltatori încă se termină cu o suită de testare UI fragilă, chiar fără a utiliza înregistratoare de ecran doar pentru că testele lor prezintă aceleași atribute.

Toate testele din acest articol sunt scrise pe site-ul MVC Music Store. Site-ul web, așa cum se întâmplă, are unele probleme care fac testul UI destul de greu, așa că am portat codul și am rezolvat problemele. Puteți găsi codul actual pe care scriu aceste teste impotriva replicilor GitHub pentru acest articol aici

Deci, cum arată un test friabil? Arata cam asa:

clasa BrittleTest [Test] public void Can_buy_an_Album_when_registered () var driver = Host.Instance.Application.Browser; . Driver.Navigate () GoToUrl (driver.Url); . Driver.FindElement (By.LinkText ( "Admin")) Faceți clic pe (); . Driver.FindElement (By.LinkText ( "Înregistrare")) Faceți clic pe (); driver.FindElement (By.Id ( "UserName")) Clear ().; driver.FindElement (By.Id ( "UserName")) SendKeys ( "HJSimpson"). driver.FindElement (By.Id ( "Parola")) Clear ().; driver.FindElement (By.Id ( "Parola")) SendKeys ( "2345Qwert!").; driver.FindElement (By.Id ( "ConfirmPassword")) Clear ().; driver.FindElement (By.Id ( "ConfirmPassword")) SendKeys ( "2345Qwert!").; driver.FindElement (By.CssSelector ( "input [type = \" submit \ "]")) Faceți clic pe ().; . Driver.FindElement (By.LinkText ( "Disco")) Faceți clic pe (); driver.FindElement (By.CssSelector ("img [alt = \" Le Freak \ "]")) Faceți clic pe (); driver.FindElement (By.LinkText ("Adăugare în coș")). Faceți clic pe (); driver.FindElement (By.LinkText ("Checkout >>")) Faceți clic pe (); driver.FindElement (By.Id ( "FirstName")) Clear ().; driver.FindElement (By.Id ( "FirstName")) SendKeys ( "Homer".); driver.FindElement (By.Id ( "LastName")) Clear ().; driver.FindElement (By.Id ( "LastName")) SendKeys ( "Simpson".); driver.FindElement (By.Id ( "Address")) Clear ().; driver.FindElement (By.Id ("Adresa")) SendKeys ("Terasa Evergreen 742"); driver.FindElement (By.Id ( "Orașul")) Clear ().; driver.FindElement (By.Id ( "Orașul")) SendKeys ( "Springfield").; driver.FindElement (By.Id ( "stat")) Clear ().; driver.FindElement (By.Id ( "stat")) SendKeys ( "Kentucky").; driver.FindElement (By.Id ( "CodPoștal")) Clear ().; driver.FindElement (By.Id ( "CodPoștal")) SendKeys ( "123456".); driver.FindElement (By.Id ( "Țară")) Clear ().; driver.FindElement (By.Id ("Țară")) SendKeys ("Statele Unite"); driver.FindElement (By.Id ( "Telefon")) Clear ().; driver.FindElement (By.Id ( "Phone")) SendKeys ( "2341231241").; driver.FindElement (By.Id ( "E-mail")) Clear ().; driver.FindElement (By.Id ( "E-mail")) SendKeys ( "[email protected]").; driver.FindElement (By.Id ( "Promocodul")) Clear ().; driver.FindElement (By.Id ( "Promocodul")) SendKeys ( "FREE").; driver.FindElement (By.CssSelector ( "input [type = \" submit \ "]")) Faceți clic pe ().; Assert.IsTrue (driver.PageSource.Contains ("Finalizare completă")); 

Puteți găsi BrittleTest clasa aici.

Gazdă este o clasă statică, cu o singură proprietate statică: Instanță, care, la instanțiere, declanșează IIS Express pe site-ul testat și leagă Firefox WebDriver de instanța browserului. Când testul este terminat, acesta închide automat browserul și expresia IIS Express.

Acest test lansează un browser web, merge pe pagina de pornire a site-ului Mvc Music Store, înregistrează un nou utilizator, navighează către un album, îl adaugă în cărucior și verifică.

S-ar putea argumenta că acest test face prea mult și de aceea este fragil; dar mărimea acestui test nu este motivul pentru care este fragil - așa este scris că face ca acesta să fie un coșmar pentru a menține.

Există diferite școli de gândire cu privire la testarea UI și cât de mult ar trebui să fie acoperit fiecare test. Unii cred că acest test face prea mult și unii cred că un test ar trebui să acopere un scenariu real, să se sfârșească și să considere acest lucru un test perfect (mentenabilitatea deoparte).

Deci, ce este în neregulă cu acest test?

  • Acesta este codul procedural. Una dintre problemele principale ale acestui stil de codificare este lizibilitatea sau lipsa acestuia. Dacă doriți să schimbați testul sau dacă acesta se rupe deoarece una dintre paginile implicate sa schimbat, veți avea dificultăți în a afla ce să modificați și să trasați o linie între secțiunile de funcționalitate; pentru că este vorba despre o grămadă mare de coduri în care obținem "șoferul" pentru a găsi un element pe pagină și pentru a face ceva cu el. Nicio modularitate.
  • Acest test unic ar putea să nu aibă prea multă duplicare, dar alte câteva teste ca acesta și veți avea o mulțime de selector și logică dublă pentru a interacționa cu paginile web din diferite teste. De exemplu By.Id ( "NumeUtilizator") selectorul va fi duplicat în toate testele care necesită înregistrare și driver.FindElement (By.Id ( "UserName")). Clear () și driver.FindElement (By.Id ( "UserName")). SendKeys (“„) sunt duplicate oriunde doriți să interacționați cu caseta de text Nume utilizator. Apoi, există întreaga formă de înregistrare, formular de checkout etc. care va fi repetată în toate testele care trebuie să interacționeze cu ele! Codul dublu duce la coșmaruri de întreținere.
  • Există o mulțime de șiruri magice peste tot, care din nou reprezintă o problemă de mentenabilitate.

Codul de test este codul!

Există, de asemenea, modele care vă permit să scrieți mai multe teste UI întreținute.

La fel ca codul dvs. real, va trebui să vă mențineți testele. Deci, dați-le același tratament.

Ce este testele care ne fac să credem că putem renunța la calitate în ele? În caz contrar, în opinia mea, o suită de testare rău este mult mai greu de menținut decât codul rău. Am avut bucăți de cod de lucru în producție de ani de zile care nu s-au rupt niciodată și nu am avut niciodată nevoie să le ating. Sigur a fost urât și greu de citit și de întreținut, dar a funcționat și nu avea nevoie de schimbare, astfel încât costul reale de întreținere era zero. Situația nu este însă aceeași pentru testele rele: totuși, pentru că testele rele se vor sfărâma, iar fixarea lor va fi dificilă. Nu pot număra de câte ori am văzut dezvoltatorii să evite testarea, deoarece ei cred că scrierea testelor este o mare pierdere de timp, deoarece durează prea mult timp pentru a menține.

Codul de test este cod: aplicați SRP pe codul dvs.? Apoi trebuie să-l aplicați și pe testele dvs. Este codul dvs. uscat? Apoi uscati-va si testele. Dacă nu scrieți teste bune (UI sau altfel), veți pierde mult timp menținându-le.

Există, de asemenea, modele care vă permit să scrieți mai multe teste UI întreținute. Aceste modele sunt agnostic pe platformă: am folosit aceleași idei și modele similare pentru a scrie teste UI pentru aplicațiile WPF și aplicațiile web scrise în ASP.Net și Ruby on Rails. Deci, indiferent de stiva dvs. de tehnologie, ar trebui să puteți face testele UI mult mai ușor de întreținut urmând câțiva pași simpli.

Introducerea modelului de obiect al paginii

Multe dintre problemele menționate mai sus au rădăcini în natura procedurală a scenariului de testare și soluția este ușoară: orientarea obiectului.

Obiectul paginii este un model utilizat pentru a aplica orientarea obiectului la testele UI. Din wiki-ul Selenium:

În UI din aplicația dvs. web există zone cu care interacționează testele dvs. Un obiect de pagină simplifică pur și simplu aceste obiecte în cadrul codului de testare. Aceasta reduce cantitatea de cod duplicat și înseamnă că, dacă UI se modifică, fixarea trebuie aplicată numai într-un singur loc.

Ideea este că pentru fiecare pagină din aplicația / site-ul dvs. doriți să creați un obiect de pagină. Obiectele de pagină sunt, în principiu, echivalente cu automatizarea UI a paginilor dvs. Web.

Am continuat și am refacut logica și interacțiunile din programul BrittleTest în câteva obiecte de pagină și am creat un nou test care le folosește în loc să lovească driverul web direct. Puteți găsi noul test aici. Codul este copiat aici pentru referință:

public clasă TestWithPageObject [Test] public void Can_buy_an_Album_when_registered () var registerPage = HomePage.Initiate () .GoToAdminForAnonymousUser () .GoToRegisterPage (); registerPage.Username = "HJSimpson"; registerPage.Email = "[email protected]"; registerPage.Password = "! 2345Qwert"; registerPage.ConfirmPassword = "! 2345Qwert"; var TransportPage = RegisterPage .SubmitRegistration () .SelectGenreByName ("Disco") .SelectAlbumByName ("Le Freak") .AddToCart () .Checkout (); ShippingPage.FirstName = "Homer"; ShippingPage.LastName = "Simpson"; ShippingPage.Address = "742 Terasa Evergreen"; ShippingPage.City = "Springfield"; ShippingPage.State = "Kentucky"; ShippingPage.PostalCode = "123456"; ShippingPage.Country = "Statele Unite"; ShippingPage.Phone = "2341231241"; ShippingPage.Email = "[email protected]"; ShippingPage.PromoCode = "GRATUIT"; var comandPage = expedițiePage.SubmitOrder (); Assert.AreEqual (OrderPage.Title, "Checkout Complete"); 

Desigur, corpul de testare nu a scăzut mult în dimensiune și, de fapt, a trebuit să creez șapte clase noi pentru a susține acest test. În ciuda mai multor linii de cod necesare, tocmai am fixat o mulțime de probleme pe care le-a avut testul original fragil (mai multe despre acest lucru mai jos). Pentru moment, să ne aruncăm mai adânc în modelul obiectului de pagină și în ceea ce am făcut aici.

Cu modelul de obiect Pagină, de obicei, creați o clasă de obiect de pagină pe pagina web în care se află testul, unde modelele de clasă încorporează interacțiunile cu pagina. Deci, o casetă text din pagina dvs. web devine o proprietate șir pe Obiectul paginii și pentru a umple căsuța de text pe care tocmai ați setat proprietatea textului la valoarea dorită, în loc de:

driver.FindElement (By.Id ( "E-mail")) Clear ().; driver.FindElement (By.Id ( "E-mail")) SendKeys ( "[email protected]").;

putem scrie:

registerPage.Email = "[email protected]";

Unde registerPage este o instanță a clasei RegisterPage. O casetă de selectare a paginii devine o proprietate bool pe Obiectul paginii și bifând și deblocând caseta de selectare este doar o chestiune de setare a proprietății booleene la adevărată sau falsă. De asemenea, o legătură pe pagina web devine o metodă pe Obiectul paginii și făcând clic pe link se transformă în apelarea metodei pe Obiectul paginii. Deci, în loc de:

. Driver.FindElement (By.LinkText ( "Admin")) Faceți clic pe ();

putem scrie:

homepage.GoToAdminForAnonymousUser ();

De fapt, orice acțiune de pe pagina noastră web devine o metodă în obiectul paginii noastre și, ca răspuns la luarea acelei acțiuni (de exemplu, apelarea metodei pe obiectul de pagină), veți obține o instanță a unui alt obiect de pagină înapoi care indică la pagina web pe care tocmai ați navigați prin luarea acțiunii (de ex. trimiterea unui formular sau dând clic pe un link). În acest fel, puteți să vă lanțați cu ușurință interacțiunile de vizualizare în scriptul dvs. de testare:

var TransportPage = RegisterPage .SubmitRegistration () .SelectGenreByName ("Disco") .SelectAlbumByName ("Le Freak") .AddToCart () .Checkout ();

Aici, după înregistrarea utilizatorului am ajuns la pagina de pornire (o instanță a obiectului de pagină este returnată de către SubmitRegistration metodă). Deci, în cazul HomePage, sună SelectGenreByName care face clic pe un link "Disco" de pe pagina care returnează o instanță a AlbumBrowsePage și apoi pe acea pagină pe care o sun SelectAlbumByName care face clic pe albumul "Le Freak" și returnează o instanță a AlbumDetailsPage și așa mai departe și așa mai departe.

Recunosc: este o mulțime de clase pentru ceea ce nu era deloc clasă; dar am câștigat multe beneficii din această practică. În primul rând, codul nu mai este procedural. Avem un model bine testat, în care fiecare obiect furnizează încapsularea plăcută a interacțiunii cu o pagină. De exemplu, dacă se schimbă ceva în logica dvs. de înregistrare, singurul loc pe care trebuie să-l modificați este clasa RegisterPage, în loc să trebuiască să treceți prin întreaga suită de test și să schimbați fiecare interacțiune cu vizualizarea de înregistrare. Această modularitate oferă, de asemenea, o reutilizare reușită: vă puteți reutiliza ShoppingCartPage oriunde aveți nevoie pentru a interacționa cu coșul de cumpărături. Deci, într-o practică simplă de trecere de la codul de testare orientat la obiect, am eliminat aproape trei dintre cele patru probleme cu testul inițial fragil, care au fost codul procedural și logica și duplicarea selectorului. Mai avem un pic de duplicare, dar pe care o vom rezolva în curând.

Cum am implementat de fapt aceste obiecte de pagină? Un obiect de pagină în rădăcina acestuia nu este altceva decât un înveliș în jurul interacțiunilor pe care le aveți cu pagina. Aici am extras interfețele UI ale testelor noastre fragile și le-am pus în propriile lor obiecte de pagină. De exemplu, logica de înregistrare a fost extrasă în propria sa clasă numită RegisterPage care arăta astfel:

clasa publica RegisterPage: Pagina public HomePage SubmitRegistration () return NavigateTo(By.CssSelector ( "input [type = 'submit']"));  public string Nume de utilizator set Execute (By.Name ("UserName"), e => e.Clear (); e.SendKeys (valoare););  șirul public Email set Execute (By.Name ("Email"), e => e.Clear (); e.SendKeys (valoare););  șirul public ConfirmPassword set Execute (By.Name ("ConfirmPassword"), e => e.Clear (); e.SendKeys (valoare););  șirul public al parolei set Execute (By.Name ("Password"), e => e.Clear (); e.SendKeys (value);); 

Am creat o Pagină superclaza care are grijă de câteva lucruri, cum ar fi Navigheaza catre care vă ajută să navigați la o nouă pagină, luând o acțiune și A executa care execută anumite acțiuni asupra unui element. Pagină clasa arata ca:

public class Page protejate RemoteWebDriver WebDriver get returnați Host.Instance.WebDriver;  șir public Titlu get return WebDriver.Title;  publice TPage NavigateTo(By by) unde TPage: Page, new () WebDriver.FindElement (de) .Click (); întoarceți Activator.CreateInstance();  void publice Execute (By by, Action acțiune) var element = WebDriver.FindElement (de); acțiune (elementul); 

În BrittleTest, să interacționăm cu un element pe care l-am făcut FindElement o dată pe acțiune. A executa , în afară de interacțiunea cu driverul web, are un beneficiu suplimentar care permite selectarea unui element, care ar putea fi o acțiune costisitoare, o dată și luând mai multe acțiuni pe el:

driver.FindElement (By.Id ( "Parola")) Clear ().; driver.FindElement (By.Id ( "Parola")) SendKeys ( "2345Qwert!").;

a fost înlocuit cu:

Executați (By.Name ("Parola"), e => e.Clear (); e.SendKeys ("! 2345Qwert");)

Luând oa doua privire la RegisterPage un obiect de pagini de mai sus avem încă un pic de duplicare acolo. Codul de test este cod și nu vrem duplicarea în codul nostru; așa că haideți să refacem asta. Putem extrage codul necesar pentru a completa o casetă de text într - o metodă pe Pagină clasa și apelați doar de la obiectele paginii. Metoda ar putea fi implementată ca:

public void SetText (string elementName, string newText) Execute (By.Name (elementName), e => e.Clear (); e.SendKeys (newText);); 

Și acum proprietatile pe RegisterPage poate fi redus la:

șirul public de utilizator set SetText ("UserName", valoare); 

Puteți, de asemenea, să creați un API fluent pentru a face cititorul să citească mai bine (de ex. Se umple ( "UserName"). Cu (valoare)), dar vă voi lăsa asta.

Nu facem nimic extraordinar aici. Doar reparații simple pe codul nostru de testare, așa cum am făcut întotdeauna pentru codul nostru "errrr", altul!!

Puteți vedea codul complet pentru Pagină și RegisterPage cursuri aici și aici.

Obiect cu pagini puternic tastat

Am rezolvat problemele procedurale cu testul fragil, care a făcut testul mai ușor de citit, modular, DRYer și eficient menținut. Există o ultimă problemă pe care nu am rezolvat-o: există peste tot o mulțime de șiruri magice. Nu este chiar un coșmar, dar este totuși o problemă pe care o putem rezolva. Introduceți Obiecte de pagină puternic introduse!

Această abordare este practică dacă utilizați un cadru MV * pentru interfața dvs. utilizator. În cazul nostru, folosim MVC ASP.Net.

Să mai luăm o privire la RegisterPage:

clasa publica RegisterPage: Pagina public HomePage SubmitRegistration () return NavigateTo(By.CssSelector ( "input [type = 'submit']"));  șir public Numele de utilizator set SetText ("UserName", valoare);  șir public E-mail set SetText ("Email", valoare);  șirul public ConfirmPassword set SetText ("ConfirmPassword", valoare);  șirul public de parole set SetText ("Password", valoare); 

Această pagină modelează vizualizarea Înregistrează în aplicația noastră web (copiați doar partea de sus aici pentru confortul dvs.):

@model MvcMusicStore.Models.RegisterModel @ ViewBag.Title = "Înregistrare"; 

Hmmm, ce e asta? RegisterModel Acolo? Este Modelul de vizualizare pentru pagină: M în MVC. Iată codul (am eliminat atributele pentru a reduce zgomotul):

clasa publică RegisterModel public șir UserName get; a stabilit;  șir public Email get; a stabilit;  șir public parolă get; a stabilit;  șirul public ConfirmPassword get; a stabilit; 

Arată foarte familiar, nu-i așa? Are aceleași proprietăți ca și RegisterPage clasă care nu este surprinzătoare în considerare RegisterPage a fost creat pe baza acestui model de vizualizare și vizualizare. Să vedem dacă putem profita de modelele de vizualizare pentru a simplifica obiectele paginii noastre.

Am creat un nou Pagină superclasei; ci una generică. Puteți vedea codul aici:

clasa publica : Pagina unde TViewModel: class, new () public void FillWith (TViewModel viewModel, IDictionary> propertyTypeHandling = null) // eliminat pentru brevity

Pagină clasa de subclase vechi Pagină clasa și oferă toate funcționalitățile sale; dar are, de asemenea, o metodă suplimentară numită Umple cu care completează pagina cu exemplul modelului de vizualizare furnizat! Deci, acum RegisterPage clasa arata ca:

clasa publica public HomePage CreateValidUser (modelul ÎnregistrareModel) FillWith (model); reveniți la NavigateTo(By.CssSelector ( "input [type = 'submit']")); 

Am duplicat toate obiectele de pagină pentru a afișa ambele variante și, de asemenea, pentru a face codul de bază mai ușor de urmărit pentru tine; dar în realitate veți avea nevoie de o clasă pentru fiecare obiect de pagină.

După ce mi-am transformat obiectele de pagină pe cele generice, testul arată acum:

public clasă StronglyTypedPageObjectWithComponent [Test] public void Can_buy_an_Album_when_registered () var orderedPage = HomePage.Initiate () .GoToAdminForAnonymousUser () .GoToRegisterPage () .CreateValidUser (ObjectMother.CreateRegisterModel ()) .SelectGenreByName ("Disco") .SelectAlbumByName Freak ") .AddAlbumToCart () .Checkout () .SubmitShippingInfo (ObjectMother.CreateShippingInfo ()," Free "); Assert.AreEqual ("Completare completă", ordinePage.Titlu); 

Asta e tot testul! Mult mai ușor de citit, uscat și menținut, nu-i așa??

ObjectMother clasa pe care o folosesc în test este o mamă Obiect care oferă date de testare (codul poate fi găsit aici), nimic fantezist:

clasa publica ObjectMother public static Order CreateShippingInfo () var shippingInfo = comanda nou FirstName = "Homer", LastName = "Simpson", Adresa = "742 Evergreen Terrace", City = "Springfield" = "123456", Țara = "Statele Unite", Telefon = "2341231241", Email = "[email protected]"; return shippingInfo;  statică publică RegisterModel CreateRegisterModel () var model = nou ÎnregistrareModel UserName = "HJSimpson", Email = "[email protected]", Password = "! 2345Qwert", ConfirmPassword = "! 2345Qwert"; model de returnare; 

Nu vă opriți la Obiectul paginii

Unele pagini web sunt foarte mari și complexe. Anterior am spus că codul de testare este cod și ar trebui să îl tratăm ca atare. În mod normal, ruperea paginilor web mari și complexe în componente mai mici și, în unele cazuri, reutilizabile (parțiale). Acest lucru ne permite să compunem o pagină web de la componente mai mici, mai ușor de gestionat. Ar trebui să facem același lucru și pentru testele noastre. Pentru a face acest lucru, putem folosi Componentele paginii.

Componenta unei pagini este destul de asemănătoare cu un obiect de pagină: este o clasă care încapsulează interacțiunea cu unele elemente dintr-o pagină. Diferența este că interacționează cu o mică parte a unei pagini web: modelează un control al utilizatorului sau o vizualizare parțială, dacă doriți. Un exemplu bun pentru o componentă a paginii este o bară de meniu. O bară de meniu apare de obicei pe toate paginile unei aplicații web. Nu doriți să continuați să repetați codul necesar pentru a interacționa cu meniul în fiecare obiect de pagină. În schimb, puteți să creați o componentă a paginii de meniu și să o utilizați din obiectele paginii. De asemenea, puteți utiliza componentele paginii pentru a vă ocupa de grilele de date de pe paginile dvs. și pentru ao face un pas mai departe, componenta paginii de rețea în sine ar putea fi compusă din componente ale paginii de rânduri. În cazul magazinului Mvc Music Store am putea avea un a TopMenuComponent și a SideMenuComponent și să le folosim de la noi Pagina principala.

Ca și în aplicația dvs. web, puteți crea, de asemenea, un exemplu, LayoutPage un obiect de pagină care vă modelează pagina de layout / master și o folosește ca o superclasă pentru toate celelalte obiecte de pagină. Pagina de layout va fi apoi compusă din componente ale paginii de meniu, astfel încât toate paginile să poată atinge meniurile. Cred că o regulă bună ar fi să aveți o componentă de pagină pe vizualizare parțială, un obiect de pagină de aspect pentru fiecare aspect și un obiect de pagină pe pagină web. În acest fel știți că codul dvs. de testare este granularar și bine compilat ca și codul dvs..

Unele cadre pentru testarea UI

Ceea ce am arătat mai sus a fost un eșantion foarte simplu și controversat, cu câteva clase de sprijin ca infrastructură pentru teste. În realitate, cerințele pentru testarea UI sunt mult mai complexe: există controale complexe și interacțiuni, trebuie să scrieți și să citiți de pe paginile dvs., trebuie să vă ocupați de latențele de rețea și să aveți control asupra AJAX și a altor interacțiuni Javascript, trebuie să trageți de pe diferite browsere și așa mai departe, pe care nu le-am explicat în acest articol. Deși este posibil să codificați în jurul tuturor acestor lucruri, utilizarea unor cadre ar putea să vă economisească mult timp. Iată cadrele pe care le recomand:

Cadru pentru .Net:

  • Seleno este un proiect open source de la TestStack, care vă ajută să scrieți teste automate cu UI cu Selenium. Se concentrează pe utilizarea obiectelor de pagină și a componentelor paginii și prin citirea și scrierea pe pagini web utilizând modele de vizualizare puternic tipizate. Dacă ți-a plăcut ce am făcut în acest articol, atunci îți va plăcea și Seleno, deoarece cea mai mare parte a codului prezentat aici a fost împrumutat de la codul de bază Seleno.
  • White este un framework open source de la TestStack pentru automatizarea aplicațiilor client bogate bazate pe platformele Win32, WinForms, WPF, Silverlight și SWT (Java).

Dezvăluire: Sunt co-fondator și membru al echipei de dezvoltare a organizației TestStack.

Cadru pentru Ruby:

  • Capybara este un cadru de test de acceptare pentru aplicațiile web care vă ajută să testați aplicațiile web prin simularea modului în care un utilizator real ar interacționa cu aplicația dvs..
  • Poltergeist este un șofer pentru Capybara. Vă permite să rulați testele Capybara pe un browser WebKit fără cap, furnizat de PhantomJS.
  • un obiect de pagină (nu am folosit personal această bijuterie) este o bijuterie simplă care ajută la crearea de obiecte flexibile de pagină pentru testarea aplicațiilor bazate pe browser. Scopul este de a facilita crearea de straturi de abstractizare în testele dvs. pentru a decupla testele de la elementul pe care îl testează și pentru a oferi o interfață simplă elementelor de pe o pagină. Funcționează atât cu watir-webdriver cât și cu seleniul-webdriver.

Concluzie

Am început cu o experiență tipică de automatizare a UI, am explicat de ce testele UI nu reușesc, au oferit un exemplu de testare fragilă și au discutat problemele sale și le-au rezolvat folosind câteva idei și modele.

Dacă doriți să luați un punct din acest articol ar trebui să fie: Codul de test este codul. Dacă vă gândiți la asta, tot ce am făcut în acest articol a fost să aplicați bunele practici de codificare și orientare obiect pe care le cunoașteți deja unui test UI.

Mai sunt încă multe de învățat despre testarea UI și voi încerca să discut unele dintre sfaturile mai avansate într-un articol viitor.

Testarea fericită!

Cod