Testa JavaScript med PhantomJS

Jag tror inte att jag måste övertyga dig om att testa din JavaScript-kod är en bra idé. Men det kan ibland vara tråkigt att testa JavaScript-kod som kräver en DOM. Det betyder att du måste testa din kod i webbläsaren och kan inte använda terminalen, eller hur? Fel, faktiskt: skriv in PhantomJS.


Vad är PhantomJS? Tja, här är en blurb från PhantomJS hemsida:

PhantomJS är en huvudlös WebKit med JavaScript API.

Som du vet är Webkit den layoutmotor som Chrome, Safari och några andra nisch-webbläsare använder. Så PhantomJS är en webbläsare, men en huvudlös webbläsare. Det innebär att de gjorda webbsidorna aldrig visas. Det här låter konstigt för dig; så du kan tänka på det som en programmerbar webbläsare för terminalen. Vi tittar på ett enkelt exempel på en minut, men vi behöver först installera PhantomJS.


Installera PhantomJS

Installera PhantomJS är faktiskt ganska enkelt: det är bara ett enda binärt som du laddar ner och håller fast i din terminalväg. På PhantomJS nedladdningssida, välj ditt operativsystem och ladda ner rätt paket. Flytta sedan binärfilen från det nedladdade paketet till en katalog i din terminalväg (jag gillar att sätta in den här typen av saker ~ / Bin).

Om du är på Mac OS X finns det ett enklare sätt att installera PhantomJS (och det här är faktiskt den metod jag använde). Använd bara Homebrew, så här:

brygga uppdatering & & brygga installationsfantomier

Du ska nu ha PhantomJS installerad. Du kan dubbelkontrollera din installation genom att köra den här:

fantom - version

Jag ser 1.7.0; du?


Ett litet exempel

Låt oss börja med ett litet exempel.

simple.js
console.log ("vi kan logga ut saker."); funktion lägg till (a, b) return a + b;  conslole.log ("Vi kan även utföra regelbundna JS:", lägg till (1, 2)); phantom.exit ();

Fortsätt och kör denna kod genom att utfärda följande kommando:

phantomjs simple.js

Du bör se produktionen från de två console.log rader i ditt terminalfönster.

Visst, det här är enkelt, men det gör en bra sak: PhantomJS kan utföra JavaScript precis som en webbläsare. Men det här exemplet har ingen PhantomJS-specifik kod ... bortsett från den sista raden. Det är en viktig rad för varje PhantomJS-skript eftersom det lämnar manuset. Det här kan inte vara meningslöst här, men kom ihåg att JavaScript inte alltid kör linjärt. Till exempel kanske du vill sätta utgång() ringa in en återuppringningsfunktion.

Låt oss titta på ett mer komplext exempel.


Läser in sidor

Med PhantomJS API kan vi faktiskt ladda alla webbadresser och arbeta med sidan från två perspektiv:

  • som JavaScript på sidan.
  • som användare tittar på sidan.

Låt oss börja med att välja att ladda en sida. Skapa en ny skriptfil och lägg till följande kod:

script.js
var page = require ('webbsida'). skapa (); page.open ('http://net.tutsplus.com', funktion (er) console.log (s); phantom.exit (););

Vi börjar med att ladda PhantomJS ' webbsida modul och skapa ett webbsidobjekt. Vi ringer sedan på öppna metod, skickar den en URL och en återuppringningsfunktion; Det finns inom denna återuppringningsfunktion som vi kan interagera med själva sidan. I ovanstående exempel loggar vi bara på statusen för begäran, som tillhandahålls av återuppringningsfunktionens parameter. Om du kör det här skriptet (med phantomjs script.js), bör du få "framgång" tryckt i terminalen.

Men låt oss göra det mer intressant genom att ladda en sida och utföra några JavaScript på den. Vi börjar med ovanstående kod, men vi ringer sedan till page.evaluate:

page.open ('http://net.tutsplus.com', funktion () var title = page.evaluate (funktion () var inlägg = document.getElementsByClassName ("post"); inlägg [0] .style. backgroundColor = "# 000000"; return document.title;); page.clipRect = topp: 0, vänster: 0, bredd: 600, höjd: 700; sid.render (titel + ".png"); .utgång(); );

PhantomJS är en webbläsare, men en huvudlös webbläsare.

Funktionen som vi skickar till page.evaluate exekveras som JavaScript på den laddade webbsidan. I det här fallet hittar vi alla element med posta klass; då ställer vi bakgrunden till det första inlägget till svart. Slutligen återkommer vi dokument titel. Det här är en bra funktion, som returnerar ett värde från vår utvärdera återuppringning och tilldela den till en variabel (i det här fallet, titel).

