În activitatea noastră, folosim cu regularitate platforma SonarQube pentru a menține calitatea codului la un nivel înalt. În timpul integrării unuia dintre proiecte, scris în VueJs+Typescript, au apărut probleme. Aș dori să împărtășesc mai multe detalii despre cum am reușit să le rezolv.

În acest articol, voi discuta, așa cum am menționat anterior, despre platforma SonarQube. Puțină teorie - ce este aceasta pentru cei care aud despre ea pentru prima dată:
SonarQube (fostul Sonar) - platformă open-source pentru analiza continuă și măsurarea calității codului.
Suportă analiza codului și identificarea erorilor conform standardelor de programare MISRA C, MISRA C++, MITRE/CWE și CERT Secure Coding Standards. De asemenea, poate recunoaște erorile din listele OWASP Top-10 și CWE/SANS Top-25 ale erorilor de programare.
Deși platforma utilizează diverse instrumente gata făcute, SonarQube sintetizează rezultatele într-un panou informativ unic, urmărind istoricul rulărilor și permițându-le astfel utilizatorilor să observe tendința generală a calității software-ului pe parcursul dezvoltării.
Pentru mai multe detalii, puteți vizita
Se suportă un număr mare de limbaje de programare. Judecând după informațiile din linkul de mai sus, este vorba de peste 25 de limbaje. Pentru suportul unui limbaj specific, este necesar să instalați pluginul corespunzător. Versiunea community include un plugin pentru lucrul cu Javascript (inclusiv TypeScript), deși în wiki este scris contrariul. pentru Javascript se ocupă pluginul SonarJS, iar pentru TypeScript, SonarTS corespunzător.
Pentru a trimite informații despre acoperire, se utilizează clientul oficial sonarqube-scanner, care, utilizând setările din config-fișier, trimite aceste date către server SonarQube pentru consolidare și agregare ulterioară.
Pentru Javascript are Deci, să începem implementarea pas cu pas SonarQube în Vue-proiect, care folosește TypeScript.
Pentru a desfășura serverul, SonarQube vom folosi docker-compose.
sonar.yaml:
version: '1'
services:
simplesample-sonar:
image: sonarqube:lts
ports:
- 9001:9000
- 9092:9092
network_mode: bridgePornire:
docker-compose -f sonar.yml upDupă aceasta, SonarQube va fi accesibil la adresa – .

În momentul de față nu are proiecte și acest lucru este corect. Vom corecta această situație. Am folosit ca bază proiectul oficial de exemplu pentru VueJS+TS+Jest. Vom fi clonați:
git clone https://github.com/vuejs/vue-test-utils-typescript-example.gitMai întâi, trebuie să instalăm clientul SonarQube, care se numește sonar-scanner, pentru npm există un wrapper:
yarn add sonarqube-scannerȘi imediat adăugăm comanda în scripts pentru a lucra cu el.
package.json:
{
…
scripts: {
...
"sonar": "sonar-scanner"
...
},
…
}Apoi, pentru a opera scanerul, trebuie să configurăm setările proiectului într-un fișier special. Să începem cu cele de bază.
sonar-project.properties:
sonar.host.url=http://localhost:9001
sonar.projectKey=test-project-vuejs-ts
sonar.projectName=Aplicație de test (VueJS+TS)
sonar.sources=src
# sonar.tests=
sonar.test.inclusions=src/**/*tests*/**
sonar.sourceEncoding=UTF-8- sonar.host.url – adresa Sonar’a;
- sonar.projectKey – identificatorul unic al proiectului pe server Sonar’a;
- sonar.projectName – denumirea sa, care poate fi schimbată oricând, deoarece identificarea proiectului se face după projectKey;
- sonar.sources – folderul cu sursele, de obicei aceasta este src, dar poate fi oricare. Acest folder este specificat în raport cu folderul principal, care este folderul din care este lansat scanerul;
- sonar.tests – parametru care vine împreună cu precedentul. Acesta este folderul în care se află testele. În acest proiect, nu există un astfel de folder, iar testul se află lângă componenta testată în folderul 'test', așa că deocamdată îl vom ignora și vom folosi următorul parametru;
- sonar.test.inclusions – calea pentru teste folosind un model, pot fi mai multe elemente enumerate prin virgulă;
- sonar.sourceEncoding – codificarea pentru fișierele sursă.
Pentru prima lansare a scanerului, totul este pregătit, cu excepția principalului pas premergător: lansarea motorului de testare pentru a genera informațiile despre acoperire, pe care ulterior scanerul le va folosi.
Dar pentru aceasta trebuie să configurăm motorul de testare pentru a genera aceste informații. În acest proiect, motorul de testare este Jest. Iar setările sale se află în secțiunea corespunzătoare a fișierului package.json.
Să adăugăm aceste setări:
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*",
"!src/main.ts",
"!src/App.vue",
"!src/**/*.d.*",
"!src/**/*__tests__*"
],Adică, setăm flagul pentru necesitatea calculării acoperirii și sursa (împreună cu excepțiile) pe baza cărora aceasta va fi generată.
Acum să lansăm testul:
yarn testVom vedea următoarele:

Motivul este că în componentă, ca atare, nu există cod. Să corectăm acest aspect.
HelloWorld.vue:
...
methods: {
calc(n) {
return n + 1;
}
},
mounted() {
this.msg1 = this.msg + this.calc(1);
},
...Aceasta va fi suficient pentru calcularea acoperirii.
După relansarea testului, să ne asigurăm de acest lucru:

Pe ecran ar trebui să vedem informații despre acoperire, iar în folderul proiectului va fi creat un folder coverage cu informații despre acoperirea testelor în format universal LCOV (extensie LTP GCOV).
Gcov — un instrument liber distribuit pentru analiza acoperirii codului. Gcov generează un număr exact de execuții pentru fiecare declarație din program și permite adăugarea de anotații la codul sursă. Gcov este livrat ca instrument standard în pachetul GCC.
Lcov — o interfață grafică pentru gcov. Aceasta colectează fișierele gcov pentru mai multe fișiere sursă și creează un set de pagini HTML cu codul și informațiile despre acoperire. De asemenea, sunt generate pagini pentru o navigare simplificată. Lcov susține acoperirea linilor, funcțiilor și ramificațiilor.
După executarea testelor, informațiile despre acoperire vor fi găsite în coverage/lcov.info.
Trebuie să-i spunem Sonarde unde să o ia. Prin urmare, vom adăuga următoarele linii în fișierul său de configurare. Dar există un aspect: proiectele pot fi multilingve, adică în folderul src se află sursele pentru mai multe limbaje de programare, iar apartenența la unul sau altul, și în același timp utilizarea unui plugin sau altul, este determinată de extensia sa. Iar informațiile despre acoperire pot fi stocate în locuri diferite pentru diferite limbaje de programare, prin urmare, pentru fiecare limbaj de programare există o secțiune dedicată pentru configurarea acestuia. Proiectul nostru utilizează TypeScript, așa că avem nevoie de o secțiune de configurare special pentru el:
sonar-project.properties:
sonar.typescript.coveragePlugin=lcov
sonar.typescript.lcov.reportPaths=coverage/lcov.infoTotul este pregătit pentru prima rulare a scanner-ului. Vreau să menționez că proiectul în Sonarse creează automat la prima rulare a scanner-ului pentru acest proiect. În runde ulterioare, informațiile vor fi acumulate pentru a vedea dinamica modificării parametrilor proiectului în timp.
Așadar, să folosim comanda creată anterior în package.json:
yarn run sonar Notă: de asemenea, se poate folosi parametrul -X pentru o jurnalizare mai detaliată.
Dacă rularea scanner-ului a fost prima dată, atunci mai întâi se va descărca binarul scanner-ului în sine. După aceasta, acesta se pornește și începe să scaneze serverul Sonarpentru a verifica pluginurile instalate, calculând astfel limbajele de programare suportate. De asemenea, sunt încărcate diferite alte parametrii necesari pentru funcționarea sa: profiluri de calitate, reguli active, depozit de metrici, reguli de server.


