Headless Functional Testing met Selenium en PhantomJS

Laten we een systeem bouwen voor het uitvoeren van functionele tests op webtoepassingen, met behulp van Selenium en PhantomJS. Het resulterende systeem zal ons toelaten om gemakkelijke testscenario's te schrijven in JavaScript, en die scenario's zowel in echte browsers als een headless simulator te testen.


Componenten kiezen

Het voor de hand liggende nadeel van Selenium is dat het een volledig grafische desktop vereist voor alle tests.

Om te beginnen moeten we een browserbesturing of emulatie-engine kiezen om een ​​eindgebruiker te simuleren. Lange tijd was de primaire speler op dit gebied Selenium en dat is het nog steeds. Selenium maakt geautomatiseerde controle van echte browsers op echte besturingssystemen mogelijk, wat het belangrijkste voordeel is: u kunt er absoluut zeker van zijn dat de tests de realiteit zo dicht mogelijk weergeven.

Het voor de hand liggende nadeel van Selenium is dat het een volledig grafische desktop vereist voor alle tests. Als gevolg hiervan kunnen uw tests langzaam worden. Selenium kan echter fantastisch zijn, als je over de nodige middelen beschikt om virtuele machines voor verschillende besturingssystemen in te stellen en alles met elkaar te verbinden.

Aan de andere kant van het spectrum staat PhantomJS: een klein maar uitstekend project, met een WebKit-engine met volledige JavaScript-toegang, maar zonder het grafische gedeelte. PhantomJS is een makkie om in te stellen, werkt op elke machine en is aanzienlijk sneller.

Selenium kan PhantomJS nu op dezelfde manier besturen als een andere browser.

PhantomJS, zijnde een volledige WebKit, dekt 90% van uw functionele testbehoeften. Immers, als uw applicatie correct in WebKit wordt uitgevoerd, is deze waarschijnlijk correct in andere browsers. Vanzelfsprekend sluit dit Internet Explorer 6-8 uit.

Naarmate uw project echter steeds populairder wordt, wordt die resterende 10% een belangrijk probleem. Als uw functionele testsuite rechtstreeks op PhantomJS is geïnstalleerd, zou het lastig zijn om de tests voor Selenium te herschrijven.

Gelukkig hebben we onlangs, kort na het einde van 2012, een geschenk ontvangen in de vorm van PhantomJS-bindingen met Selenium. Met andere woorden, Selenium kan PhantomJS nu op dezelfde manier besturen als een andere browser.

Aangezien Selenium zelf geen ingewikkelde opstelling nodig heeft en overal kan worden gebruikt, kunnen we Selenium-bindingen gebruiken om PhantomJS te besturen en 90% van onze testbehoeften dekken. Als u later krachtigere tests nodig heeft, kunt u extra browserverbindingen met Selenium instellen zonder een enkele regel in uw code te wijzigen.

Onze keuze voor een browserengine is dus Selenium met PhantomJS.

Tests beschrijven

Selenium biedt bindingen in de meest populaire programmeertalen, zodat we een taal kunnen kiezen op basis van onze behoeften. Dit is misschien wel het meest controversiële onderdeel van dit artikel: ik beschouw JavaScript als de beste keuze voor het beschrijven van functionele tests voor websites en webtoepassingen..

  • Welke back-endtechnologie u ook gebruikt, uw front-end gebruikt altijd JavaScript (Dit is ook van toepassing als u een taal gebruikt die overeenkomt met vanille JavaScript, zoals CoffeeScript of TypeScript.). Als zodanig is JavaScript altijd een door ten minste één persoon in uw team begrepen taal.
  • Overweeg vervolgens de mogelijkheid dat uw functionele tests worden geschreven door niet-programmeurs. De populariteit van JavaScript op de front-end, gecombineerd met expressiviteit in het vermogen om duidelijke domein-specifieke talen te creëren, maakt het duidelijk dat meer mensen functionele tests kunnen schrijven.
  • Ten slotte is het logisch om een ​​testbrowser te besturen met JavaScript, aangezien deze zeer asynchroon is en waarmee we dagelijks de browser besturen..

Selenium-bindingen voor JavaScript worden genoemd, webdriverjs. Hoewel het project minder volwassen is dan de officieel ondersteunde stuurprogramma's voor Java, C #, Ruby en Python, bevat het toch al de meeste functionaliteit die we nodig hebben.

Test hardlopen

Voor de toepassing van dit artikel zijn Mocha met Chai geselecteerd.

