Obiecte de Ruby Page pentru cunoscătorii Capybara

Ce veți crea

Ce sunt obiectele de pagină?

Îți dau mai întâi pasul scurt. Este un model de design care încapsulează marcajul și interacțiunile paginii - în special pentru a vă refactor specificațiile de caracteristică. Este o combinație a două tehnici de refactorizare foarte frecvente: Clasa de extragere și Metoda de extragere-care nu trebuie să se întâmple în același timp deoarece puteți construi treptat până la extracția unei clase întregi prin intermediul unui nou obiect de pagină.

Această tehnică vă permite să scrieți specificații la nivel înalt ale caracteristicilor care sunt foarte expresive și uscate. Într-un fel, sunt teste de acceptare cu limbajul aplicației. S-ar putea să întrebați, nu sunt specificații scrise cu Capybara deja la nivel înalt și expresive? Sigur, pentru dezvoltatorii care scriu coduri zilnic, specificațiile Capybara citesc foarte bine. Sunt uscate din cutie? Nu într-adevăr - cu siguranță nu!

"caracterul rubinic" M creează o misiune "face scenariul" cu succes "face sign_in_as" [email protected] "

vizitați misi_path click_on 'Creare misiune' fill_in 'Name Name', cu: 'Project Moonraker' click_button 'Submit' așteptați (page) .to have_css 'li.mission-name', text:

"caracteristica rubinică" M marchează misiunea ca completă "face scenariul" cu succes "face sign_in_as" [email protected] "

vizitați misi_path click_on 'Creare misiune' fill_in 'Mission Name', cu: 'Octopussy' click_button 'Trimite' în 'li: conține (' Octopussy ') "click_on' Mission completed ' misiunile li.mission-name.completed ', text:' Octopussy 'end end "

Când vă uitați la aceste exemple de specificații ale funcțiilor, unde vedeți oportunități pentru a face acest lucru mai bine citit și cum ați putea extrage informații pentru a evita suprapunerea? De asemenea, este suficient acest nivel suficient pentru a modela ușor povestirile utilizatorilor și pentru a înțelege părțile interesate non-tehnice?

In mintea mea, exista cateva modalitati de a imbunatati acest lucru si de a face pe toata lumea fericiti-dezvoltatori care pot evita fiddling cu detalii de interactiune cu DOM in timp ce se aplica OOP, si alti membri non-coding echipa care au probleme cu jumping intre povestile utilizatorilor și aceste teste. Ultimul punct este frumos de a avea, desigur, dar cele mai importante beneficii provin în special din a face ca specificațiile dvs. DOM-interactiv să fie mai robuste.

Encapsularea este conceptul cheie cu obiectele de pagină. Când scrieți specificațiile dvs. de caracteristică, veți beneficia de o strategie de extragere a comportamentului care conduce un flux de testare. Pentru codul de calitate, doriți să capturați interacțiunile cu anumite seturi de elemente pe paginile dvs. - mai ales dacă vă împiedicați să repetați modele. Pe măsură ce cererea dvs. crește, doriți / aveți nevoie de o abordare care să evite răspândirea acestei logici peste toate specificațiile.

"Ei bine, nu-i supărat? Capybara citește bine ", spuneți voi?

Întrebați-vă: De ce nu aveați toate detaliile implementării HTML într-un singur loc, în timp ce aveți teste mai stabile? De ce nu ar trebui testele de interacțiune UI să aibă aceeași calitate ca testele pentru codul aplicației? Chiar vrei să te oprești acolo?

Datorită schimbărilor de zi cu zi, codul Capybara este vulnerabil când este răspândit peste tot - introduce posibile puncte de întrerupere. Să presupunem că un designer vrea să schimbe textul pe un buton. Nu e mare, nu? Dar vrei sa te adaptezi la acea schimbare intr-un pachet central pentru acel element din specificatiile tale, sau preferi sa faci asta peste tot? M-am gândit eu!

Există multe refactorizări posibile pentru specificațiile dvs. de caracteristică, dar obiectele Page oferă cele mai curate abstracții pentru încapsularea comportamentului orientat spre utilizator pentru pagini sau pentru fluxuri mai complexe. Nu trebuie să simulați toată pagina (ele), totuși, concentrați-vă asupra biților esențiali necesari fluxurilor de utilizatori. Nu este nevoie să exagerați!

