Išsamus vadovas, kaip sukurti patikimą nuolatinės integracijos (CI) konvejerį JavaScript projektams. Geriausios automatizuoto testavimo praktikos su GitHub Actions, GitLab CI ir Jenkins.
JavaScript testavimo automatizavimas: išsamus nuolatinės integracijos diegimo vadovas
Įsivaizduokite tokį scenarijų: vėlyvas darbo dienos metas. Jūs ką tik įkėlėte, jūsų manymu, nedidelį klaidos pataisymą į pagrindinę šaką. Po kelių akimirkų pradeda plūsti pranešimai. Klientų aptarnavimo kanalai užtvindyti pranešimais apie kritinę, nesusijusią funkciją, kuri visiškai sugedo. Prasideda įtemptas, didelės įtampos reikalaujantis skubus taisymas. Ši situacija, pernelyg dažna kūrimo komandoms visame pasaulyje, yra būtent tai, kam skirta patikima automatizuoto testavimo ir nuolatinės integracijos (CI) strategija, kad tokių situacijų išvengtumėte.
Šiandieniniame sparčiai kintančiame, pasauliniame programinės įrangos kūrimo pasaulyje greitis ir kokybė nėra vienas kitam prieštaraujantys dalykai; jie yra vienas nuo kito priklausomi. Gebėjimas greitai pristatyti patikimas funkcijas yra didelis konkurencinis pranašumas. Būtent čia automatizuoto JavaScript testavimo ir nuolatinės integracijos konvejerių sinergija tampa šiuolaikinių, našiai dirbančių inžinierių komandų kertiniu akmeniu. Šis vadovas bus jūsų išsamus planas, padėsiantis suprasti, įdiegti ir optimizuoti CI sąranką bet kokiam JavaScript projektui, skirtas pasaulinei auditorijai, kurią sudaro programuotojai, komandų vadovai ir DevOps inžinieriai.
„Kodėl“: CI pagrindinių principų supratimas
Prieš pradedant gilintis į konfigūracijos failus ir konkrečius įrankius, labai svarbu suprasti nuolatinės integracijos filosofiją. CI – tai ne tik scenarijų vykdymas nuotoliniame serveryje; tai kūrimo praktika ir kultūrinis pokytis, kuris iš esmės veikia komandų bendradarbiavimą ir programinės įrangos pristatymą.
Kas yra nuolatinė integracija (CI)?
Nuolatinė integracija yra praktika, kai visi programuotojai dažnai sujungia savo darbinio kodo kopijas į bendrą pagrindinę šaką – dažnai kelis kartus per dieną. Kiekvienas sujungimas, arba „integracija“, yra automatiškai patikrinamas sukuriant versiją (angl. build) ir atliekant seriją automatizuotų testų. Pagrindinis tikslas – kuo anksčiau aptikti integracijos klaidas.
Galvokite apie tai kaip apie budrų, automatizuotą komandos narį, kuris nuolat tikrina, ar nauji kodo pakeitimai nesugadina esamos programos. Šis tiesioginis grįžtamojo ryšio ciklas yra CI esmė ir jos galingiausia savybė.
Pagrindiniai CI pranašumai
- Ankstyvas klaidų aptikimas ir greitesnis grįžtamasis ryšys: Testuodami kiekvieną pakeitimą, klaidas pagaunate per minutes, o ne dienas ar savaites. Tai drastiškai sumažina jų taisymui reikalingą laiką ir išlaidas. Programuotojai gauna neatidėliotiną grįžtamąjį ryšį apie savo pakeitimus, leisdami jiems greitai ir užtikrintai atlikti pakeitimus.
- Pagerinta kodo kokybė: CI konvejeris veikia kaip kokybės vartai. Jis gali užtikrinti kodavimo standartų laikymąsi su linteriais, tikrinti tipų klaidas ir užtikrinti, kad naujas kodas būtų padengtas testais. Laikui bėgant, tai sistemingai kelia visos kodo bazės kokybę ir palaikomumą.
- Sumažėję sujungimo konfliktai: Dažnai integruodami mažas kodo dalis, programuotojai rečiau susiduria su dideliais, sudėtingais sujungimo konfliktais („sujungimo pragaru“). Tai sutaupo daug laiko ir sumažina klaidų atsiradimo riziką rankinių sujungimų metu.
- Padidėjęs programuotojų produktyvumas ir pasitikėjimas: Automatizavimas atlaisvina programuotojus nuo varginančių, rankinių testavimo ir diegimo procesų. Žinojimas, kad išsamus testų rinkinys saugo kodo bazę, suteikia programuotojams pasitikėjimo atlikti refaktorizavimą, diegti naujoves ir pristatyti funkcijas nebijant sukelti regresijų.
- Vienintelis tiesos šaltinis: CI serveris tampa galutiniu „žalios“ arba „raudonos“ versijos šaltiniu. Visi komandos nariai, nepriklausomai nuo jų geografinės padėties ar laiko juostos, turi aiškų matomumą apie programos būklę bet kuriuo momentu.
„Ką“: JavaScript testavimo aplinka
Sėkmingas CI konvejeris yra toks geras, kokie geri yra jo vykdomi testai. Bendra ir efektyvi strategija testams struktūrizuoti yra „Testavimo piramidė“. Ji vaizduoja sveiką skirtingų tipų testų pusiausvyrą.
Įsivaizduokite piramidę:
- Pagrindas (didžiausia dalis): Vienetų testai. Jie yra greiti, gausūs ir tikrina mažiausias jūsų kodo dalis izoliuotai.
- Vidurys: Integraciniai testai. Jie patikrina, ar keli vienetai veikia kartu, kaip tikėtasi.
- Viršūnė (mažiausia dalis): End-to-End (E2E) testai. Tai lėtesni, sudėtingesni testai, kurie imituoja realaus vartotojo kelionę per visą jūsų programą.
Vienetų testai: pamatas
Vienetų testai sutelkti į vieną funkciją, metodą ar komponentą. Jie yra izoliuoti nuo likusios programos dalies, dažnai naudojant „mocks“ ar „stubs“ priklausomybėms imituoti. Jų tikslas – patikrinti, ar konkreti logikos dalis veikia teisingai su įvairiais įvesties duomenimis.
- Paskirtis: Patikrinti individualius logikos vienetus.
- Greitis: Itin greiti (milisekundės per testą).
- Pagrindiniai įrankiai:
- Jest: Populiarus, „viskas viename“ testavimo karkasas su integruotomis tvirtinimo bibliotekomis, imitavimo galimybėmis ir kodo padengimo įrankiais. Palaikomas „Meta“.
- Vitest: Modernus, žaibiškai greitas testavimo karkasas, sukurtas sklandžiam darbui su „Vite“ kūrimo įrankiu, siūlantis su „Jest“ suderinamą API.
- Mocha: Labai lankstus ir brandus testavimo karkasas, suteikiantis pagrindinę testų struktūrą. Dažnai naudojamas kartu su tvirtinimo biblioteka, tokia kaip „Chai“.
Integraciniai testai: jungiamasis audinys
Integraciniai testai yra vienu žingsniu aukščiau nei vienetų testai. Jie tikrina, kaip bendradarbiauja keli vienetai. Pavyzdžiui, „frontend“ programoje integracinis testas gali atvaizduoti komponentą, kuriame yra keli vaikiniai komponentai, ir patikrinti, ar jie teisingai sąveikauja, kai vartotojas paspaudžia mygtuką.
- Paskirtis: Patikrinti sąveiką tarp modulių ar komponentų.
- Greitis: Lėtesni nei vienetų testai, bet greitesni nei E2E testai.
- Pagrindiniai įrankiai:
- React Testing Library: Ne testų paleidimo įrankis, o pagalbinių funkcijų rinkinys, skatinantis testuoti programos elgseną, o ne įgyvendinimo detales. Veikia su tokiais paleidimo įrankiais kaip „Jest“ ar „Vitest“.
- Supertest: Populiari biblioteka Node.js HTTP serveriams testuoti, todėl puikiai tinka API integraciniams testams.
End-to-End (E2E) testai: vartotojo perspektyva
E2E testai automatizuoja realią naršyklę, kad imituotų visą vartotojo darbo eigą. El. prekybos svetainėje E2E testas galėtų apimti apsilankymą pagrindiniame puslapyje, produkto paiešką, jo įdėjimą į krepšelį ir perėjimą į atsiskaitymo puslapį. Šie testai suteikia didžiausią pasitikėjimą, kad jūsų programa veikia kaip visuma.
- Paskirtis: Patikrinti visas vartotojo darbo eigas nuo pradžios iki pabaigos.
- Greitis: Lėčiausias ir trapiausias testų tipas.
- Pagrindiniai įrankiai:
- Cypress: Modernus, „viskas viename“ E2E testavimo karkasas, žinomas dėl puikios programuotojo patirties, interaktyvaus testų paleidimo įrankio ir patikimumo.
- Playwright: Galingas „Microsoft“ karkasas, leidžiantis automatizuoti skirtingas naršykles (Chromium, Firefox, WebKit) su viena API. Jis žinomas dėl savo greičio ir pažangių funkcijų.
- Selenium WebDriver: Ilgametis naršyklių automatizavimo standartas, palaikantis daugybę kalbų ir naršyklių. Jis siūlo maksimalų lankstumą, tačiau jo sąranka gali būti sudėtingesnė.
Statinė analizė: pirmoji gynybos linija
Prieš pradedant vykdyti bet kokius testus, statinės analizės įrankiai gali pagauti įprastas klaidas ir užtikrinti kodo stilių. Tai visada turėtų būti pirmasis etapas jūsų CI konvejeryje.
- ESLint: Labai konfigūruojamas linteris, skirtas rasti ir taisyti problemas jūsų JavaScript kode, nuo galimų klaidų iki stiliaus pažeidimų.
- Prettier: Nuomonę turintis kodo formatuotojas, užtikrinantis nuoseklų kodo stilių visoje komandoje, pašalinantis ginčus dėl formatavimo.
- TypeScript: Pridėdamas statinius tipus į JavaScript, TypeScript gali sugauti visą klasę klaidų kompiliavimo metu, gerokai anksčiau, nei kodas yra vykdomas.
„Kaip“: Jūsų CI konvejerio kūrimas – praktinis vadovas
Dabar pereikime prie praktikos. Mes sutelksime dėmesį į CI konvejerio kūrimą naudojant GitHub Actions – vieną populiariausių ir prieinamiausių CI/CD platformų pasaulyje. Tačiau koncepcijos yra tiesiogiai pritaikomos ir kitoms sistemoms, tokioms kaip GitLab CI/CD ar Jenkins.
Būtinos sąlygos
- JavaScript projektas (Node.js, React, Vue ir kt.).
- Įdiegtas testavimo karkasas (naudosime Jest vienetų testams ir Cypress E2E testams).
- Jūsų kodas patalpintas GitHub.
- Scenarijai apibrėžti jūsų `package.json` faile.
Tipiškas `package.json` gali turėti tokius scenarijus:
`package.json` scenarijų pavyzdys:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"lint": "eslint .",
"test": "jest",
"test:ci": "jest --ci --coverage",
"cypress:open": "cypress open",
"cypress:run": "cypress run"
}
1 žingsnis: Pirmojo GitHub Actions darbo srauto nustatymas
GitHub Actions yra apibrėžiami YAML failuose, esančiuose jūsų repozitorijos `.github/workflows/` kataloge. Sukurkime failą pavadinimu `ci.yml`.
Failas: `.github/workflows/ci.yml`
Šis darbo srautas vykdys mūsų linterius ir vienetų testus kiekvieną kartą, kai kodas bus įkeltas į `main` šaką ir kiekvienam pull request, nukreiptam į `main`.
# Tai yra jūsų darbo srauto pavadinimas
name: JavaScript CI
# Ši sekcija apibrėžia, kada darbo srautas vykdomas
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# Ši sekcija apibrėžia užduotis, kurios bus vykdomos
jobs:
# Mes apibrėžiame vieną užduotį pavadinimu 'test'
test:
# Virtualios mašinos tipas, kurioje bus vykdoma užduotis
runs-on: ubuntu-latest
# Žingsniai apibrėžia užduočių seką, kuri bus vykdoma
steps:
# 1 žingsnis: Atsisiųsti jūsų repozitorijos kodą
- name: Checkout code
uses: actions/checkout@v4
# 2 žingsnis: Nustatyti teisingą Node.js versiją
- name: Use Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm' # Tai įjungia npm priklausomybių podėliavimą
# 3 žingsnis: Įdiegti projekto priklausomybes
- name: Install dependencies
run: npm ci
# 4 žingsnis: Paleisti linterį kodo stiliaus patikrinimui
- name: Run linter
run: npm run lint
# 5 žingsnis: Paleisti vienetų ir integracinius testus
- name: Run unit tests
run: npm run test:ci
Kai įkelsite šį failą į GitHub, jūsų CI konvejeris bus aktyvus! Eikite į „Actions“ skirtuką savo GitHub repozitorijoje, kad pamatytumėte, kaip jis veikia.
2 žingsnis: End-to-End testų integravimas su Cypress
E2E testai yra sudėtingesni. Jiems reikalingas veikiantis programos serveris ir naršyklė. Mes galime išplėsti savo darbo srautą, kad tai apdorotume. Sukurkime atskirą užduotį E2E testams, kad jie galėtų veikti lygiagrečiai su mūsų vienetų testais, taip pagreitinant bendrą procesą.
Naudosime oficialų `cypress-io/github-action`, kuris supaprastina daugelį sąrankos žingsnių.
Atnaujintas failas: `.github/workflows/ci.yml`
name: JavaScript CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
# Vienetų testų užduotis lieka ta pati
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run test:ci
# Pridedame naują, lygiagrečią užduotį E2E testams
e2e-tests:
runs-on: ubuntu-latest
# Ši užduotis turėtų būti vykdoma tik sėkmingai pabaigus unit-tests užduotį
needs: unit-tests
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
# Naudoti oficialų Cypress veiksmą
- name: Cypress run
uses: cypress-io/github-action@v6
with:
# Reikia sukurti programos versiją prieš vykdant E2E testus
build: npm run build
# Komanda vietiniam serveriui paleisti
start: npm start
# Naršyklė, kurią naudoti testams
browser: chrome
# Laukite, kol serveris bus pasiekiamas šiuo URL
wait-on: 'http://localhost:3000'
Ši sąranka sukuria dvi užduotis. `e2e-tests` užduotis priklauso nuo `unit-tests` užduoties (`needs`), o tai reiškia, kad ji prasidės tik sėkmingai užbaigus pirmąją užduotį. Tai sukuria nuoseklų konvejerį, užtikrinantį pagrindinę kodo kokybę prieš vykdant lėtesnius, brangesnius E2E testus.
Alternatyvios CI/CD platformos: pasaulinė perspektyva
Nors GitHub Actions yra puikus pasirinkimas, daugelis organizacijų visame pasaulyje naudoja kitas galingas platformas. Pagrindinės koncepcijos yra universalios.
GitLab CI/CD
GitLab turi giliai integruotą ir galingą CI/CD sprendimą. Konfigūracija atliekama per `.gitlab-ci.yml` failą jūsų repozitorijos šakniniame kataloge.
Supaprastintas `.gitlab-ci.yml` pavyzdys:
image: node:20
cache:
paths:
- node_modules/
stages:
- setup
- test
install_dependencies:
stage: setup
script:
- npm ci
run_unit_tests:
stage: test
script:
- npm run test:ci
run_linter:
stage: test
script:
- npm run lint
Jenkins
Jenkins yra labai išplečiamas, savarankiškai talpinamas automatizavimo serveris. Tai populiarus pasirinkimas įmonių aplinkose, kurioms reikalinga maksimali kontrolė ir pritaikymas. Jenkins konvejeriai paprastai apibrėžiami `Jenkinsfile` faile.
Supaprastintas deklaratyvus `Jenkinsfile` pavyzdys:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm run lint'
sh 'npm run test:ci'
}
}
}
}
Pažangios CI strategijos ir geriausios praktikos
Kai turite veikiantį pagrindinį konvejerį, galite jį optimizuoti greičiui ir efektyvumui, o tai ypač svarbu didelėms, paskirstytoms komandoms.
Lygiagretinimas ir podėliavimas (angl. caching)
Lygiagretinimas: Dideliems testų rinkiniams visų testų vykdymas nuosekliai gali užtrukti ilgai. Dauguma E2E testavimo įrankių ir kai kurie vienetų testų paleidimo įrankiai palaiko lygiagretinimą. Tai apima jūsų testų rinkinio padalijimą į kelias virtualias mašinas, kurios veikia vienu metu. Tokios paslaugos kaip „Cypress Dashboard“ ar integruotos funkcijos CI platformose gali tai valdyti, drastiškai sumažindamos bendrą testavimo laiką.
Podėliavimas: Kiekvieną kartą vykdant CI iš naujo diegti `node_modules` yra laiko švaistymas. Visos pagrindinės CI platformos suteikia mechanizmą šioms priklausomybėms saugoti podėlyje. Kaip parodyta mūsų GitHub Actions pavyzdyje (`cache: 'npm'`), pirmasis vykdymas bus lėtas, tačiau vėlesni bus žymiai greitesni, nes jie galės atkurti podėlį, užuot viską atsisiuntę iš naujo.
Kodo padengimo ataskaitos
Kodo padengimas matuoja, koks procentas jūsų kodo yra vykdomas testų metu. Nors 100% padengimas ne visada yra praktiškas ar naudingas tikslas, šio rodiklio sekimas gali padėti nustatyti netestuotas jūsų programos dalis. Tokie įrankiai kaip „Jest“ gali generuoti padengimo ataskaitas. Galite integruoti paslaugas, tokias kaip Codecov ar Coveralls, į savo CI konvejerį, kad sektumėte padengimą laikui bėgant ir netgi nutrauktumėte vykdymą, jei padengimas nukristų žemiau tam tikros ribos.
Padengimo ataskaitos įkėlimo į Codecov žingsnio pavyzdys:
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
Paslapčių ir aplinkos kintamųjų tvarkymas
Jūsų programai greičiausiai prireiks API raktų, duomenų bazės prisijungimo duomenų ar kitos jautrios informacijos, ypač E2E testams. Niekada neįkelkite jų tiesiogiai į savo kodą. Kiekviena CI platforma suteikia saugų būdą saugoti paslaptis.
- GitHub Actions sistemoje galite juos saugoti `Settings > Secrets and variables > Actions`. Tada jie yra prieinami jūsų darbo sraute per `secrets` kontekstą, pavyzdžiui, `${{ secrets.MY_API_KEY }}`.
- GitLab CI/CD sistemoje jie valdomi `Settings > CI/CD > Variables`.
- Jenkins sistemoje prisijungimo duomenys gali būti valdomi per integruotą Credentials Manager.
Sąlyginiai darbo srautai ir optimizavimas
Ne visada reikia vykdyti kiekvieną užduotį su kiekvienu pakeitimu. Galite optimizuoti savo konvejerį, kad sutaupytumėte laiko ir išteklių:
- Vykdykite brangius E2E testus tik pull request'ams arba sujungimams į `main` šaką.
- Praleiskite CI vykdymą pakeitimams, susijusiems tik su dokumentacija, naudodami `paths-ignore`.
- Naudokite matricos strategijas, kad vienu metu testuotumėte savo kodą su keliomis Node.js versijomis ar operacinėmis sistemomis.
Anapus CI: kelias į nuolatinį diegimą (CD)
Nuolatinė integracija yra pirmoji lygties pusė. Natūralus kitas žingsnis yra nuolatinis pristatymas arba nuolatinis diegimas (CD).
- Nuolatinis pristatymas: Po to, kai visi testai sėkmingai praeina pagrindinėje šakoje, jūsų programa automatiškai sukuriama ir paruošiama išleidimui. Reikalingas galutinis, rankinis patvirtinimo žingsnis, kad ją įdiegtumėte į produkciją.
- Nuolatinis diegimas: Tai žengia dar vieną žingsnį toliau. Jei visi testai praeina sėkmingai, nauja versija automatiškai įdiegiama į produkciją be jokio žmogaus įsikišimo.
Galite pridėti `deploy` užduotį į savo CI darbo srautą, kuri būtų paleidžiama tik sėkmingai sujungus kodą į `main` šaką. Ši užduotis vykdytų scenarijus, skirtus jūsų programai įdiegti į tokias platformas kaip Vercel, Netlify, AWS, Google Cloud ar jūsų pačių serverius.
Konceptualios diegimo užduoties pavyzdys GitHub Actions:
deploy:
needs: [unit-tests, e2e-tests]
runs-on: ubuntu-latest
# Vykdyti šią užduotį tik įkeliant kodą į pagrindinę šaką
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
# ... checkout, setup, build žingsniai ...
- name: Deploy to Production
run: ./deploy-script.sh # Jūsų diegimo komanda
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
Išvada: kultūrinis pokytis, o ne tik įrankis
CI konvejerio įdiegimas jūsų JavaScript projektams yra daugiau nei techninė užduotis; tai įsipareigojimas kokybei, greičiui ir bendradarbiavimui. Tai sukuria kultūrą, kurioje kiekvienas komandos narys, nepriklausomai nuo jo buvimo vietos, yra įgalintas prisidėti su pasitikėjimu, žinodamas, kad yra galingas automatizuotas saugumo tinklas.
Pradėdami nuo tvirto automatizuotų testų pagrindo – nuo greitų vienetų testų iki išsamių E2E vartotojo kelionių – ir integruodami juos į automatizuotą CI darbo srautą, jūs transformuojate savo kūrimo procesą. Jūs pereinate nuo reaktyvios klaidų taisymo būsenos prie proaktyvios jų prevencijos būsenos. Rezultatas – atsparesnė programa, produktyvesnė kūrimo komanda ir galimybė greičiau bei patikimiau teikti vertę savo vartotojams nei bet kada anksčiau.
Jei dar nepradėjote, pradėkite šiandien. Pradėkite nuo mažų dalykų – galbūt nuo linterio ir kelių vienetų testų. Palaipsniui plėskite savo testų padengimą ir kurkite savo konvejerį. Pradinė investicija atsipirks daug kartų stabilumu, greičiu ir ramybe.