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é testypredstavujú 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) testovanieje 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álvá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 pripraviliGitHub Starter Project. Ak nemáte zaújem programovať počas tutoriálu, môžete si stiahnuť finálny projektGitHub 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ží nahttp://localhost:8080. Po otvorení by ste mali vidieť niečo takéto:
Ď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ájdetepuppeteer_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. Spuppeteer.launch()na riadku 5 spúšťame novú inštanciuChromia, v argumentoch špecifikujemeheadless: 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 nahttp://localhost:8080.waitUntil: '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.jssú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 špecifikujemejest-puppeteer
preset, ktorý nám umožní použiť Jest s Pupeteer. Vglobals
deklarujeme premenné, ktoré budú dostupné vo všetkých testoch. A vtestMatch
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.jssú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
}
}
Vlaunch
objekte špecifikujeme argumenty pre inštanciuChromia, 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 dopuppeteer.launch(). Na riadku 3 špecifikujeme, či by mal Puppeteer spustiť prehliadač vheadless
móde alebo nie. A na riadku 4 mu hovoríme, aby bežal vslowMo
, č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. Vsrc/test/
nájdete súbor s menomfrontend.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:
Poďme zanalyzovať tento kód po jednotlivých riadkoch. Na prvom riadku nastavujemetimeout
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žívamebeforeAll, 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 naURL
, ktorú smešpecifikovali predtýmako globálnu premennú. Ale odkiaľ sme zobralipage
premennú? Tá je dostupná tiež vo všetkých testovacích súboroch vďakajest-puppeteer
preset. Na riadku 7 používamedescribe, 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žnicuexpect, ktorá je vstavaná vJest, 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živamepage.$()funkciu, kotrá nám umožňuje vybrať HTML element pomocou normálneho CSS selektoru, a nakoniec nám táto funkcia vrátiElementHandle, ktorú neskôr použijeme na to, aby sme získali innerHTML tohto elementu. Na riadku 3 potom používamepage.evaluate(), ktorá vyhodnotí funkciu v kontexte stránky, a tým pádom získame prístup k innerHTML našeElementHandle.
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:
Premenujteform.test.js.example
vsrc/test
naform.test.js
a 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ímeje taká, že klikneme na Login link v navigácii. Používame na topage.click()funkciu, ktorá berie jeden argument CSS selektor. Keďže sme navigovali na inú URL používamepage.waitForSelector(), na to aby sme počkali, kým DOM zobrazí náš formulár, takžebudeme s ním môcť interagovať. Potom používamepage.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 pomocoupage.$eval().
Ak teraz zadátenpm 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í.
Premenujtescreenshots.test.js.example
vsrc/test
nascreenshots.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 spage.setViewport()a potom spravíme screenshot s funkcioupage.screenshot(), ktorej poskytujeme nejaké argumenty, aby sme špecifikovali kde a v akom formáte uložiť screenshot.
Po tom, čo zadátenpm run test,
mali by ste byť schopnínájsť obrázok s názvom home.jpg vtest/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 zpuppeteer/DeviceDescriptors
. Potom vyberáme IPhone X na riadku 3 z objektu devices. Na ďalšiom riadku emulujeme toto zariadenie spage.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 premenujtegeneral.test.js.example
vsrc/test
nageneral.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 nastavujemepage.setRequestInterception()natrue
, č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 spage.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 nastavujemepage.setRequestInterception()nafalse
, č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 sbrowser.on('targetcreated'), či nový target (page
) je alebo nie je vytvorený. Znova máme prístup k globálnej premennejbrowser
, vďakajest-puppeteerpreset. 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 vjest-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žijemejest-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ť sjestPuppeteer.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ámradiodporučil jednu Chrome extension, ktorá sa vám môže zísť, keď píšete testy s Puppeteer. Volá saPuppeteer Recordera umožňuje vám náhravať vaše interakcie s prehliadačoma 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.