Teste de acceptare / caracteristici de caracteristici

Înainte de a trece la inima problemei, aș dori să fac un pas înapoi pentru oameni noi pentru întreaga activitate de testare și clarifica unele dintre limbile care sunt importante în acest context. Oamenii mai familiarizați cu TDD nu vor pierde prea mult dacă vor sări peste.

Despre ce vorbim aici? Acceptarea testelor vine, de obicei, într-o etapă ulterioară a proiectelor, pentru a evalua dacă ați construit ceva de valoare pentru utilizatorii dvs., proprietarul produsului sau alte părți interesate. Aceste teste sunt de obicei rulate de clienți sau de utilizatorii dvs. Este un fel de verificare dacă cerințele sunt îndeplinite sau nu. Există ceva asemănător unei piramide pentru tot felul de straturi de testare, iar testele de acceptare sunt aproape de partea superioară. Deoarece acest proces include deseori oameni non-tehnici, un limbaj de nivel înalt pentru scrierea acestor teste este un bun valoros pentru comunicarea înainte și înapoi.

Elementele de specificație, pe de altă parte, sunt puțin mai mici în lanțul alimentar de testare. Mai multe teste de nivel înalt decât unitatea, care se concentrează pe detaliile tehnice și logica de afaceri a modelelor dvs., prezintă specificații care descriu fluxurile pe și între paginile dvs..

Unelte precum Capybara vă ajută să evitați să faceți acest lucru manual, ceea ce înseamnă că rareori trebuie să deschideți browserul pentru a testa manual lucrurile. Cu aceste tipuri de teste, ne place să automatizăm aceste sarcini pe cât posibil și să testăm interacțiunea prin browser în timp ce scriem afirmații împotriva paginilor. Apropo, nu o folosiți obține, a pune, post sau șterge așa cum faceți cu specificațiile de solicitare.

Specificațiile specificațiilor sunt foarte asemănătoare cu testele de acceptare - câteodată cred că diferențele sunt prea neclare pentru a îngriji cu adevărat terminologia. Scrieți teste care vă exercită întreaga aplicație, care implică adesea un flux multiplu de acțiuni ale utilizatorilor. Aceste teste de interacțiune arată dacă componentele dvs. lucrează în armonie atunci când sunt reunite.

În terenul Ruby, ele sunt protagonistul principal atunci când avem de-a face cu Obiecte de pagină. Elementele de specificații ale elementelor sunt deja foarte expresive, dar pot fi optimizate și curățate prin extragerea datelor, comportamentului și marcării într-o clasă sau clase separate.

Sper că eliminarea acestei terminologii neclară vă va ajuta să vedeți că având obiecte Page este un lucru asemănător cu efectuarea testelor de acceptare la scrierea caracteristicilor caracteristicilor.

Capibara

Poate că ar trebui să mergem și peste asta foarte repede. Această bibliotecă se descrie ca un "cadru de test de acceptare pentru aplicațiile web". Puteți simula interacțiunile utilizatorilor cu paginile dvs. printr-un limbaj foarte puternic și convenabil pentru anumite domenii. În opinia mea, RSpec asociat cu Capybara oferă cea mai bună modalitate de a scrie caracteristicile dvs. de caracteristică în acest moment. Vă permite să vizitați pagini, să completați formulare, să faceți clic pe link-uri și butoane și să căutați marcaj pe paginile dvs. și puteți combina cu ușurință toate tipurile de aceste comenzi pentru a interacționa cu paginile dvs. prin testele dvs..

În principiu, puteți evita deschiderea browserului pentru a testa manual aceste lucruri în majoritatea timpului - ceea ce nu este doar mai puțin elegant, ci și mult mai consumator de timp și predispus la erori. Fără acest instrument, procesul de "exterioară în testare" - vă conduceți codul de la teste la nivel înalt până la testele pe unitate - ar fi mult mai dureros și, prin urmare, posibil mai neglijat.

