CasperJS - automatizované testovanie webu

CasperJS je dobre použiteľný nástroj na automatizované testovanie (aj) webových aplikácií použitím prehliadača (bez prehliadača).

Samotné testy sú písané v javascripte. Namiesto skutočného prehliadača je predvolene použité javascriptové API WebKitu - PhantomJS. Voliteľne je možné použiť obdobný projekt SlimerJS s jadrom Gecko.

Minitest na ukážku

V ukážke je vytvorená sada testov obsahujúca jediný test. Načíta sa web google.com, skontroluje sa či má načítaná stránka titulok Google a test sa ukončí.

casper.test.begin('Google minitest', 1, function suite(test) {
    casper.start("http://google.com/", function() {
        test.assertTitle("Google", "Homepage title");
    });
    casper.run(function() {
        test.done();
    });
});

CasperJS - Google minitest

Inštalácia

Proces inštalácie popisujem pre aktuálny systém Crunchbang (Debian). Pre návod na inštaláciu na iných systémoch použite inštalačný manuál.

.NET/Mono

K behu je potrebný .NET Framework 3.5 alebo Mono 2.10.8, alebo ich vyššie verzie.

$ sudo apt-get install mono-devel mono-mcs

Kontrola inštalácie:

$ mono --version
Mono JIT compiler version 2.10.8.1 (Debian 2.10.8.1-8)
Copyright (C) 2002-2011 Novell, Inc, Xamarin, Inc and Contributors. www.mono-project.com
    TLS:           __thread
    SIGSEGV:       altstack
    Notifications: epoll
    Architecture:  x86
    Disabled:      none
    Misc:          softdebug 
    LLVM:          supported, not enabled.
    GC:            Included Boehm (with typed GC and Parallel Mark)

PhantomJS

Podla architektúry sú dostupné balíčky:
- 32-bit phantomjs-1.9.2-linux-i686.tar.bz2 (13,0 MB).
- 64-bit phantomjs-1.9.2-linux-x86_64.tar.bz2 (12,7 MB),

Popísaný postup funguje na 32-bitovej architektúre. V prípade 64-bitovej je v skriptoch potrebné zmeniť iba reťazec i686 na x86_64.

$ wget https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-i686.tar.bz2

Rozbalenie stiahnutého archívu:

$ tar -xvf phantomjs-1.9.2-linux-i686.tar.bz2

Presunutie do priečinka s programami:

$ sudo mv phantomjs-1.9.2-linux-i686 /usr/local/src/phantomjs

Symlink na program:

$ sudo ln -sf /usr/local/src/phantomjs/bin/phantomjs /usr/bin/phantomjs

Kontrola inštalácie:

$ phantomjs --version
1.9.2

Odstránenie stiahnutého archívu:

$ rm phantomjs-1.9.2-linux-i686.tar.bz2

SlimerJS (voliteľne)

V prípade ak je nainštalovaný PhantomJS, inštalácia SlimerJS nie je nutná.

Podla architektúry sú dostupné balíčky:
- 32-bit slimerjs-0.9.0-linux-i686.tar.bz2 (24,1 MB).
- 64-bit slimerjs-0.9.0-linux-x86_64.tar.bz2 (23,4 MB),

Popísaný postup funguje na 32-bitovej architektúre. V prípade 64-bitovej je v skriptoch potrebné zmeniť iba reťazec i686 na x86_64.

$ wget http://download.slimerjs.org/v0.9/0.9.0/slimerjs-0.9.0-linux-i686.tar.bz2

Rozbalenie stiahnutého archívu:

$ tar -xvf slimerjs-0.9.0-linux-i686.tar.bz2

Presunutie do priečinka s programami:

$ sudo mv slimerjs-0.9.0 /usr/local/src/slimerjs

Symlink na program:

$ sudo ln -sf /usr/local/src/slimerjs/slimerjs /usr/local/bin/slimerjs

Kontrola inštalácie:

$ slimerjs --version
Innophi SlimerJS 0.9.0, Copyright 2012-2013 Laurent Jouanneau & Innophi

