Crearea unui flux de lucru de testare a integrării continue utilizând acțiuni GitHub
Publicat: 2022-03-10Când contribuiți la proiecte pe platforme de control al versiunilor precum GitHub și Bitbucket, convenția este că există ramura principală care conține baza de cod funcțională. Apoi, există și alte ramuri în care mai mulți dezvoltatori pot lucra la copii ale principalului fie pentru a adăuga o funcție nouă, fie pentru a remedia o eroare și așa mai departe. Are foarte mult sens, deoarece devine mai ușor de monitorizat tipul de efect pe care îl vor avea modificările primite asupra codului existent. Dacă există vreo eroare, aceasta poate fi urmărită și remediată cu ușurință înainte de a integra modificările în ramura principală. Poate fi consumatoare de timp să parcurgeți manual fiecare linie de cod în căutarea erorilor sau erorilor - chiar și pentru un proiect mic. Aici intervine integrarea continuă.
Ce este integrarea continuă (CI)?
„Integrarea continuă (CI) este practica de automatizare a integrării modificărilor de cod de la mai mulți contributori într-un singur proiect software.”
— Atlassian.com
Ideea generală din spatele integrării continue (CI) este să se asigure că modificările aduse proiectului nu „rup construcția”, adică ruinează baza de cod existentă. Implementarea integrării continue în proiectul dvs., în funcție de modul în care vă configurați fluxul de lucru, ar crea o construcție ori de câte ori cineva face modificări în depozit.
Deci, ce este o construcție?
O build - în acest context - este compilarea codului sursă într-un format executabil. Dacă are succes, înseamnă că modificările primite nu vor avea un impact negativ asupra bazei de cod și sunt gata de plecare. Cu toate acestea, dacă construcția eșuează, modificările vor trebui reevaluate. De aceea, este recomandabil să faceți modificări unui proiect lucrând la o copie a proiectului pe o altă ramură înainte de a o încorpora în baza de cod principală. În acest fel, dacă construcția se întrerupe, ar fi mai ușor să vă dați seama de unde vine eroarea și, de asemenea, nu vă afectează codul sursă principal.
„Cu cât detectați mai devreme defectele, cu atât sunt mai ieftine de reparat.”
— David Farley, Livrare continuă: lansări de software de încredere prin automatizarea construirii, testării și implementării
Există mai multe instrumente disponibile pentru a ajuta la crearea unei integrări continue pentru proiectul dvs. Acestea includ Jenkins, TravisCI, CircleCI, GitLab CI, GitHub Actions etc. Pentru acest tutorial, voi folosi GitHub Actions.
Acțiuni GitHub pentru integrare continuă
CI Actions este o caracteristică destul de nouă pe GitHub și permite crearea de fluxuri de lucru care rulează automat construirea și testele proiectului. Un flux de lucru conține una sau mai multe lucrări care pot fi activate atunci când are loc un eveniment. Acest eveniment ar putea fi un impuls către oricare dintre ramurile din repo sau crearea unei cereri de extragere. Voi explica acești termeni în detaliu pe măsură ce procedăm.
Să începem!
Cerințe preliminare
Acesta este un tutorial pentru începători, așa că voi vorbi mai ales despre GitHub Actions CI la nivel de suprafață. Cititorii ar trebui să fie deja familiarizați cu crearea unui API REST Node JS folosind baza de date PostgreSQL, Sequelize ORM și scrierea de teste cu Mocha și Chai.
De asemenea, ar trebui să aveți următoarele instalate pe mașina dvs.:
- NodeJS,
- PostgreSQL,
- NPM,
- VSCode (sau orice editor și terminal la alegere).
Voi folosi un API REST pe care l-am creat deja, numit countries-info-api
. Este o API simplă, fără autorizații bazate pe roluri (ca la momentul scrierii acestui tutorial). Aceasta înseamnă că oricine poate adăuga, șterge și/sau actualiza detaliile unei țări. Fiecare țară va avea un id (UUID generat automat), nume, capitală și populație. Pentru a realiza acest lucru, am folosit Node js, express js framework și Postgresql pentru baza de date.
Voi explica pe scurt cum am configurat serverul, baza de date înainte de a începe să scriu testele pentru acoperirea testului și fișierul fluxului de lucru pentru integrare continuă.
Puteți clona countries-info-api
pentru a-l urmări sau puteți crea propriul dvs. API.
Tehnologie utilizată : Node Js, NPM (un manager de pachete pentru Javascript), bază de date Postgresql, sequelize ORM, Babel.
Configurarea Serverului
Înainte de a configura serverul, am instalat câteva dependențe de la npm.
npm install express dotenv cors npm install --save-dev @babel/core @babel/cli @babel/preset-env nodemon
Folosesc cadrul expres și scriu în format ES6, așa că am nevoie de Babeljs pentru a-mi compila codul. Puteți citi documentația oficială pentru a afla mai multe despre cum funcționează și cum să o configurați pentru proiectul dvs. Nodemon va detecta orice modificări aduse codului și va reporni automat serverul.
Notă : pachetele Npm instalate utilizând --save-dev
sunt necesare doar în etapele de dezvoltare și sunt văzute sub devDependencies în fișierul package.json
.
Am adăugat următoarele în fișierul meu index.js
:
import express from "express"; import bodyParser from "body-parser"; import cors from "cors"; import "dotenv/config"; const app = express(); const port = process.env.PORT; app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(cors()); app.get("/", (req, res) => { res.send({message: "Welcome to the homepage!"}) }) app.listen(port, () => { console.log(`Server is running on ${port}...`) })
Acest lucru setează API-ul nostru să ruleze pe orice este atribuit variabilei PORT
din fișierul .env
. Tot aici vom declara variabile la care nu dorim ca alții să aibă acces ușor. Pachetul dotenv
npm încarcă variabilele noastre de mediu din .env
.
Acum, când rulez npm run start
în terminalul meu, primesc asta:
După cum puteți vedea, serverul nostru este în funcțiune. Yay!
Acest link https://127.0.0.1:your_port_number/
din browserul dvs. web ar trebui să returneze mesajul de bun venit. Adică atâta timp cât serverul rulează.
În continuare, Baza de date și modele.
Am creat modelul de țară folosind Sequelize și m-am conectat la baza mea de date Postgres. Sequelize este un ORM pentru Nodejs. Un avantaj major este că ne economisește timpul de a scrie interogări SQL brute.
Deoarece folosim Postgresql, baza de date poate fi creată prin linia de comandă psql folosind comanda CREATE DATABASE database_name
. Acest lucru se poate face și pe terminalul dvs., dar prefer PSQL Shell.
În fișierul env, vom configura șirul de conexiune al bazei noastre de date, urmând acest format de mai jos.
TEST_DATABASE_URL = postgres://<db_username>:<db_password>@127.0.0.1:5432/<database_name>
Pentru modelul meu, am urmat acest tutorial de sequelize. Este ușor de urmărit și explică totul despre configurarea Sequelize.
În continuare, voi scrie teste pentru modelul pe care tocmai l-am creat și voi configura acoperirea pe Coverall.
Teste de scriere și acoperire de raportare
De ce să scriu teste? Personal, cred că scrierea testelor vă ajută, ca dezvoltator, să înțelegeți mai bine cum se așteaptă să funcționeze software-ul dvs. în mâinile utilizatorului, deoarece este un proces de brainstorming. De asemenea, vă ajută să descoperiți erori la timp.
Teste:
Există diferite metode de testare a software-ului, totuși, pentru acest tutorial, am folosit teste unitare și end-to-end.
Mi-am scris testele folosind cadrul de testare Mocha și biblioteca de aserții Chai. De asemenea, am instalat sequelize-test-helpers
pentru a ajuta la testarea modelului creat folosind sequelize.define
.
Acoperire de testare:
Este recomandabil să vă verificați acoperirea testului, deoarece rezultatul arată dacă cazurile noastre de testare acoperă de fapt codul și, de asemenea, cât de mult cod este folosit atunci când rulăm cazurile noastre de testare.
Am folosit Istanbul (un instrument de acoperire a testelor), nyc (clientul CLI al instabul) și salopete.
Conform documentelor, Istanbul vă instrumentează codul JavaScript ES5 și ES2015+ cu contoare de linii, astfel încât să puteți urmări cât de bine vă exersează baza de cod prin testele unitare.
În fișierul meu package.json
, scriptul de testare rulează testele și generează un raport.
{ "scripts": { "test": "nyc --reporter=lcov --reporter=text mocha -r @babel/register ./src/test/index.js" } }
În acest proces, va crea un folder .nyc_output
care conține informațiile brute de acoperire și un folder de coverage
care conține fișierele raportului de acoperire. Ambele fișiere nu sunt necesare în depozitul meu, așa că le-am plasat în fișierul .gitignore
.
Acum că am generat un raport, trebuie să-l trimitem la Salopete. Un lucru interesant despre salopete (și despre alte instrumente de acoperire, presupun) este modul în care vă raportează acoperirea testului. Acoperirea este împărțită fișier cu fișier și puteți vedea acoperirea relevantă, liniile acoperite și ratate și ce s-a schimbat în acoperirea de construcție.
Pentru a începe, instalați pachetul salopete npm. De asemenea, trebuie să vă conectați la salopete și să adăugați repo la acesta.
Apoi configurați salopete pentru proiectul dvs. javascript creând un fișier coveralls.yml
în directorul rădăcină. Acest fișier va păstra repo-token
obținut din secțiunea de setări pentru depozitul dvs. pe salopete.
Un alt script necesar în fișierul package.json sunt scripturile de acoperire. Acest script va fi util atunci când creăm o versiune prin Acțiuni.
{ "scripts": { "coverage": "nyc npm run test && nyc report --reporter=text-lcov --reporter=lcov | node ./node_modules/coveralls/bin/coveralls.js --verbose" } }
Practic, va rula testele, va primi raportul și îl va trimite la salopete pentru analiză.
Acum, la punctul principal al acestui tutorial.
Creați fișierul flux de lucru Node JS
În acest moment, am configurat joburile necesare pe care le vom rula în GitHub Action. (Vă întrebați ce înseamnă „locuri de muncă”? Continuați să citiți.)
GitHub a facilitat crearea fișierului de flux de lucru, oferind un șablon de pornire. După cum se vede pe pagina Acțiuni, există mai multe șabloane de flux de lucru care servesc diferite scopuri. Pentru acest tutorial, vom folosi fluxul de lucru Node.js (pe care GitHub l-a sugerat deja).
Puteți edita fișierul direct pe GitHub, dar voi crea manual fișierul în depozitul meu local. Dosarul .github/workflows
care conține fișierul node.js.yml
va fi în directorul rădăcină.
Acest fișier conține deja câteva comenzi de bază, iar primul comentariu explică ce fac acestea.
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
Îi voi face câteva modificări, astfel încât, pe lângă comentariul de mai sus, să ruleze și acoperire.
Fișierul meu .node.js.yml
:
name: NodeJS CI on: ["push"] jobs: build: name: Build runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm run build --if-present - run: npm run coverage - name: Coveralls uses: coverallsapp/github-action@master env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} COVERALLS_GIT_BRANCH: ${{ github.ref }} with: github-token: ${{ secrets.GITHUB_TOKEN }}
Ce inseamna asta?
Să-l descompunem.
-
name
Acesta ar fi numele fluxului de lucru (NodeJS CI) sau al jobului (build) și GitHub îl va afișa pe pagina de acțiuni a depozitului. -
on
Acesta este evenimentul care declanșează fluxul de lucru. Acea linie din fișierul meu îi spune, practic, lui GitHub să declanșeze fluxul de lucru ori de câte ori se face un push către depozitul meu. -
jobs
Un flux de lucru poate conține cel puțin una sau mai multe joburi și fiecare job rulează într-un mediu specificat deruns-on
. În exemplul de fișier de mai sus, există doar o singură lucrare care rulează construcția și, de asemenea, rulează acoperire și rulează într-un mediu Windows. De asemenea, îl pot separa în două locuri de muncă diferite, astfel:
Fișierul Node.yml actualizat
name: NodeJS CI on: [push] jobs: build: name: Build runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm run build --if-present - run: npm run test coverage: name: Coveralls runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: coverallsapp/github-action@master env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} with: github-token: ${{ secrets.GITHUB_TOKEN }}
-
env
Acesta conține variabilele de mediu care sunt disponibile pentru toate sau pentru anumite sarcini și pași din fluxul de lucru. În jobul de acoperire, puteți vedea că variabilele de mediu au fost „ascunse”. Acestea pot fi găsite în pagina de secrete a depozitului dvs. din setări. -
steps
Aceasta este, practic, o listă a pașilor care trebuie urmați atunci când rulați acea lucrare. - Lucrarea de
build
face o serie de lucruri:- Utilizează o acțiune de checkout (v2 înseamnă versiunea) care vă verifică literalmente depozitul, astfel încât să fie accesibil de fluxul de lucru;
- Utilizează o acțiune setup-node care setează mediul nodului care urmează să fie utilizat;
- Rulează scripturi de instalare, creare și testare care se găsesc în fișierul nostru package.json.
-
coverage
Aceasta utilizează o acțiune coverallsapp care postează datele de acoperire LCOV ale suitei dvs. de testare pe coveralls.io pentru analiză.
Inițial am făcut un impuls către ramura mea feat-add-controllers-and-route
și am uitat să adaug repo_token-ul de la Coveralls în fișierul meu .coveralls.yml
, așa că am primit eroarea pe care o puteți vedea pe linia 132.
Bad response: 422 {"message":"Couldn't find a repository matching this job.","error":true}
Odată ce am adăugat repo_token
, construcția mea a putut să ruleze cu succes. Fără acest simbol, salopetele nu ar putea raporta corect analiza mea de acoperire a testului. Bine că CI GitHub Actions a subliniat eroarea înainte de a fi împinsă în ramura principală.
NB: Acestea au fost luate înainte de a separa jobul în două locuri de muncă. De asemenea, am putut vedea rezumatul acoperirii și mesajul de eroare pe terminalul meu, deoarece am adăugat --verbose
la sfârșitul scriptului meu de acoperire
Concluzie
Putem vedea cum să setăm integrarea continuă pentru proiectele noastre și, de asemenea, să integrăm acoperirea testelor folosind Acțiunile puse la dispoziție de GitHub. Există atât de multe alte moduri în care acest lucru poate fi ajustat pentru a se potrivi nevoilor proiectului dumneavoastră. Deși exemplul de repo folosit în acest tutorial este un proiect cu adevărat minor, puteți vedea cât de esențială este integrarea continuă chiar și într-un proiect mai mare. Acum că locurile de muncă mele s-au desfășurat cu succes, sunt sigur că voi fuziona filiala cu filiala mea principală. Aș sfătui totuși să citiți și rezultatele pașilor după fiecare alergare pentru a vedea că este complet de succes.