Cu alte cuvinte, începeți să scrieți aceste teste de caracteristici care se bazează pe povestirile dvs. de utilizator și de acolo mergeți în jos până când testele unității vă oferă acoperirea specificațiilor de caracteristică necesare. După aceasta, când testele dvs. sunt verde, desigur, jocul începe din nou și reveniți pentru a continua cu un nou test de caracteristică.

Cum?

Să aruncăm o privire asupra a două exemple simple de specificații ale caracteristicilor care permit M să creeze misiuni clasificate care pot fi apoi finalizate.

În marcaj, aveți o listă de misiuni, iar finalizarea cu succes creează o clasă suplimentară terminat pe Li a misiunii respective. Obișnuite, nu? Ca o primă abordare, am început cu mici refactorizări foarte comune, care extrag comportamentul comun în metode.

spec / caracteristici / m_creates_a_mission_spec.rb

"ruby necesită 'rails_helper'

caracteristică "M creează misiune" face scenariu "cu succes" face sign_in_as "[email protected]"

create_classified_mission_named "Project Moonraker" agent_sees_mission "End of Project Moonraker" 

def create_classified_mission_named (mission_name) vizitați missions_path click_on 'Creare misiune' fill_in 'Mission Name', cu: mission_name click_button 'Trimite' sfârșit

def agent_sees_mission (misiune_name) asteptati (pagina) .to have_css 'li.mission-name', text: end_name

def sign_in_as (e-mail) vizitați root_path fill_in 'E-mail', cu: email click_button 'Submit' end end "

spec / caracteristici / agent_completes_a_mission_spec.rb

"ruby necesită 'rails_helper'

caracteristica "M marchează misiunea ca completă" face scenariul "cu succes" face sign_in_as "[email protected]"

create_classified_mission_named 'Project Moonraker' mark_mission_as_complete 'Proiectul Moonraker' agent_sees_completed_mission 'Sfârșitul proiectului Moonraker' 

def create_classified_mission_named (mission_name) vizitați missions_path click_on 'Creare misiune' fill_in 'Mission Name', cu: mission_name click_button 'Trimite' sfârșit

def mark_mission_as_complete (nume_plicație) în cadrul "li: conține ('# mission_name')" faceți clic pe 'Finalizarea misiunii' sfârșitul final 

def agent_sees_completed_mission (misiune_name) așteptați (pagina) .to have_css 'ul.missions li.mission-name.completed', text: mission_name end

def sign_in_as (e-mail) vizitați root_path fill_in 'E-mail', cu: email click_button 'Submit' end end "

Deși există și alte modalități, desigur, de a trata chestii precum sign_in_as, create_classified_mission_named și așa mai departe, este ușor să vedem cât de repede pot începe să suge și să se adune.

UI-specificațiile legate de multe ori nu primesc tratamentul OO de care au nevoie / merită, cred. Ei au reputația de a oferi prea puțină lovitură pentru buck și, bineînțeles, dezvoltatorii nu sunt prea îndrăgostiți de momente când trebuie să atingă multe lucruri de marcare. În mintea mea, acest lucru face chiar mai important să DRY aceste specificații și să facă distractiv să se ocupe cu ei prin aruncarea în câteva clase Ruby.

Să facem un mic truc magic în care ascund punerea în aplicare a aplicației Page Object pentru moment și să vă arăt doar rezultatul final aplicat specificațiilor de caracteristică de mai sus:

spec / caracteristici / m_creates_a_mission_spec.rb

"ruby necesită 'rails_helper'

caracteristică "M creează misiune" face scenariu "cu succes" face sign_in_as "[email protected]" vizită missions_path mission_page = Pagini :: Missions.new

mission_page.create_classified_mission_named "Project Moonraker" așteptați (mission_page) .to have_mission_named "Project Moonraker" end end "

spec / caracteristici / agent_completes_a_mission_spec.rb

"ruby necesită 'rails_helper'

caracteristică 'M mărcile misiune ca completă' face scenariul 'cu succes' face sign_in_as '[email protected]' vizita missions_path mission_page = Pagini :: Missions.new

mission_page.create_classified_mission_named 'Project Moonraker' mission_page.mark_mission_as_complete 'Project Moonraker' așteptați (mission_page) .to have_completed_mission_named 'Project Moonraker' end end "