Odstránenie stiahnutého archívu:

$ rm slimerjs-0.9.0-linux-i686.tar.bz2

CasperJS

Stiahnutie inštalačného archívu:

$ wget https://github.com/n1k0/casperjs/tarball/master -O casperjs.tar.gz

Rozbalenie stiahnutého archívu:

$ tar -xvf casperjs.tar.gz

Presunutie do priečinka s programami:

$ sudo mv n1k0-casperjs-5c80139 /usr/local/src/casperjs

Reťazec 5c80139 sa môže meniť podla aktuálneho commitu.

Symlink na program:

$ sudo ln -sf /usr/local/src/casperjs/bin/casperjs /usr/local/bin/casperjs

Kontrola inštalácie:

$ casperjs --version
1.1.0-beta3

Odstránenie stiahnutého archívu:

$ rm casperjs.tar.gz

Spustenie vlastného testu:

$ casper selftest

Test by v ideálnom prípade mal vypísať úspešné prebehnutie všetkých testov.

Ako sa s tým pracuje?

Dva problémy na úvod

Pre čo najstrmšiu krivku učenia som siahol po manuále. Žiaľ to nebolo celkom bezproblémové. Na začiatku som musel vyriešiť dva drobné problémy so samotným programom. Bezproblémovo mi totiž nefungovali ani len testy z dokumentácie. Teda tie na testovanie nejakej JS triedy áno, ale test s použitím prehliadača mi zlyhával aj keď k tomu nemal dôvod.

Nefunkčné odoslanie formulára

Takto sa mi javil problém na začiatku pri skúšaní testu s použitím prehliadača. Po čase strávenom skúšaním a objavovaním možností som prišiel na to, že síce sa mi podarilo formulár odoslať, ale dáta prichádzajú nie v najbližšom bloku then, ale až v druhom v poradí. Problém je pravdepodobne spôsobený nie celkom kompatibilnou konfiguráciou systému. Ostatným to pravdepodobne pôjde bez problémov. Taktiež som si všimol, že mi zlyháva vstavaný casperjs selftest na metóde fill (a iných).

Riešenie

V rozumnom čase sa mi nepodarilo zistiť kde problém vzniká, tak som sa zatiaľ uspokojil s dočasným riešením. Za metódu s odoslaním formulára stačí pridať prázdnu metódu:

casper.then(function(){});

Loalizácia google.fr

V tom istom príklade z dokumentácie mi test zlyhal aj z ďalšieho dôvodu. Google ma z testovanej stránky google.fr automaticky presmeroval na google.sk a teda nesedela lokalizácia hľadaného textu.

Riešenie

Stačí prepísať reťazec kontrolovaného titulku casperjs - Recherche Google na casperjs - Hľadať v Google. Skutočne drobnosť:)

Testovanie Nette Sanboxu

Pre účel demonštrovania spôsobu testovania som sa rozhodol použiť Nette Sandbox.

Skelet

Základný skelet testu obsahuje obdobne ako ukážkový test na začiatku iba načítanie stránky a kontrolu titulku.

casper.test.begin('Nette Sandbox', 5, function suite(test) {
    casper.start("http://sandbox/", function() {
        test.assertTitle("CONGRATULATIONS!", "Homepage title");
    });
    casper.run(function() {
        test.done();
    });
});

Odoslanie furmulára

Medzi metódy start a run vložíme kód na otestovanie prihlasovania:

casper.thenOpen("http://sandbox/sign/in", function() {
    this.echo('I\'m on ' + this.getCurrentUrl());
    test.assertTitle("SIGN IN", "Sign in title");
    test.assertExists('h1', "Title exists");
    test.assertSelectorHasText('h1', 'Sign in', "Title is correct");
    test.assertExists('form[action="/sign/in?do=signInForm-submit"]', "Login form is found");
    this.fill('form[action="/sign/in?do=signInForm-submit"]', {
        username: "roman",
        password: "H3sl0"
    }, true);
    this.capture('filled.png');
});