Sedan satte vi clipRect på sidan; Det här är dimensionerna för skärmdumpen vi tar med göra metod. Som du kan se ställer vi in topp och vänster värden för att ställa utgångspunkten, och vi ställer också in a bredd och höjd. Slutligen kallar vi page.render, skickar det ett namn på filen (the titel variabel). Sedan slutar vi med att ringa phantom.exit ().

Fortsätt och kör det här skriptet, och du borde ha en bild som ser något ut så här:

Du kan se båda sidorna av PhantomJS-myntet här: Vi kan utföra JavaScript från insidan och även exekvera från utsidan, på sidans instans själv.

Detta har varit roligt, men inte otroligt användbart. Låt oss fokusera på att använda PhantomJS när vi testar vår DOM-relaterade JavaScript.


Testning med PhantomJS

Yeoman använder PhantomJS i sitt testförfarande, och det är nästan sömlöst.

För mycket JavaScript-kod kan du testa utan att behöva en DOM, men det finns tillfällen då dina test måste fungera med HTML-element. Om du är som jag och föredrar att köra tester på kommandoraden, så här kommer PhantomJS till spel.

Naturligtvis är PhantomJS inte ett testbibliotek, men många av de andra populära testbiblioteken kan springa på toppen av PhantomJS. Som du kan se från PhantomJS wiki-sidan om headless testning, finns PhantomJS test löpare tillgängliga för stort sett alla testbibliotek du kanske vill använda. Låt oss titta på hur man använder PhantomJS med jasmin och mocka.

Först Jasmine och en ansvarsfriskrivning: det finns inte en bra PhantomJS löpare för Jasmine vid denna tidpunkt. Om du använder Windows och Visual Studio bör du kolla Chutzpah och Rails-utvecklare ska försöka skydda jasmin. Men förutom det är Jasmine + PhantomJS-stödet gles.

Av denna anledning rekommenderar jag att du använder Mocha för DOM-relaterade test.

DOCK.

Det är möjligt att du redan har ett projekt med Jasmine och vill använda det med PhantomJS. Ett projekt, fantom-jasmin, tar lite arbete att sätta upp, men det borde göra tricket.

Låt oss börja med en uppsättning JasmineJS-tester. Ladda ner koden för denna handledning (länken längst upp) och kolla in jasmin-starter mapp. Du ser att vi har en enda tests.js fil som skapar ett DOM-element, ställer in några egenskaper och lägger till det i kroppen. Sedan kör vi några jasminprov för att säkerställa att processen fungerade korrekt. Här är innehållet i den filen:

tests.js
beskriv ("DOM-test", funktion () var el = document.createElement ("div"); el.id = "myDiv"; el.innerHTML = "Hej där!"; el.style.background = "#ccc "; document.body.appendChild (el); var myEl = document.getElementById ('myDiv'); det (" finns i DOM ", funktionen () förvänta (myEl) .not.toBeNull ();); det ("är ett barn i kroppen", funktionen () (förvänta (myEl.parentElement) .toBe (document.body);); det ("har rätt text", funktion () förvänta (myEl.innerHTML ) .toEqual ("Hi there!";); den ("har rätt bakgrund", funktion () expect (myEl.style.background) .toEqual ("rgb (204, 204, 204)"); ););

De SpecRunner.html filen är ganska lagrad Den enda skillnaden är att jag flyttade skriptet taggar in i kroppen för att säkerställa att DOM fylls helt innan våra test körs. Du kan öppna filen i en webbläsare och se till att alla tester passerar bra.

Låt oss övergå till projektet PhantomJS. Först klonera fantom-jasminprojektet:

git klon git: //github.com/jcarver989/phantom-jasmine.git

Detta projekt är inte så organiserat som det kan vara, men det finns två viktiga delar du behöver av det:

  • PhantomJS löpare (vilket gör Jasmine använda en PhantomJS DOM).
  • Jasmine konsol reporter (som ger konsolen output).

Båda dessa filer bor i lib mapp; kopiera dem till jasmin-starter / lib. Vi behöver nu öppna vår SpecRunner.html fil och justera

Observera att vi har två reportrar för våra test: en HTML-reporter och en konsol reporter. Detta betyder SpecRunner.html och dess test kan köras både i webbläsaren och i konsolen. Det är praktiskt. Tyvärr behöver vi det console_reporter variabel eftersom den används inom CoffeeScript-filen som vi ska köra.

Så, hur går vi att faktiskt köra dessa tester på konsolen? Förutsatt att du är i jasmin-starter mapp på terminalen, här är kommandot:

phantomjs lib / run \ _jasmin \ _test.coffee ./SpecRunner.html

Vi kör köra \ _jasmine \ _test.coffee manus med PhantomJS och passerar vår SpecRunner.html filen som en parameter. Du borde se något så här:

Självklart, om ett test misslyckas ser du något som följande:

Om du planerar att använda det ofta, kan det vara en bra idé att flytta köra \ _jasmine \ _test.coffee till en annan plats (som ~ / Bin / kör \ _jasmine \ _test.coffee) och skapa ett terminalalias för hela kommandot. Så här gör du det i ett Bash-skal:

alias phantom-jasmine = "phantomjs / path /to/run\_jasmine\_test.coffee"

Kasta bara det i din .bashrc eller .bash_profile fil. Nu kan du bara springa:

fantom-jasmin SpecRunner.html

Nu fungerar dina jasminprov bara bra på terminalen via PhantomJS. Du kan se den slutliga koden i jasmin-totalt mappen i hämtningen.


PhantomJS och mocka

Tack och lov är det mycket lättare att integrera Mocha och PhantomJS med mocka-fantom. Det är super lätt att installera om du har installerat NPM (vilket du borde):

npm installera -g mocha-phantomjs

Kommandot installerar a mocka-phantomjs binärt som vi ska använda för att köra våra tester.

I en tidigare handledning visade jag dig hur man använder mocka i terminalen, men du gör saker annorlunda när du använder den för att testa DOM-koden. Som med Jasmine börjar vi med en HTML-testreporter som kan köras i webbläsaren. Skönheten i detta är att vi kommer att kunna köra samma fil på terminalen för konsoltestresultat med PhantomJS; precis som vi kunde med Jasmine.

Så, låt oss bygga ett enkelt projekt. Skapa en projektkatalog och flytta in i den. Vi börjar med en package.json fil:

"namn": "projekt", "version": "0.0.1", "devDependencies": "mocha": "*", "chai": "*"

Mocka är testramen, och vi använder Chai som vårt assertionsbibliotek. Vi installerar dessa genom att köra NPM.

Vi ringer vår testfil test / tests.js, och här är dess test:

beskriv ("DOM-test", funktion () var el = document.createElement ("div"); el.id = "myDiv"; el.innerHTML = "Hej där!"; el.style.background = "#ccc "; document.body.appendChild (el); var myEl = document.getElementById ('myDiv'); det (" är i DOM ", funktionen () förvänta (myEl) .to.not.equal (null); ), det ("är ett barn i kroppen", funktionen () (förvänta (myEl.parentElement) .to.equal (document.body);); det ("har rätt text" förvänta (myEl.innerHTML) .to.equal ("Hej där!");); det ("har rätt bakgrund", funktion () förvänta (myEl.style.background) .to.equal ("rgb 204, 204, 204) ");););

De är mycket lik Jasmine-testerna, men Chai-stämningssyntaxen är lite annorlunda (så kopiera inte bara dina jasminprov).

Det sista stycket av pusslet är TestRunner.html fil:

   tester     

Det finns flera viktiga faktorer här. Först märker du att detta är tillräckligt för att springa i en webbläsare; Vi har CSS och JavaScript från de nodmoduler som vi installerade. Lägg märke till inline-skriptet. Detta bestämmer om PhantomJS är laddad, och om så är fallet, körs PhantomJS-funktionaliteten. Annars stämmer den med rå Mocha-funktionalitet. Du kan prova detta i webbläsaren och se det fungera.

För att köra det i konsolen, kör det här helt enkelt:

mocha-phantomjs TestRunner.html

Voila! Nu testar du i konsolen, och det är allt tack vare PhantomJS.


PhantomJS och Yeoman

Jag slår vad om att du inte visste att den populära Yeoman använder PhantomJS i sin testprocedur, och det är vritligt ogenomträngligt. Låt oss titta på ett snabbt exempel. Jag antar att du har Yeoman all set up.

Skapa en ny projektkatalog, kör yeoman init inuti det och svara nej till alla alternativ. Öppna test / index.html fil, och du hittar en skript tag nära botten med en kommentar som säger att du ska ersätta den med dina egna specifikationer. Helt ignorera det goda råd och sätt detta inuti Det blockera:

var el = document.createElement ("div"); förväntar (el.tagName) .to.equal ( "DIV");

Nu kör yeoman test, och du ser att testet går bra. Nu öppen test / index.html filen i webbläsaren. Det fungerar! Perfekt!

Självklart finns det mycket mer du kan göra med Yeoman, så kolla in dokumentationen för mer.


Slutsats

Använd biblioteken som utökar PhantomJS för att göra testningen enklare.

Om du använder PhantomJS på egen hand finns det ingen anledning att lära dig om PhantomJS själv; du kan bara veta att det existerar och använda bibliotek som utökar PhantomJS för att göra testningen enklare.

Jag hoppas att denna handledning har uppmuntrat dig att titta på PhantomJS. Jag rekommenderar att du börjar med exempelfilerna och dokumentationen som PhantomJS erbjuder; De öppnar verkligen dina ögon för vad du kan göra med PhantomJS - allt från sidautomatisering till nätverkssniffning.

Så, kan tänker du på ett projekt som PhantomJS skulle förbättra? Låt oss höra om det i kommentarerna!