testing

Ako funguje End-to-End testovanie?

testing

Ako funguje End-to-End testovanie?

Jest a Puppeteer sú veľmi populárne Javascriptové frameworky, ktoré sa často využívajú na End-to-End testovanie. Čo je to a aké sú ďalšie známe typy testovania?

 

Testovanie je nevyhnutnou súčasťou procesu vyvíjania softvéru. Môže drasticky znížiť cenu vášho projektu a zvýšiť produktivitu vášho vývojárskeho tímu. Dnes rozlišujeme tieto tri hlavné druhy testov:
 

  • Unit testy – S unit testami testujeme malé izolované časti nášho kódu.
  • Integračné testy predstavujú spôsob testovania, v ktorom kombinujeme jednotlivé časti nášho kódu, ktoré potom testujeme spoločne ako jednu ucelenú skupinu.
  • End-to-End (E2E) testovanie je definované ako testovanie kompletnej funkcionality našej aplikácie.

Pri End-to-End testovaní sa veľmi často využívajú už spomínané nástroje Jest a Puppeteer:

  • Jest: Je plne vybavený testovací framework, ktorý je vyvíjaný Facebookom. Nepotrebuje v podstate žiadnu konfiguráciu, čiže funguje takmer ihneď po inštalácii.
  • Puppeteer: Knižnica pre Node.js vytvorená Googlom, ktorá poskytuje praktickú API, pomocou ktorej môžeme ovládať Headless Chrome.

Náš tutoriál vás naučí:

1. Ako integrovať Puppeteer a Jest

2. Testovanie formulárov

3. Testovanie frontendu

4. Ako urobiť screenshot

5. Emulovanie mobilných zariadení

6. Ako zachytiť requesty

7. Ako targetovať novo otvorenú stránku v headless prehliadači

8. A ako debugovať vaše testy

Project Setup

Ako prvý krok si budete musieť stiahnuť projekt, ktorý sme pripravili GitHub Starter Project. Ak nemáte zaújem programovať počas tutoriálu, môžete si stiahnuť finálny projekt GitHub Final Project.

 

Postup po stiahnutí projektu:

1.) cd do repozitára

cd /E2E_Testing_with_Puppeteer_Starter

2.) install dependencies

npm install

3.) rozbehnite projekt

npm run dev-server

 

Výborne teraz naša aplikácia beží na http://localhost:8080. Po otvorení by ste mali vidieť niečo takéto:

testing puppeteer jest

 

Ďalšia vec, ktorú musíme spraviť, je nainštalovať všetky nevyhnutné nástroje.

npm install puppeteer jest jest-puppeteer

Taktiež budeme potrebovať nainštalovať jest-cli, aby sme mohli spúšťať jeden test separátne od ostatných.

npm install -g jest-cli

Prvý pohľad na Puppeteer

Najprv spustíme Puppeteer samostatne, aby ste mohli vidieť ako funguje bez Jest. V root directory projektu nájdete puppeteer_firts_try.js súbor, ktorý obsahuje základné inštrukcie pre Puppeteer.

Do terminálu zadajte:

node puppeteer_firts_try.js

V puppeteer_firts_try.js:

const puppeteer = require('puppeteer');
(async function main(){
  try {
    const browser = await puppeteer.launch({ headless: false });
    const page = await browser.newPage();
    await page.goto('http://localhost:8080', {waitUntil: 'domcontentloaded'});
    await new Promise(resolve => setTimeout(resolve, 5000));
    console.log('done'); await browser.close();
  }
  catch (e) {
    console.log('Err', e);
  }
})();

Ako možete už pravdepodobne vidieť Puppeteer spolieha výhradne na promises, takže ho budeme vždy používať s async/await. S puppeteer.launch() na riadku 5 spúšťame novú inštanciu Chromia, v argumentoch špecifikujeme headless: false, čo znamená, že prehliadač sa nespustí v headless móde ( v podstate bez grafického používateľského rozhrania). Na ďalšiom riadku otvárame novú stránku a potom na riadku 7 navigujeme na http://localhost:8080waitUntil: 'domcontentloaded' argument na riadku 7 špecifikuje, že náš kód bude čakať až pokiaľ DOM content nebude načítaný. Riadok 9 len spôsobí, že aplikácia sa zastaví na 5 sekúnd, takže budete môcť vidieť, čo sa deje. A na riadku 11 zatvárame prehliadač.

Integrácia Puppeteer s Jest

Teraz zintegrujeme Puppeteer s Jest. Ale prečo to vlastne potrebujeme ? Robíme to, preto že Pupeteer sám o sebe nie je testovací framework, je to nástroj, ktorý nám umožňuje kontrolovať Headless Chrome. Takže aby sme si uľahčili našu prácu, skombinujeme Puppeteer s Jest, ktorý nám poskytuje veľmi užitočné testovacie utilities.

 