Tretím parametrom metódy fill je boolean hodnota popisujúca, či sa má formulár odoslať alebo nie.

Jednotlivé selektory sa dajú testovať ako na existenciu, tak aj na obsah. V kóde vyššie sú testy assertExists iba ilustračne, pretože sa so zvolenými selektormi v ďalšom kóde pracuje. V prípade ich neexistencie, by zlyhal test obsahu aj snaha o vyplnenie formulára.

1. Nette Sandbox test

Z ukážky vidno, že je metódou capture dostupná aj možnosť tvorby snímok obrazovky. Pre nastavenie rozmerov obrazovky je možné použiť metódu viewport alebo druhý parameter metódy capture.

Vyplnený formulár

Po osekaní zbytočností by kód vyzeral nasledovne:

casper.thenOpen("http://sandbox/sign/in", function() {
    test.assertTitle("SIGN IN", "Sign in title");
    test.assertSelectorHasText('h1', 'Sign in', "Title is correct");
    this.fill('form[action="/sign/in?do=signInForm-submit"]', {
        username: "roman",
        password: "H3sl0"
    }, true);
});

Komplexný test

Nasledovný kód pokrýva testom základnú funkcionalitu Nette Sandboxu. Do testov je asi vždy čo dopísať, ale základ tu je. Testy sú rozdelené do dvoch súborov podla presentrov.

Súbor nette-sandbox-homepage.js:

// Nette Sandbox test for CasperJS - Homepage presenter
casper.test.begin('Nette Sandbox - Homepage Presenter', 1, function suite(test) {
    // Homepage
    casper.start("http://sandbox/", function() {
        test.assertTitle("CONGRATULATIONS!", "Homepage title");
    });

    casper.run(function() {
        test.done();
    });
});

Súbor nette-sandbox-sign.js:

// Nette Sandbox test for CasperJS - Sign Presenter
casper.test.begin('Nette Sandbox - Sign Presenter', 9, function suite(test) {
    // Sign in
    casper.start("http://sandbox/sign/in", function() {
        test.assertTitle("SIGN IN", "Sign in title");
        test.assertSelectorHasText('h1', 'Sign in', "Title is correct");
        this.fill('form[action="/sign/in?do=signInForm-submit"]', {
            username: "roman",
            password: "H3sl0"
        }, true);
    });

    casper.then(function(){}); // my hack

    casper.then(function() {
        test.assertSelectorHasText('ul.error li', 'The username is incorrect.', "Incorrect username");
        this.fill('form[action="/sign/in?do=signInForm-submit"]', {
            username: "tester",
            password: "H3sl0"
        }, true);
    });

    casper.then(function(){}); // my hack

    casper.then(function() {
        test.assertSelectorHasText('ul.error li', 'The password is incorrect.', "Incorrect password");
        this.fill('form[action="/sign/in?do=signInForm-submit"]', {
            username: "tester",
            password: "Sup3r!H3sl0"
        }, true);
    });

    casper.then(function(){}); // my hack

    casper.then(function() {
        test.assertDoesntExist('ul.error li', "No error after login");
        test.assertUrlMatch(/^http:\/\/sandbox\/$/, 'Redirect after login');
    });

    // Sign out
    casper.thenOpen("http://sandbox/sign/out", function() {
        test.assertTitle("SIGN IN", "Title after logout");
        test.assertSelectorHasText('div.flash', 'You have been signed out.', "Logout flash message");
        test.assertUrlMatch(/^http:\/\/sandbox\/sign\/in\?_fid=\w{4}$/, 'Redirect after logout');
    });

    casper.run(function() {
        test.done();
    });
});

Komplexný Nette Sandbox test

Záver

CasperJS splnil moje očakávania. Až na zatiaľ nepochopenú chybu s nutnosťou jedného then navyše sa používa veľmi rýchle a pohodlne. Poskytuje rozsiahle možnosti nie iba testovania, ale aj automatického sťahovania a ovládania webových aplikácií.

Zdroje