TestAutomatisering & PerformanceTesten

Cucumber, Selenium en het gebruik van het Page Object Pattern

Als je Selenium gebruikt om je testen aan te sturen, kan je gebruik maken van het Page Object Pattern om een laag van abstractie aan te maken: Het maakt een model van je pagina zodat het makkelijker is om acties op die pagina uit te voeren. Dit heeft grote voordelen: Zodra er een wijziging op de pagina plaatsvindt, hoef je alleen het model aan te passen en niet je test acties. Een tester kan daarnaast redelijk eenvoudig teststappen opbouwen zonder de achterliggende Selenium code te hoeven beheersen.

Het spreekt voor zich dat je het Page Object Pattern graag zou willen gebruiken als je wilt testen met het Cucumber framework: Je glue code (of steps) kunnen dan op een hoog abstractielaag gemaakt worden waardoor je het lostrekt van het onderliggende uitvoerende mechanisme.

Voor een enkele pagina kan je dat als volgt doen:

public class CustomerStep {
    // maak een page object aan voor de customer page
    static CustomerPage customerPage = new CustomerPage();

    @Given("^I navigate to the customers page$")
    public void I_navigate_to_the_customers_page() throws 
                                               Throwable {
        // roep deze methode aan op het page object
        customerPage.makeActivePage(); 
    }
}

public class CustomerPage {

    // laten we hier een webDriver aanmaken zodat we kunnen 
    // interacteren met de pagina.
    public WebDriver webDriver;

    public CustomerPage() {
        WebDriver webDriver = new FirefoxDriver();
    }

    public void makeActivePage() {
        webDriver.navigate().to("http://foo.com/customer.html");
    }
}

Nu hebben we een werkend geheel waarbij we het Page Object Pattern kunnen gebruiken binnen Cucumber. We krijgen alleen een probleem als we meerdere pagina’s gaan gebruiken. Voor elke pagina wordt er namelijk een nieuwe webdriver aangemaakt. Niet handig als je meerdere scenario’s op dezelfde context (dus in dezelfde browsersessie) uit wilt voeren.

Dat lossen we als volgt op: We maken een class aan met een static webdriver die overerfbaar is.

public class CommonPage {

    // De webDriver verplaatsen we hiernaartoe. We maken hem 
    // static zodat er maar 1 webdriver aangemaakt wordt, 
    // onafhankelijk van het aantal pagina instanties die er 
    // gebruikt worden.
    public static WebDriver webDriver;

    protected CommonPage() {
        if (webDriver == null)
            webDriver = new FirefoxDriver();
    }
}

Wanneer we een nieuw Page Object maken, dan overerven we van deze class. Op die manier maken we altijd gebruik van dezelfde webdriver. Hieronder zien we hoe de het CustomerPage object eruit ziet met een CommonPage als super class:

public class CustomerPage extends CommonPage {

    // We gebruiken de webDriver van de Super class
    public void makeActivePage() {
        webDriver.navigate().to("http://foo.com/customer.html");
    }
}

De class ziet er dus nog simpeler uit, terwijl we toch altijd gebruik kunnen maken van de webdriver.

Een ander voordeel is, is dat we generieke acties, die dus niet specifiek  tot een pagina behoren, kwijt kunnen in deze super class: Alle pagina objecten hebben vanaf dan toegang tot deze aspecifieke methodes.

public class CommonPage {

    // [ ... ]

    // Op alle pagina’s komen secties voor. Laten we hier een
    // herbruikbare methode van maken. Alle pagina objecten kunnen
    // nu gebruik maken van “getSections”
    public List<WebElement> getSections(String sectionName) {
        return webDriver.findElements(
            By.cssSelector("section." + sectionName));
    }
}

Nu is de method “getSections” te gebruiken door iedere PageStep library. Op deze manier kunnen we dus ook een Common PageStep library maken waar we alle Common Steps in plaatsen:

public class CommonSteps {

    // We gebruiken geen specifieke page om onze acties op te 
    // doen, we kunnen natuurlijk wel een common page object 
    // gebruiken.
    CommonPage commonPage = new CommonPage();

    @Before
    public void beforeScenario() {
        // Plaats hier acties die altijd moeten gebeuren voordat 
        // een scenario uitgevoerd moet worden.
        // Bedenk eens wat je hier allemaal zou kunnen doen!
    }

    // Een pagina aspecifieke actie. Die kunnen we het beste 
    // hier kwijt.
    @Then("^it contains a \"([^\"]*)\" section$")
    public void it_contains_a_section(String sectionName) throws 
                                                     Throwable {
        List<WebElement> sections = 
            commonPage.getSections(sectionName);
        assertTrue("a section "+sectionName+" could be found.", 
            !sections.isEmpty());
    }
}

Daarnaast hebben we meteen een mooie placeholder voor de acties die voor, met de @Before annotatie, en na, met behulp van de @After annotatie, een scenario moeten gebeuren.

Door slim gebruik te maken van pagina objecten, overerving en een static webdriver kan een net en overzichtelijke codebasis gemaakt worden voor Cucumber testen die gebruik maken van Selenium. We splitsen de stap op in een beoogde actie op de pagina door een call naar het pagina object en de specifiek invulling van de actie door Selenium. Ook splitsen we generieke acties van specifieke pagina acties door gebruik te maken van een common library.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *

Nieuws

Blijf op de hoogte

APACHE MPM (op *nix servers)

07/11/2017

Benieuwd naar de impact van het wijzigen van Apache MPM Prefork naar Worker? Lees dan door! Bij één van onze klanten heb ik dit onderzocht. Omdat dit ook interessant kan zijn voor anderen, deel ik mijn resultaten en ervaringen graag. Het is misschien wat technisch allemaal, maar voor performancetesters, de doelgroep, is het vast goed […]

Performancetesten en CI/CD, gaat dat samen?

13/10/2017

De afgelopen najaarseditie van Testnet stond onder het thema Continuous Everything vooral stil bij CI/CD en natuurlijk testen. Gezien DevOps en CI/CD ook grote invloed hebben op het vakgebied performance (testen), zijn wij blij dat we vanuit PerformanceArchitecten een bijdrage mochten leveren door middel van het delen van onze visie hierop. Onze collega René Meijboom […]

Een eerste indruk van Gauge

08/09/2017

Tijdens één van onze kennismiddagen hebben we gekeken naar het testtool Gauge. Doel van de sessie was om een beeld te krijgen wat de toegevoegde waarde van Gauge is voor een tester. Benieuwd naar onze ervaringen? Lees dan snel verder! Gauge is een open source project, gesponsord door ThoughtWorks en belooft in het kort het […]

Test Automation Framework BDD

16/06/2017

De afgelopen tijd heb ik in een opdracht collega’s mogen adviseren over de positieve impact van het Test Automation Framework BDD rondom het thema Agile. In een vorige blog ‘Transformation’ is te lezen wat BDD voor mij betekent en hoe BDD voor synergie zorgt tussen verschillende disciplines binnen Agile-teams met als doel het leveren van kwalitatief hoogwaardig […]

Creëer meer eenheid in je SpecFlow steps met Step Argument Transformations

11/04/2017

Laatst kreeg ik de vraag, “Hoe maak je in je testdata onderscheid tussen een regular expression en een gewone tekst”. Oftewel: Hoe beheer je verschillende soorten steps als ze alleen verschillen in de manier waarop data vergeleken wordt. Je hebt een veld en je wilt controleren of er een bepaalde waarde in staat, maar soms […]

SSL/TLS versie en cipher in HP LoadRunner

29/03/2017

In deze blog wil ik even stilstaan bij de resultaten van een performance test die niet overeenkwamen met de verwachtingen die wij als team hadden. Een aantal transacties gingen in responstijd omhoog en het CPU gebruik nam flink toe. Omdat het ons veel tijd heeft gekost, deel ik dit graag met jullie zodat wij performance […]

Regular Expressions en Testautomatisering, twee problemen of juist een oplossing?

25/03/2017

Bij geautomatiseerde checks wil je regelmatig een verwachte waarde controleren tegen een actuele waarde. Vroeg of laat kom je dan in aanraking met wildcards: Je wilt bijvoorbeeld weten of de tekst “Er zijn 42 resultaten gevonden” voorkomt, maar het aantal, hier 42, kan variabel zijn. Van 42 wil je dan een wildcard maken. De meest […]

Automated Approval Testing with Dynamic Data Sets

21/02/2017

For some tests, you’ll need to check if files or documents are the same as they were before, or that they comply with a certain format. When something differs, you need to “reject” or “approve“ them, or even make them the new baseline if you are welcoming the changes. We call this Approval Testing. For more […]

Transformation

20/01/2017

Inmiddels hebben veel organisaties de overstap gemaakt van de traditionele waterval methode naar een iteratieve ontwikkelmethode als Agile/Scrum. Een aantal organisaties zijn in hun werkwijze een stap verder gegaan en hebben DevOps geïntroduceerd. Waar de methodiek Agile vooral focust op het tevredenstellen van de klant, richt DevOps zich daarnaast op het dichten van de kloof […]

Cucumber, Selenium en het gebruik van het Page Object Pattern

14/12/2016

Als je Selenium gebruikt om je testen aan te sturen, kan je gebruik maken van het Page Object Pattern om een laag van abstractie aan te maken: Het maakt een model van je pagina zodat het makkelijker is om acties op die pagina uit te voeren. Dit heeft grote voordelen: Zodra er een wijziging op de […]

SpecFlow Extensies voor Dummies

21/11/2016

TechTalk heeft voor SpecFlow natuurlijk al een handige set aan extensies uitgebracht die vooral het gebruik van eigen datatypes in combinatie met SpecFlow steps mogelijk maken. Dit is slimme, maar vrij geavanceerde set waarbij je best wel wat ervaring moet hebben met zowel het gebruik van SpecFlow als C#. De beginnende gebruiker zal eerder behoefte […]

SpecFlow Extensies voor Dummies, de code

21/11/2016

Hieronder vind je de code die behoort bij het artikel SpecFlow Extensies voor Dummies  Feature File: Feature: Table Transformations In order to have convenient datatypes As a SpecFlow user I want to transform tables into dictionaries or datatables Scenario: Transform a vertical table into a dictionary Given I have the following table: | Movie | […]