Ten slotte hebben we een testrunner of een toepassing nodig om tests op naam uit te voeren en de uitvoer mooi af te drukken, terwijl we zien hoeveel tests zijn geslaagd of mislukt. Deze testrunner moet ook een assertiebibliotheek aanbieden, waarmee de coder kan uitspreken of een test slaagt of faalt.

De keuze is hier helemaal gratis. Er zijn veel JavaScript-testlopers, maar voor de toepassing van dit artikel zijn Mocha met Chai geselecteerd. Mocha biedt een aanzienlijke hoeveelheid flexibiliteit, een breed scala aan uitvoerformaten en de populaire syntaxis van Jasmine. Met Chai kun je beschrijvende BDD-achtige beweringen schrijven.


Opstelling

Dit is de uiteindelijke stapel die we gaan gebruiken:

  1. Mocha - test loper
  2. Chai - assertiebibliotheek
  3. webdriverjs - browser control bindingen
  4. Selenium - browseronttrekking en draaiende fabriek
  5. PhantomJS - snelle browser zonder kop

Node.js en npm

Omdat de meeste van onze stack is gebaseerd op JavaScript, hebben we node.js en npm nodig. Beide zijn algemene hulpmiddelen in de community en ik neem aan dat je ze al hebt ingesteld. Als u dit niet doet, gebruikt u het installatieprogramma op de node.js-website. Maak je geen zorgen; als er iets misgaat, zijn er veel node install-handleidingen beschikbaar op internet.

Mocha, Chai en webdriverjs

Alle drie kunnen worden geïnstalleerd, met behulp van NPM:

sudo npm install -g mocha chai webdriverjs

U kunt ze ook lokaal installeren in de directory waar uw tests zich bevinden:

npm installeer mocha chai webdriverjs

Selenium

Download Selenium Server. Het wordt als een single verspreid pot bestand, dat u eenvoudig uitvoert:

java -jar selenium-server-standalone-2.28.0.jar

Zodra u deze opdracht uitvoert, wordt een server gestart waaraan uw testcode later zal worden gekoppeld. Houd er rekening mee dat u Selenium Server elke keer dat u uw tests uitvoert, moet uitvoeren.

PhantomJS

Snelle versie

Gebruik NPM om PhantomJS wereldwijd te installeren:

sudo npm install -g phantomjs

Andere opties

We hebben een nieuwe versie van PhantomJS nodig - minimaal 1,8. Dit betekent dat pakketten worden aangeboden door uw pakketbeheerder (apt-get, MacPorts, ...) zullen hoogstwaarschijnlijk verouderd zijn.

U kunt met npm installeren zonder een globale installatie of met andere methoden handmatig. In dit geval moet u echter Selenium vertellen waar u PhantomJS hebt geplaatst elke keer dat u Selenium gebruikt:

PATH = "/ path / to / node_modules / phantomjs / bin: $ PATH" java -jar selenium-server-standalone-2.28.0.jar

Alles combineren

Nu we alle stukjes hebben, moeten we alles samenvoegen.

Vergeet niet: voordat u een test uitvoert, moet u Selenium Server gebruiken:

java -jar selenium-server-standalone-2.28.0.jar

Selenium zal PhantomJS intern uitvoeren; daar hoef je je geen zorgen over te maken.

Nu moeten we verbinding maken met Selenium via onze JavaScript. Hier is een voorbeeldfragment dat een verbinding met Selenium initieert en een klaar object heeft om onze Selenium-instantie te besturen:

// Gebruik webdriverjs om een ​​Selenium Client te maken var client = require ('webdriverjs'). Remote (desiredCapabilities: // U kunt andere browsers kiezen // http://code.google.com/p/selenium/wiki/ DesiredCapabilities browserName: 'phantomjs', // webdriverjs heeft veel output die over het algemeen nutteloos is // Als er echter iets fout gaat, verwijder dit dan om meer details te zien logLevel: 'silent'); client.init ();

Nu kunnen we onze tests beschrijven en de cliënt variabele om de browser te besturen. Een volledige referentie voor de webdriverjs API is beschikbaar in de documentatie, maar hier is een kort voorbeeld:

client.url ('http://example.com/') client.getTitle (functie (titel) console.log ('Title is', title);); client.setValue ('# veld', 'waarde'); client.submitForm (); client.end ();

Laten we de Mocha en Chai syntax gebruiken om een ​​test te beschrijven; we zullen enkele eigenschappen van de testen example.com webpagina:

beschrijven ('Test example.com', function () before (function (done) client.init () .url ('http://example.com', done);); beschrijven ('Homepage controleren' , function () it ('zou de juiste titel moeten zien', functie (gereed) client.getTitle (functie (titel) verwachten (titel) .to.have.string ('Voorbeelddomein'); done (); );); it ('zou het lichaam moeten zien', functie (gereed) client.getText ('p', functie (p) verwachten (titel) .to.have.string ('voor illustratieve voorbeelden in documenten . '); done ();));); after (function (done) client.end (); done ();););

Misschien wil je er een delen cliënt initialisatie over veel testbestanden. Maak een kleine knooppuntmodule om het te initialiseren en te importeren in elk testbestand:

client.js:

exports.client = require ('webdriverjs'). remote (// Instellingen;

test.js:

var client = require ('./ client'). client; var expect = require ('chai'). verwachten; // Voer tests uit

hardlopen

Mocha-testsuites worden uitgevoerd met de mokka binair. Als je deze handleiding hebt gevolgd en Mocha lokaal hebt geïnstalleerd, moet je zelf een volledig pad naar het binaire bestand beschrijven: node_modules / mokka / bin / mokka.

Standaard behandelt Mocha elke test die langer dan twee seconden duurt als mislukt. Aangezien we eigenlijk een webbrowser initialiseren en een HTTP-aanvraag doen, moeten we deze time-out verhogen tot 5 of 10 seconden:

node_modules / mocha / bin / mocha test.js -t 10000

Als alles volgens plan verliep, zou je de uitvoer als volgt moeten zien:

 . ✔ 1 test voltooid

De volgende stappen

Nadat u de gewenste functionele testresultaten hebt behaald, kunt u overwegen uw configuratie verder te verbeteren.

Twee voor de hand liggende richtingen zijn continue integratie en gedistribueerde testen op Selenium.

Continue integratie

Uw doel moet zijn om de tijd die u aan het uitvoeren van tests besteedt te minimaliseren.

Misschien wilt u een volledig automatische continue integratieserver gebruiken, die de tests automatisch uitvoert wanneer dat nodig is en u op de hoogte stelt als er iets misgaat.

In de wereld van open source wordt de rol van zo'n server gedekt door Jenkins CI: een handige, krachtige, eenvoudig te installeren service die de tests uitvoert wanneer dat nodig is, deze uitvoert in elke configuratie die u levert en mogelijk veel uitvoert meer buildgerelateerde taken, zoals het implementeren van uw code op externe servers.

Als alternatief kun je, als je je avontuurlijk voelt, experimenteren met een nieuw project, genaamd GitLab CI, dat minder functies biedt, maar er beter uitziet en is geïntegreerd met GitLab, een zelf gehoste GitHub-kloon.

In ieder geval zou het uw doel moeten zijn om de tijd die u doorbrengt met testen te minimaliseren. In plaats daarvan moeten de tests automatisch worden uitgevoerd en moeten ze u alleen informeren als er iets misgaat.

Selenium Grid

Selenium heeft een aantal implementatiebeperkingen. U kunt bijvoorbeeld niet meer dan enkele browsers op dezelfde computer gebruiken om met Selenium te worden getest.

Bovendien zult u merken dat, als u eenmaal vele tests hebt uitgevoerd, het uitvoeren van al deze taken een langdurig proces kan worden. Hoewel continue integratie dit probleem gedeeltelijk verhelpt, wilt u misschien nog steeds enkele tests parallel uitvoeren op verschillende machines.

Ten slotte zult u snel merken dat u verschillende browsers op verschillende besturingssystemen wilt testen. En, terwijl je testcode in theorie kan praten met verschillende Selenium-servers, als je eenmaal een beetje groeit, heeft deze opstelling centralisatie nodig.

Selenium Grid-instellingen proberen precies dat te bieden. In plaats van één Selenium-server een aantal browsers op een computer te laten besturen, hebt u één Selenium-server, die meerdere Selenium-knooppunten bestuurt, die elk maar een paar browsers op één besturingssysteem besturen.


Conclusie

De resulterende stapel, hoewel niet triviaal, is in werkelijkheid vrij eenvoudig. De toevoeging van PhantomJS aan het Selenium-einde stelt ons in staat Selenium te gebruiken zonder veel initiële investering, zoals het opzetten van grafische testservers.

Het gebruik van JavaScript als testengine zorgt ervoor dat onze tests in de nabije toekomst relevant blijven in de context van webontwikkeling.