Jest Konfigurácia

Vytvorte jest.config.js súbor v root directory projektu a vložte tam tento kód:


module.exports = {
    preset: "jest-puppeteer",
    globals: {
      URL: "http://localhost:8080"
    },
    testMatch: [
      "**/test/**/*.test.js"
    ],
    verbose: true
}

Na riadku 2 špecifikujeme jest-puppeteer preset, ktorý nám umožní použiť Jest s Pupeteer. V globals deklarujeme premenné, ktoré budú dostupné vo všetkých testoch. A v  testMatch jednoducho hovoríme, v ktorých zložkách má Jest hľadať súbory.

 

Konfigurácia pre for jest-puppeteer preset

Vytvorte jest-puppeteer.config.js súbor v root directory nášho projektu a použite tento kód:


module.exports = {
    launch: {
        headless: process.env.HEADLESS !== 'false',
        slowMo: process.env.SLOWMO ? process.env.SLOWMO : 0,
        devtools: true
    }
}

launch objekte špecifikujeme argumenty pre inštanciu Chromia, ktorá bude spustená predtým ako pobežia naše testy a bude dostupná vo všetkých našich testovacích súboroch, takže tu môžete zadať všetky argumenty, ktoré by ste normálne dali do puppeteer.launch(). Na riadku 3 špecifikujeme, či by mal Puppeteer spustiť prehliadač v  headless móde alebo nie. A na riadku 4 mu hovoríme, aby bežal v slowMo, čo spomalý Puppeteer o milisekundy, ktoré špecifikujeme, takže budeme môcť pozorovať čo vlastne robí. Obidve možnosti, ktoré tu definujeme sú skvelé pre debugovanie.

Písanie našich testov

Testing Frontend

Keďže máme naše testovacie prostredie pripravené, môžeme začať s písaním prvých testov. Bude lepšie, ak začneme s niečím jednoduchým. V src/test/ nájdete súbor s menom frontend.test.js , do ktorého budete potrebovať vložiť tento kód :


const timeout = process.env.SLOWMO ? 30000 : 10000;

beforeAll(async () => {
    await page.goto(URL, {waitUntil: 'domcontentloaded'});
});

describe('Test header and title of the page', () => {
    test('Title of the page', async () => {
        const title = await page.title();
        expect(title).toBe('E2E Puppeteer Testing');
        
    }, timeout);
});

A teraz tak môžete zadať toto do vášho terminálu:

npm run test

A mali by ste vidieť niečo takéto:

touch4it testovanie

 

Poďme zanalyzovať tento kód po jednotlivých riadkoch. Na prvom riadku nastavujeme timeout premennú, ktorú neskôr používame na to, aby sme špecifikovali timeout pre naše testy (nezabudnite, že špecifikujeme tento timeout v milisekundách). Ako môžete vidieť, ak Puppeteer beží v slowMo, zvýšime náš timeout z 10000 ms na 30000ms. Toto zaistí, že naše testy nezlyhajú kvôli timeoutu. Na riadku 3 používame beforeAll, táto funkcia vykoná nejaký kód predtým ako pobežia všetky testy v tomto súbore. Tejto funkcii dávame ako parameter async callback, v ktoróm navigujeme na URL , ktorú sme špecifikovali predtým ako globálnu premennú. Ale odkiaľ sme zobrali page premennú? Tá je dostupná tiež vo všetkých testovacích súboroch vďaka jest-puppeteer preset. Na riadku 7 používame describe, ktoré nám umožňuje zoskupovať testy, a v ňom už potom píšeme naše samotné testy. Tento test je celkom jednoduchý, na riadku 9 získavame title stránky a potom používame assertion knižnicu expect, ktorá je vstavaná v Jest, na to aby sme overili, či sme dostali správny výsledok.

 

Skúsme teraz pridať ďalší test do tochto súboru. Vložte tento kód hneď pod náš prvý test v describe bloku:


test('Header of the page', async () => {
        const h1Handle = await page.$('.learn_header');
        const html = await page.evaluate(h1Handle => h1Handle.innerHTML, h1Handle);

        expect(html).toBe("What will you learn");
    }, timeout);

Na druhom riadku použivame page.$() funkciu, kotrá nám umožňuje vybrať HTML element pomocou normálneho CSS selektoru, a nakoniec nám táto funkcia vráti ElementHandle , ktorú neskôr použijeme na to, aby sme získali innerHTML tohto elementu. Na riadku 3 potom používame page.evaluate(), ktorá vyhodnotí funkciu v kontexte stránky, a tým pádom získame prístup k innerHTML naše ElementHandle.

Form Tests

Teraz, keď už máme nejaké základné testy za nami, skúsime napísať test pre jednoduchý formulár, ktorý sme pripravili:

touch4it testing form

 

Premenujte form.test.js.example v src/test na form.test.jsa vložte tento kód do describe bloku, ktorý tam už je:


test('Submit form with valid data', async () => {
        await page.click('[href="/login"]');
        
        await page.waitForSelector('form');
        await page.type('#name', 'Rick');

        await page.type('#password','szechuanSauce');
        await page.type('#repeat_password','szechuanSauce');

        await page.click('[type="submit"]');
        await page.waitForSelector('.success');
        const html = await page.$eval('.success', el => el.innerHTML);

        expect(html).toBe('Successfully signed up!');
    }, timeout);

 

Prvá vec ktorú tu robíme je taká, že klikneme na Login link v navigácii. Používame na to page.click() funkciu, ktorá berie jeden argument CSS selektor. Keďže sme navigovali na inú URL používame page.waitForSelector(), na to aby sme počkali, kým DOM zobrazí náš formulár, takže budeme s ním môcť interagovať. Potom používame page.type() metódu, aby sme vyplnil náš formulár. Táto metóda berie dva argumenty CSS selektor a text, ktorý chceme napísať. Potom klikáme na submit button a čakáme kým sa objaví správa o úspešnom podaní formulára, ktorú získame pomocou page.$eval().

Ak teraz zadáte npm run test, mali by ste vidieť tri úspešne testy.

Branie screenshotov na mobilných a desktopových zariadeniach

Formulár a frontend je otestovaný, takže môžeme obrátiť našu pozornosť na branie screenshotov a emulovanie mobilných zariadení.

Premenujte screenshots.test.js.example v src/test na screenshots.test.js a vložte tento kód do describe bloku, ktorý tam už je:


    test('Take screenshot of home page', async () => {
        await page.setViewport({ width: 1920, height: 1080 });
        await page.screenshot({
            path: './src/test/screenshots/home.jpg',
            fullpage: true,
            type: 'jpeg'
        });
    }, timeout);

V tomto kóde najprv nastavíme viewport našej stránky s page.setViewport() a potom spravíme screenshot s funkciou page.screenshot(), ktorej poskytujeme nejaké argumenty, aby sme špecifikovali kde a v akom formáte uložiť screenshot.

 

Po tom, čo zadáte npm run test, mali by ste byť schopní nájsť obrázok s názvom home.jpg v  test/screenshots zložke. Teraz skúsme spraviť screenshot, počas toho ako emulujeme mobilné zariadenie. Najprv pridajte tento kód na vrch nášho súboru:

const devices = require('puppeteer/DeviceDescriptors');

 

A potom pridajte tento test do describe bloku:


test('Emulate Mobile Device And take screenshot', async () => {
    await page.goto(`${URL}/login`, {waitUntil: 'domcontentloaded'})
    const iPhonex = devices['iPhone X'];
    await page.emulate(iPhonex);
    await page.setViewport({ width: 375, height: 812, isMobile: true});
    await page.screenshot({
        path: './src/test/screenshots/home-mobile.jpg',
        fullpage: true,
        type: 'jpeg'
    });
}, timeout);

Tento kód je podobný tomu predchádzajúcemu, rozdiel je v tom, že teraz importujeme zariadenia z puppeteer/DeviceDescriptors. Potom vyberáme IPhone X na riadku 3 z objektu devices. Na ďalšiom riadku emulujeme toto zariadenie s page.emulate(). A potom jednoducho urobíme screenshot presne tým istým spôsobom ako v predchádzajúcom teste.

Ako zachytiť request a targetovanie novo otvorených stránok

Teraz sa pozrieme na trochu pokročilejšie vlastnosti, ktoré Puppeteer poskytuje, ako je napríklad zachytávanie requestov. A taktiež vám ukážeme, ako targetovať novo otvorenú stránku v headless prehliadači.

Na úvod premenujte general.test.js.example v src/test na general.test.js a skopírujte tam tento kód:


   test('Intercept Request', async () => {
        await page.setRequestInterception(true);
        page.on('request', interceptedRequest => {
            if (interceptedRequest.url().endsWith('.png')) {
                interceptedRequest.abort();
            } else {
                interceptedRequest.continue();
            }
        });
        await page.reload({waitUntil: 'networkidle0'});
        // await jestPuppeteer.debug();
        await page.setRequestInterception(false);
    }, timeout);

 