Nu citește prea rău, nu? În principiu, creați metode expresive de împachetare pe Obiectele dvs. de pagină care vă permit să vă ocupați de concepte de nivel înalt - în loc să vă lămuriți peste tot cu intestinele marcajului dvs. tot timpul. Metodele extrase fac acum acest tip de lucru murdar, și astfel operația de pușcă nu este problema ta.

Puneți altfel, încapsulați majoritatea codului interactiv DOM. Trebuie să spun, totuși, că uneori metode extrase inteligent în specificațiile dvs. de caracteristică sunt suficiente și pot fi citite mai bine, deoarece puteți evita să vă ocupați de instanțele de tip Obiect. Oricum, să aruncăm o privire la punerea în aplicare:

Specificatii / suport / caracteristici / pagini / missions.rb

Modul ruby ​​Modele de pagini Misiuni includ Capybara :: DSL

Defineți numele de misiune cu numele de misiune, faceți clic pe butonul "Creare misiune" fill_in "Numele misiunii", cu: nume_locație click_button "Trimiteți" sfârșitul definiției def_image_as_complete (misiune_name) sfarsit def has_mission_named? (mission_name) mission_list.has_css? 'li', text: mission_name end def has_completed_mission_named? (mission_name) mission_list.has_css? 'li.mission-name.completed', text: mission_name sfârșitul privat def mission_list find ('ul.missions') end end end "

Ceea ce vedeți este un obiect obișnuit Ruby obiect-Pagina Obiecte sunt în esență clase foarte simple. În mod normal, nu instanțiați obiectele de pagină cu date (când apare necesitatea, desigur, puteți) și creați mai ales o limbă prin interfața API utilizată de un utilizator sau de o persoană interesată non-tehnică a unei echipe. Când vă gândiți să vă numiți metodele, cred că este un sfat bun să vă puneți întrebarea: Cum ar descrie un utilizator fluxul sau acțiunea întreprinsă?

Ar trebui să adaug că fără a include Capybara, muzica se oprește destul de repede.

ruby include Capybara :: DSL

Probabil vă întrebați cum funcționează aceste produse personalizate:

"ruby așteaptă (pagina) .to have_mission_named" Project Moonraker "așteptați (pagina) .to have_completed_mission_named" Project Moonraker "

def has_miss_named? (nume_primire) ... end

def has_completed_mission_named? (nume de misiune) ... end "

RSpec generează aceste metode personalizate bazate pe metode predicate pe obiectele dvs. de pagină. RSpec le convertește eliminând ? și schimbări are la avea. Boom, matheri de la zero, fără fuzz! Un pic de magie, îți voi da asta, dar un fel de vrăjitorie, aș spune.

De când am parcat Obiectul nostru de pagină la Specificatii / suport / caracteristici / pagini / missions.rb, trebuie, de asemenea, să vă asigurați că următoarele nu sunt comentate în spec / rails_helper.rb.

rubin Dir [Rails.root.join ('spec / support / ** / *. rb')] fiecare | f | cer f

Dacă intri într-un NameError cu un pagini constante neinitializate, veți ști ce să faceți.

Dacă sunteți curios ce sa întâmplat cu sign_in_as , am extras-o într-un modul la spec / suport / sign_in_helper.rb și a spus RSpec să includă modulul respectiv. Acest lucru nu are nimic de-a face cu obiectele Direct Page-este doar mai logic să păstreze funcționalitatea de testare cum ar fi conectare într-un mod accesibil mai mult la nivel global decât prin intermediul unui obiect Page.

spec / suport / sign_in_helper.rb

rubin modul SignInHelper def sign_in_as (e-mail) vizitați root_path fill_in 'E-mail', cu: email click_button 'Trimite' sfârșit sfârșit

Și trebuie să lăsați RSpec să știe că doriți să accesați acest modul auxiliar:

spec / spec_helper.rb

"ruby ... necesită" support / sign_in_helper "

RSpec.configure do | config | config.include SignInHelper ... end "