Notă: nu ne vom opri detaliat asupra acestora în cadrul acestui articol, dar întotdeauna se poate apela la surse oficiale.
Apoi începe analiza folderului src pentru existența fișierelor sursă pentru toate (dacă nu este specificat explicit un anumit) limbaje de programare susținute, cu indexarea ulterioară a acestora.

Următoarele sunt alte diverse analize, la care nu ne concentrăm în acest articol (de exemplu, cum ar fi: linting, determinarea duplicării codului etc.).
La finalul lucrării scanner-ului are loc agregarea tuturor informațiilor colectate, arhivarea și trimiterea acesteia pe server.
După aceea, putem verifica rezultatul în interfața web:

Așa cum vedem, ceva a ieșit, și chiar arată o acoperire, dar aceasta nu corespunde raportului nostru Jest-raport.
Să ne lămurim. Să ne uităm la proiect mai în detaliu, să facem clic pe valoarea acoperirii și să „picăm” în raportul detaliat pe fișiere:

Aici vedem, pe lângă fișierul principal, examinat HelloWorld.vue, există și fișierul main.ts, care strică întreaga imagine a acoperirii. Dar cum este posibil, l-am exclus din calculul acoperirii. Da, totul este corect, dar a fost la nivelul Jest, dar scannerul l-a indexat, așa că a intrat în calculele sale.
Să corectăm asta:
sonar-project.properties:
...
sonar.exclusions=src/main.ts
...Aș dori să fac o precizare: pe lângă folderele specificate în acest parametru, se adaugă și toate folderele enumerate în parametrul sonar.test.inclusions.
După ce am pornit scannerul, vedem deja informații corecte:


Să discutăm despre următorul aspect – Profiluri de calitate. Am menționat mai sus despre suportul Sonarpentru mai multe limbaje de programare simultan. Iată, tocmai asta observăm. Dar știm că proiectul nostru este scris în TS, deci de ce să aglomerăm scannerul cu manipulări și verificări inutile. Limbajul pentru analiză îl vom specifica prin adăugarea unui alt parametru în fișierul de configurare Sonar:
sonar-project.properties:
...
sonar.language=ts
...Să rerulăm scannerul și să vedem rezultatul:

Acoperirea a dispărut complet.
Dacă ne uităm în log-ul scanner-ului, putem vedea următoarea linie:
![]()
Asta înseamnă că fișierele proiectului nostru pur și simplu nu au fost indexate.
Situația este următoarea: suportul oficial pentru VueJs este în pluginul SonarJS, care se ocupă de Javascript.

Dar acest suport nu există în pluginul SonarTS pentru TS, despre care a fost deschis un tichet oficial în tracker-ul de bug-uri. Sonar:
Iată câteva răspunsuri de la unul dintre reprezentanții dezvoltatorilor SonarQube, care confirmă acest fapt.


Dar totuși, totul a funcționat, ați putea obiecta. Da, așa este, să încercăm puțin să „hack-uim”.
Dacă există suport pentru .vue-fișiere. Sonaratunci hai să încercăm să-i spunem să le trateze ca TypeScript.
Adăugăm parametrul:
sonar-project.properties:
...
sonar.typescript.file.suffixes=.ts,.tsx,.vue
...Să pornim scannerul:

Și, voila, totul a revenit la normal, cu un singur profil doar pentru TypeScript. Asta înseamnă că am reușit să rezolvăm problema suportului VueJs+TS pentru SonarQube.
Hai să mergem mai departe și să îmbunătățim puțin informațiile despre acoperire.
Ce am realizat până acum:
- am adăugat în proiect Sonar-scanner;
- am configurat Jest pentru a genera informațiile despre acoperire;
- am configurat Sonar-scanner;
- am rezolvat problema suportului .vue-fișierelor + TypeScript.
Pe lângă acoperirea cu teste, există alți indicatori interesanți de calitate a codului, cum ar fi duplicarea codului și numărul de linii (care contribuie la calcularea coeficientului legat de complexitatea codului) proiectului.
În implementarea curentă a pluginului pentru lucrul cu TS (SonarTS) nu va funcționa CPD (Copy Paste Detector) și numărarea liniilor de cod .vue-fișierelor.
Pentru a crea o situație sintetică de duplicare a codului, pur și simplu vom duplicat fișierul componentului cu un alt nume, de asemenea, vom adăuga în cod main.ts o funcție goală și o vom duplica cu un alt nume. Pentru a verifica duplicarea ca în .vue, cât și în .ts -fișiere.
main.ts:
...
function name(params:string): void {
console.log(params);
}
...Pentru aceasta, trebuie să comentăm temporar rândul de configurare:
sonar-project.properties:
...
sonar.exclusions=src/main.ts
...Să repornim scannerul împreună cu testarea:
yarn test && yarn run sonarDe obicei, ne va scădea acoperirea, dar acum nu este interesant pentru noi.
În ceea ce privește duplicarea liniilor de cod, vom vedea:

Pentru verificare, vom folosi CPD-utilitarul – jscpd:
npx jscpd src
Pentru liniile de cod:

Este posibil ca acest lucru să se rezolve în viitoarele versiuni ale pluginurilor SonarJS(TS). Vreau să menționez că acestea încep treptat să fuzioneze aceste două pluginuri într-unul SonarJS, ceea ce cred că este corect.
Acum, aș dori să discut despre o opțiune de îmbunătățire a informațiilor despre acoperire.
Deocamdată vedem acoperirea cu teste în raport procentual, pe întregul proiect și pe fișiere în particular. Dar există posibilitatea de a extinde acest indicator cu informații despre numărul unit-testelor din proiect, precum și în analiza fișierelor.
Există o bibliotecă care poate Jest-converti raportul în format pentru Sonar:
generic test data — .
Să instalăm această bibliotecă în proiectul nostru:
yarn add jest-sonar-reporterȘi să o adăugăm în configurare Jest:
package.json:
…
"testResultsProcessor": "jest-sonar-reporter"
…Acum să rulăm testul:
yarn testDupă care un fișier va fi creat în rădăcina proiectului test-report.xml.
Să-l includem în configurare Sonar:
sonar-project.properties:
…
sonar.testExecutionReportPaths=test-report.xml
…Și să repornim scannerul:
yarn run sonarSă vedem ce s-a schimbat în interfață Sonar:

Și nu s-a schimbat nimic. Problema este că Sonar nu ia în considerare fișierele descrise în raportul Jest ca fiind fișiere unit-teste. Pentru a remedia această situație, vom folosi un parametru de configurare Sonar sonar.tests, în care vom specifica clar folderele cu teste (deocamdată avem doar unul):
sonar-project.properties:
…
sonar.tests=src/components/__tests__
…Să repornim scannerul:
yarn run sonarSă vedem ce s-a schimbat în interfață:

Acum am văzut numărul testelor noastre unit-teste și, făcând click pe ele, putem vedea distribuția acestui număr pe fișierele proiectului:

Concluzie
Așadar, am analizat instrumentul pentru analiza continuă SonarQube. L-am integrat cu succes în proiectul scris în VueJs+TS. Am rezolvat anumite probleme de compatibilitate. Am îmbunătățit informativitatea indicatorului de acoperire a testelor. În acest articol am discutat doar despre unul dintre criteriile de calitate a codului (poate unul dintre cele principale), dar SonarQube susține și alte criterii de calitate, inclusiv testarea pe securitate. Însă nu toate aceste funcționalități sunt disponibile pe deplin în versiunea community. Una dintre funcționalitățile interesante și utile este integrarea SonarQube cu diverse sisteme de gestionare a codului sursă, cum ar fi GitLab și BitBucket. Pentru a evita merge pull(merge) requestîn ramura principală a depozitului în cazul degradării acoperirii. Dar aceasta este o poveste complet diferită.
PS: Tot ceea ce este descris în articol sub formă de cod este disponibil în .
Numai utilizatorii înregistrați pot participa la sondaj. , vă rugăm.
Folositi platforma SonarQube:
26,3%Da5
15,8%Nu3
15,8%Am auzit despre această platformă și vreau să o folosesc3
10,5%Am auzit despre această platformă și nu vreau să o folosesc2
0,0%Folosesc o altă platformă0
31,6%Aud despre ea pentru prima dată6
Au votat 19 utilizatori. S-au abținut 3 utilizatori.
Sursa: habr.com