Tu na prvom riadku nastavujeme page.setRequestInterception() na true, čo nám umožňuje zachytiť každý odchádzajúci request. Na riadkoch 3-9 hovoríme aby Puppeteer prerušil každú odchádzajúci request, ktorý konči s '.png'. Takže vďaka tomuto kódu sa na stránke nenačíta obrázok, ktorý je na domovskej stránke našej aplikácie, vlastne sa toto stane, až po tom čo znovu načítame stránku, pretože obrázok už bol načítaný, predtým ako sme nastavili zachytávanie requestov. Potom na riadku 10 znovu načítame stránku s page.reload(), takže budeme môcť vidieť, že sa obrázok nezobrazuje. Ale ako vlastne, keďže Puppeteer testy sú tak rýchle ? Na to využijeme kód na ďalšiom riadku, ktorý je momentálne zakomentovaný, ale k tomuto sa vrátim až neskôr v skecii o debugovaní. A na riadku 12 nastavujeme page.setRequestInterception() na false, čo je veľmi dôležité! Pretože kebyže to neurobíme tak by zachytávanie requestov ostalo zapnuté po zvyšok nášho testovania a to môže spôsobiť veľa problémov, a byť veľmi ťažké na debugovanie.

 

Pridajme teraz náš posledný test, s ktorým vám ukážeme ako môžete targetovať novo otvorené stránky v headless prehliadači. Pridajte tento test do describe bloku:  


   test('Target newly opened page', async () => {
        const newPagePromise = new Promise(resolve => browser.once('targetcreated', target => resolve(target.page())));
        await page.click('.repo_link');

        const newPage = await newPagePromise;
        const title = await newPage.title();
        await newPage.close();

        expect(title).toBe('GitHub - Zovi343/E2E_Testing_with_Puppeteer_Final');
    }, timeout);

 

Na riadku 2 vytvárame nový Promise, v ktorom počúvame s  browser.on('targetcreated'), či nový target (page) je alebo nie je vytvorený. Znova máme prístup k globálnej premennej browser, vďaka jest-puppeteer preset. Potom klikáme na link na našej domovskej stránke, ktorý otvára novú stránku, konkrétne: GitHub Starter Project. Na 7 riadku očakávame Promise, ktorý sme vytvorili na riadku 2 a tento Promise vracia novo otvorenú stránku. Takže v závere sme schopní získať title stránky a urobiť naše assertions.

Debugovanie vašich testov

Veľakrát budú vaše testy zlyhávať a často je veľmi ťažké takéto testy debugovať a zistiť, čo sa vlastne deje len z terminálu. To je hlavný dôvod, prečo vám chceme ukázať rôzne metódy, pomocou ktorých môžete debugovať vaše testy:

 

Headless a SlowMo argumenty

Takže pre debugovanie budete chcieť spustiť Puppeteer v headless móde a taktiež ho spomaliť, takže budete schopní vidieť, čo sa vlastne deje. Keďže sme nastavili tieto dve možnosti v jest-puppeteer.config.js , všetko čo teraz musíme spraviť je poskytnúť dve enviromentálne premenné, keď spúšťate testy z terminálu.

Do terminálu zadajte:

HEADLESS="false" SLOWMO=100 npm run test

 

Running single Test Seperately

Niekedy potrebujete rozbehnúť iba jeden test bez toho, aby sa spúšťali ďalšie. Aby sme mohli toto urobiť použijeme jest-cli, ktorú sme nainštalovali na začiatku. Vráťme sa teraz naspäť k zachytávaní requestov, pretože to sme predtým neboli celkom schopní vidieť a pozorovať.

Do terminálu zadajte:

HEADLESS="false" SLOWMO=100 jest -t 'Intercept Request'

Síce sme už teraz mohli vidieť, že sa obrázok nezobrazil (presne ako sme predpokladali), avšak bolo to stále celkom rýchle, nie? Poďme to napraviť s jestPuppeteer.debug().

 

jestPuppeteer.debug()

jestPuppeteer.debug() zastaví naše testy, aby sme mohli zistiť, čo sa deje v prehliadači. Aby ste znovu spustili testy, musíte stlačiť Enter. Takže teraz môžete odkomentovať riadok 11 z testu o zachytávaní requestov a zadať predchádzajúci príkaz do terminálu. A budete môcť jasne vidieť, že obrázok nie je zobrazený na domovskej stránke, pretože request preň bola zachytená a prerušená.

Bonus Puppeteer Recorder

Nakoniec by sme vám radi odporučil jednu Chrome extension, ktorá sa vám môže zísť, keď píšete testy s Puppeteer. Volá sa Puppeteer Recorder a umožňuje vám náhravať vaše interakcie s prehliadačom a následne vygenerovať z toho Puppeteer script.

Záver

V tomto článku sme sa zaoberali dvomi veľmi populárnymi frameworkami - Jest a Puppeteer. Naučili sme sa, že keď tieto dva nástroje skombinujeme, získame veľmi robustné testovacie prostredie. Naučili ste sa ako integrovať Puppeteer a Jest, ako písať testy pre rôzne príležitosti, ako debugovať vaše testy a mnoho ďalšieho.