În general, este ușor să vedem că am reușit să ascundem elementele de identificare de tip Capybara, clicurile pe linkuri etc. Acum ne putem concentra asupra funcționalității și mai puțin asupra structurii reale a marcajului, care este acum încapsulată într-un obiect Page - structura DOM ar trebui să fie cea mai mică dintre preocupările dvs. atunci când testați ceva la fel de înalt ca specificațiile caracteristicilor.

Atenţie!

Chestii de configurare cum ar fi datele din fabrică aparțin specificațiilor și nu în Obiecte de pagină. De asemenea, afirmațiile sunt probabil mai bine plasate în afara obiectelor dvs. pentru a obține o separare a preocupărilor.

Există două perspective diferite asupra acestui subiect. Avocații pentru a pune afirmații în Page Objects spun că ajută la evitarea dublării afirmațiilor. Puteți oferi mesaje de eroare mai bune și puteți obține un stil mai bun "Spuneți, nu întrebați". Pe de altă parte, pledoarii pentru obiectele de pagină fără afirmații susțin că este mai bine să nu se amestece responsabilitățile. Furnizarea accesului la datele paginilor și logica afirmațiilor sunt două preocupări separate și duc la obturarea obiectelor de pagină când sunt amestecate. Obiectul paginii Obiect este accesul la starea de pagini, iar logica afirmației aparține specificațiilor.

Tipuri de obiecte de tip pagină

Componente reprezintă cele mai mici unități și sunt mai concentrate, de exemplu, ca un obiect de formă.

Pagini combinați mai multe dintre aceste componente și sunt abstracții ale unei pagini complete.

experienţe, după cum ați ghicit până acum, să extindeți întregul flux pe paginile potențial diferite. Ele sunt mai înalte. Acestea se concentrează pe fluxul pe care îl întâmpină utilizatorul în timp ce interacționează cu diverse pagini. Un flux de checkout care are câțiva pași este un bun exemplu pentru a vă gândi la acest lucru.

Când și de ce?

Este o idee bună să aplicați acest model de design un pic mai târziu într-un ciclu de viață al unui proiect - când ați acumulat puțină complexitate în specificațiile dvs. de caracteristică și atunci când puteți identifica modele repetate cum ar fi structurile DOM, metodele extrase sau alte comune care sunt consistent pe paginile dvs..

Deci probabil că nu ar trebui să începeți imediat să scrieți obiecte de pagină. Apropieți aceste refactorizări treptat atunci când complexitatea și dimensiunea aplicațiilor / testelor dvs. cresc. Duplicațiile și refactorizările care au nevoie de o locuință mai bună prin intermediul obiectelor de pagină vor fi mai ușor de observat în timp.

Recomandarea mea este să începeți cu extragerea metodelor din specificațiile dvs. de caracter local. Odată ce au atins masa critică, vor arăta ca niște candidați evideni pentru extracție ulterioară și majoritatea probabil că se vor potrivi profilului pentru Page Objects. Începeți puțin, deoarece optimizarea prematură lasă semne urâte!

Gândurile finale

Obiectele de pagină vă oferă posibilitatea de a scrie specificații mai clare, care să citească mai bine și sunt, în general, mult mai expresive, deoarece acestea sunt mai înalt. În plus, oferă o abstracție plăcută tuturor celor cărora le place să scrie codul OO. Ele ascund specificul DOM și, de asemenea, vă permit să aveți metode private care fac munca murdară în timp ce sunt neexpuse la API-ul public. Metodele extrase din specificațiile dvs. nu oferă același lux. API-ul obiectelor de pagină nu are nevoie să împărtășească detaliile Capybara.

Pentru toate scenariile când se schimbă implementările de proiectare, descrierile dvs. privind modul în care ar trebui să funcționeze aplicația dvs. nu trebuie modificate atunci când utilizați obiecte din pagină - specificațiile dvs. de caracteristică sunt mai bine orientate spre interacțiunile la nivel de utilizator și nu vă interesează atât de mult despre specific a implementărilor DOM. Deoarece schimbarea este inevitabilă, obiectele de pagină devin critice atunci când aplicațiile cresc și, de asemenea, ajută la înțelegerea când dimensiunea pură a aplicației înseamnă o creștere drastică a complexității.

Cod