Avtomatizirano testiranje mikrostoritev v Dockerju za stalno integracijo

V projektih, povezanih z razvojem mikrostoritvene arhitekture, CI/CD prehaja iz kategorije prijetne priložnosti v kategorijo nujne potrebe. Avtomatizirano testiranje je sestavni del nenehne integracije, čigar kompetenten pristop lahko ekipi omogoči veliko prijetnih večerov z družino in prijatelji. V nasprotnem primeru obstaja tveganje, da projekt ne bo nikoli dokončan.

Z enotnimi testi z lažnimi objekti je možno pokriti celotno mikrostoritveno kodo, vendar to le delno reši problem in pušča veliko vprašanj in težav, predvsem pri testiranju dela s podatki. Kot vedno so najbolj pereče testiranje konsistentnosti podatkov v relacijski bazi podatkov, testiranje dela s storitvami v oblaku in napačne predpostavke pri pisanju lažnih objektov.

Vse to in še malo več lahko rešimo s testiranjem celotne mikrostoritve v Docker kontejnerju. Nedvomna prednost za zagotavljanje veljavnosti testov je, da se testirajo iste slike Dockerja, ki gredo v proizvodnjo.

Avtomatizacija tega pristopa predstavlja številne težave, katerih rešitev bo opisana v nadaljevanju:

  • konflikti vzporednih nalog v istem docker gostitelju;
  • konflikti identifikatorjev v bazi podatkov med ponovitvami testa;
  • čakanje, da bodo mikrostoritve pripravljene;
  • združevanje in izpis dnevnikov v zunanje sisteme;
  • testiranje odhodnih zahtev HTTP;
  • testiranje spletnih vtičnic (z uporabo SignalR);
  • testiranje avtentikacije in avtorizacije OAuth.

Ta članek temelji na moj govor na SECR 2019. Torej za tiste, ki ste preleni za branje, tukaj je posnetek govora.

Avtomatizirano testiranje mikrostoritev v Dockerju za stalno integracijo

V tem članku vam bom povedal, kako uporabiti skript za zagon testirane storitve, baze podatkov in storitev Amazon AWS v Dockerju, nato teste na Postmanu in, ko so končani, zaustaviti in izbrisati ustvarjene vsebnike. Testi se izvedejo vsakič, ko se koda spremeni. Na ta način zagotovimo, da vsaka različica pravilno deluje z bazo podatkov in storitvami AWS.

Enak skript izvajajo razvijalci sami na svojih namizjih Windows in strežnik Gitlab CI pod Linuxom.

Da bi bilo to upravičeno, uvedba novih testov ne bi smela zahtevati namestitve dodatnih orodij niti na računalnik razvijalca niti na strežnik, kjer se testi izvajajo ob potrditvi.Docker rešuje to težavo.

Preizkus se mora izvesti na lokalnem strežniku iz naslednjih razlogov:

  • Omrežje ni nikoli povsem zanesljivo. Od tisoč prošenj lahko ena ne uspe;
    V tem primeru samodejni test ne bo deloval, delo se bo ustavilo, vzrok pa boste morali iskati v dnevnikih;
  • Nekatere storitve tretjih oseb ne dovoljujejo prepogostih zahtev.

Poleg tega je nezaželena uporaba stojala, ker:

  • Stojalo lahko pokvari ne le slaba koda, ki se na njem izvaja, ampak tudi podatki, ki jih pravilna koda ne more obdelati;
  • Ne glede na to, kako zelo se trudimo povrniti vse spremembe, ki jih je naredil test med samim testom, gre lahko kaj narobe (sicer, zakaj testirati?).

O projektu in organizaciji procesa

Naše podjetje je razvilo mikrostoritveno spletno aplikacijo, ki deluje v Dockerju v oblaku Amazon AWS. Na projektu so bili že uporabljeni testi enot, vendar so se pogosto pojavljale napake, ki jih testi enot niso odkrili. Treba je bilo testirati celotno mikrostoritev skupaj z bazo podatkov in storitvami Amazon.

Projekt uporablja standardni postopek stalne integracije, ki vključuje testiranje mikrostoritve z vsako potrditvijo. Po dodelitvi naloge razvijalec spremeni mikrostoritev, jo ročno preizkusi in zažene vse razpoložljive avtomatizirane teste. Po potrebi razvijalec spremeni teste. Če ni odkritih nobenih težav, se izvede obveza za vejo te izdaje. Po vsaki potrditvi se na strežniku samodejno zaženejo testi. Združitev v skupno vejo in zagon samodejnih testov na njej se zgodi po uspešnem pregledu. Če so testi v skupni veji uspešni, se storitev samodejno posodobi v testnem okolju storitve Amazon Elastic Container Service (namizna naprava). Stojalo je nujno za vse razvijalce in preizkuševalce in ga ni priporočljivo zlomiti. Preizkuševalci v tem okolju preverijo popravek ali novo funkcijo z izvajanjem ročnih preizkusov.

Projektna arhitektura

Avtomatizirano testiranje mikrostoritev v Dockerju za stalno integracijo

Aplikacijo sestavlja več kot deset storitev. Nekateri od njih so napisani v .NET Core, nekateri pa v NodeJs. Vsaka storitev se izvaja v vsebniku Docker v storitvi Amazon Elastic Container Service. Vsak ima svojo bazo Postgres, nekateri pa tudi Redis. Skupnih baz podatkov ni. Če več storitev potrebuje iste podatke, potem se ti podatki, ko se spremenijo, posredujejo vsaki od teh storitev prek SNS (Simple Notification Service) in SQS (Amazon Simple Queue Service), storitve pa jih shranijo v svoje ločene baze podatkov.

SQS in SNS

SQS omogoča postavljanje sporočil v čakalno vrsto in branje sporočil iz čakalne vrste s protokolom HTTPS.

Če več storitev bere eno čakalno vrsto, potem vsako sporočilo prispe le v eno od njih. To je uporabno pri izvajanju več primerkov iste storitve za porazdelitev obremenitve med njimi.

Če želite, da je vsako sporočilo dostavljeno več storitvam, mora imeti vsak prejemnik svojo čakalno vrsto, SNS pa je potreben za podvajanje sporočil v več čakalnih vrst.

V SNS ustvarite temo in se nanjo naročite, na primer čakalno vrsto SQS. Temi lahko pošiljate sporočila. V tem primeru je sporočilo poslano v vsako čakalno vrsto, naročeno na to temo. SNS nima metode za branje sporočil. Če morate med odpravljanjem napak ali testiranjem ugotoviti, kaj je poslano v SNS, lahko ustvarite čakalno vrsto SQS, jo naročite na želeno temo in preberete čakalno vrsto.

Avtomatizirano testiranje mikrostoritev v Dockerju za stalno integracijo

Gateway API

Večina storitev ni neposredno dostopnih prek interneta. Dostop poteka preko API Gateway, ki preverja pravice dostopa. To je tudi naša storitev in tudi zanjo obstajajo testi.

Obvestila v realnem času

Aplikacija uporablja SignalRza prikaz obvestil v realnem času uporabniku. To je implementirano v storitvi obveščanja. Dostopen je neposredno iz interneta in sam deluje z OAuth, ker se je izkazalo, da je nepraktično graditi podporo za spletne vtičnice v Gateway v primerjavi z integracijo OAuth in storitve obveščanja.

Dobro znani pristop testiranja

Preizkusi enot nadomeščajo stvari, kot je zbirka podatkov, z lažnimi objekti. Če mikrostoritev na primer poskuša ustvariti zapis v tabeli s tujim ključem in zapis, na katerega se sklicuje ta ključ, ne obstaja, potem zahteve ni mogoče izvesti. Testi enot tega ne morejo zaznati.

В Microsoftov članek Predlaga se uporaba baze podatkov v pomnilniku in implementacija lažnih objektov.

Baza podatkov v pomnilniku je eden od DBMS-jev, ki jih podpira Entity Framework. Ustvarjen je bil posebej za testiranje. Podatki v takšni bazi so shranjeni le do konca procesa, ki jih uporablja. Ne zahteva ustvarjanja tabel in ne preverja celovitosti podatkov.

Lažni objekti modelirajo razred, ki ga nadomeščajo, le do te mere, da razvijalec testa razume, kako deluje.

V Microsoftovem članku ni navedeno, kako doseči, da se Postgres samodejno zažene in izvaja selitve, ko zaženete preizkus. Moja rešitev to naredi in poleg tega mikrostoritvi sami ne doda nobene kode posebej za teste.

Pojdimo k rešitvi

Med razvojnim procesom je postalo jasno, da enotni testi niso dovolj za pravočasno odkrivanje vseh težav, zato je bilo odločeno, da se tega vprašanja lotimo z drugega zornega kota.

Nastavitev testnega okolja

Prva naloga je razmestitev testnega okolja. Koraki, potrebni za zagon mikrostoritve:

  • Konfigurirajte preizkušano storitev za lokalno okolje, v spremenljivkah okolja določite podrobnosti za povezavo z bazo podatkov in AWS;
  • Zaženite Postgres in izvedite selitev z zagonom Liquibase.
    V relacijskih DBMS morate pred zapisom podatkov v bazo podatkov ustvariti podatkovno shemo, z drugimi besedami, tabele. Pri posodabljanju aplikacije je treba tabele pripeljati v obliko, ki jo uporablja nova različica, in po možnosti brez izgube podatkov. To se imenuje migracija. Ustvarjanje tabel v prvotno prazni bazi podatkov je poseben primer selitve. Migracijo je mogoče vgraditi v samo aplikacijo. Tako .NET kot NodeJS imata selitvena ogrodja. V našem primeru je zaradi varnosti mikrostoritvam odvzeta pravica do spreminjanja podatkovne sheme, migracija pa se izvede s pomočjo Liquibase.
  • Zaženite Amazon LocalStack. To je izvedba storitev AWS za izvajanje doma. Na Docker Hubu je že pripravljena slika za LocalStack.
  • Zaženite skript, da ustvarite potrebne entitete v LocalStack. Lupinski skripti uporabljajo AWS CLI.

Uporablja se za testiranje na projektu Poštar. Obstajal je že prej, vendar je bil zagnan ročno in je testiral aplikacijo, ki je bila že nameščena na stojnici. To orodje vam omogoča, da naredite poljubne zahteve HTTP(S) in preverite, ali odgovori ustrezajo pričakovanjem. Poizvedbe so združene v zbirko in lahko zaženete celotno zbirko.

Avtomatizirano testiranje mikrostoritev v Dockerju za stalno integracijo

Kako deluje samodejni test?

Med preizkusom v Dockerju deluje vse: testirana storitev, Postgres, orodje za migracijo in Postman oziroma njegova konzolna različica - Newman.

Docker rešuje številne težave:

  • Neodvisnost od konfiguracije gostitelja;
  • Nameščanje odvisnosti: Docker prenese slike iz Docker Huba;
  • Vrnitev sistema v prvotno stanje: preprosto odstranitev posod.

docker-compose združuje kontejnerje v virtualno omrežje, izolirano od interneta, v katerem se kontejnerji med seboj najdejo po imenih domen.

Preizkus nadzira lupinski skript. Za izvajanje testa v sistemu Windows uporabljamo git-bash. Tako je en skript dovolj za Windows in Linux. Git in Docker namestijo vsi razvijalci v projektu. Ko nameščate Git v sistem Windows, je nameščen git-bash, tako da ga imajo vsi.

Skript izvede naslednje korake:

  • Gradnja docker slik
    docker-compose build
  • Zagon baze podatkov in LocalStack
    docker-compose up -d <контейнер>
  • Migracija baze podatkov in priprava LocalStack
    docker-compose run <контейнер>
  • Zagon testirane storitve
    docker-compose up -d <сервис>
  • Izvajanje testa (Newman)
  • Zaustavitev vseh kontejnerjev
    docker-compose down
  • Objavljanje rezultatov v Slacku
    Imamo klepet, kamor gredo sporočila z zeleno kljukico ali rdečim križcem in povezavo do dnevnika.

V teh korakih so vključene naslednje slike Docker:

  • Storitev, ki se testira, je enaka slika kot za proizvodnjo. Konfiguracija za test je prek spremenljivk okolja.
  • Za Postgres, Redis in LocalStack se uporabljajo že pripravljene slike iz Docker Huba. Obstajajo tudi že pripravljene slike za Liquibase in Newman. Svojega gradimo na njihovem okostju in tja dodajamo svoje datoteke.
  • Za pripravo LocalStack uporabite že pripravljeno sliko AWS CLI in na njej ustvarite sliko, ki vsebuje skript.

Uporaba prostornine, vam ni treba zgraditi Dockerjeve slike samo za dodajanje datotek v vsebnik. Vendar količine niso primerne za naše okolje, ker se same naloge Gitlab CI izvajajo v vsebnikih. Docker lahko nadzirate iz takšnega vsebnika, vendar nosilci samo pripnejo mape iz gostiteljskega sistema in ne iz drugega vsebnika.

Težave, na katere lahko naletite

Čakanje na pripravljenost

Ko se vsebnik s storitvijo izvaja, to ne pomeni, da je pripravljen za sprejemanje povezav. Počakati morate, da se povezava nadaljuje.

To težavo včasih rešimo s skriptom počakaj-na-it.sh, ki čaka na priložnost za vzpostavitev povezave TCP. Vendar lahko LocalStack sproži napako 502 Bad Gateway. Poleg tega je sestavljen iz številnih storitev in če je ena od njih pripravljena, to ne pove ničesar o drugih.

odločitev: Skripti za zagotavljanje LocalStack, ki čakajo na odgovor 200 od SQS in SNS.

Konflikti vzporednih nalog

Na istem gostitelju Docker se lahko izvaja več testov hkrati, zato morajo biti imena vsebnikov in omrežij edinstvena. Poleg tega se lahko hkrati izvajajo tudi testi iz različnih vej iste storitve, zato ni dovolj, da zapišete njihova imena v vsako datoteko za sestavljanje.

odločitev: Skript nastavi spremenljivko COMPOSE_PROJECT_NAME na edinstveno vrednost.

Funkcije sistema Windows

Pri uporabi Dockerja v sistemu Windows želim poudariti številne stvari, saj so te izkušnje pomembne za razumevanje, zakaj prihaja do napak.

  1. Lupinski skripti v vsebniku morajo imeti končnice vrstic Linux.
    Simbol lupine CR je sintaksna napaka. Iz sporočila o napaki je težko razbrati, da je temu tako. Ko urejate takšne skripte v sistemu Windows, potrebujete ustrezen urejevalnik besedil. Poleg tega mora biti sistem za nadzor različic pravilno konfiguriran.

Takole je konfiguriran git:

git config core.autocrlf input

  1. Git-bash posnema standardne mape Linuxa in pri klicu datoteke exe (vključno z docker.exe) nadomesti absolutne poti Linuxa s potmi Windows. Vendar to ni smiselno za poti, ki niso na lokalnem računalniku (ali poti v vsebniku). Tega vedenja ni mogoče onemogočiti.

odločitev: dodajte dodatno poševnico na začetek poti: //bin namesto /bin. Linux razume takšne poti; zanj je več poševnic enako eni. Toda git-bash ne prepozna takih poti in jih ne poskuša pretvoriti.

Izpis dnevnika

Pri izvajanju testov bi rad videl dnevnike Newmana in storitve, ki se testira. Ker so dogodki teh dnevnikov medsebojno povezani, je njihovo združevanje v eno konzolo veliko bolj priročno kot dve ločeni datoteki. Newman lansira prek zagon docker-compose, in tako se njegov izhod konča v konzoli. Preostane le še zagotoviti, da gre tja tudi izhod storitve.

Prvotna rešitev je bila narediti docker-compose up brez zastave -d, vendar z uporabo zmogljivosti lupine pošlji ta proces v ozadje:

docker-compose up <service> &

To je delovalo, dokler ni bilo treba poslati dnevnikov iz Dockerja v storitev tretje osebe. docker-compose up prenehal izpisovati dnevnike na konzolo. Vendar je ekipa delovala Priključni priključek.

odločitev:

docker attach --no-stdin ${COMPOSE_PROJECT_NAME}_<сервис>_1 &

Konflikt identifikatorja med ponovitvami testa

Testi potekajo v več ponovitvah. Baza podatkov ni izbrisana. Zapisi v bazi podatkov imajo edinstvene ID-je. Če v zahteve zapišemo določene ID-je, bomo pri drugi ponovitvi dobili konflikt.

Da bi se temu izognili, morajo biti ID-ji enolični ali pa je treba izbrisati vse objekte, ustvarjene s preizkusom. Nekaterih predmetov ni mogoče izbrisati zaradi zahtev.

odločitev: ustvarite GUID-je z uporabo skriptov Postman.

var uuid = require('uuid');
var myid = uuid.v4();
pm.environment.set('myUUID', myid);

Nato uporabite simbol v poizvedbi {{myUUID}}, ki bo nadomeščena z vrednostjo spremenljivke.

Sodelovanje prek LocalStack

Če storitev, ki se preskuša, bere ali piše v čakalno vrsto SQS, mora za preverjanje tega tudi sam preizkus delovati s to čakalno vrsto.

odločitev: zahteve od poštarja do LocalStack.

API storitev AWS je dokumentiran, kar omogoča izvajanje poizvedb brez SDK-ja.

Če storitev piše v čakalno vrsto, jo preberemo in preverimo vsebino sporočila.

Če storitev pošilja sporočila v SNS, LocalStack v fazi priprave ustvari tudi čakalno vrsto in se naroči na to temo SNS. Potem se vse skrči na zgoraj opisano.

Če mora storitev prebrati sporočilo iz čakalne vrste, potem v prejšnjem testnem koraku to sporočilo zapišemo v čakalno vrsto.

Preizkušanje zahtev HTTP, ki izvirajo iz testirane mikrostoritve

Nekatere storitve delujejo prek HTTP z nečim drugim kot AWS, nekatere funkcije AWS pa niso implementirane v LocalStack.

odločitev: v teh primerih lahko pomaga MockServer, ki ima že pripravljeno sliko v Dock pesto. Pričakovane zahteve in odgovori nanje so konfigurirani z zahtevo HTTP. API je dokumentiran, zato zahtevamo od Postmana.

Preizkušanje avtentikacije in avtorizacije OAuth

Uporabljamo OAuth in JSON Spletni tokeni (JWT). Test zahteva ponudnika OAuth, ki ga lahko izvajamo lokalno.

Vsa interakcija med storitvijo in ponudnikom OAuth se zmanjša na dve zahtevi: najprej se zahteva konfiguracija /.well-known/openid-configuration, nato pa se na naslovu iz konfiguracije zahteva javni ključ (JWKS). Vse to je statična vsebina.

odločitev: Naš testni ponudnik OAuth je strežnik statične vsebine in dve datoteki na njem. Žeton se ustvari enkrat in prenese v Git.

Značilnosti testiranja SignalR

Poštar ne dela s spletnimi vtičnicami. Za testiranje SignalR je bilo ustvarjeno posebno orodje.

Odjemalec SignalR je lahko več kot le brskalnik. Zanj obstaja odjemalska knjižnica pod .NET Core. Odjemalec, napisan v .NET Core, vzpostavi povezavo, je overjen in čaka na določeno zaporedje sporočil. Če prejme nepričakovano sporočilo ali se povezava prekine, odjemalec zapusti s kodo 1. Če prejme zadnje pričakovano sporočilo, odjemalec zapusti s kodo 0.

Newman dela hkrati s stranko. Zažene se več odjemalcev, da se preveri, ali so sporočila dostavljena vsem, ki jih potrebujejo.

Avtomatizirano testiranje mikrostoritev v Dockerju za stalno integracijo

Za zagon več odjemalcev uporabite možnost --lestvica v ukazni vrstici docker-compose.

Pred zagonom skript Postman počaka, da vsi odjemalci vzpostavijo povezave.
S težavo čakanja na povezavo smo se že srečali. Vendar so bili strežniki in tukaj je odjemalec. Potreben je drugačen pristop.

odločitev: odjemalec v vsebniku uporablja mehanizem Zdravstveni pregledda obvesti skript na gostitelju o njegovem statusu. Odjemalec ustvari datoteko na določeni poti, recimo /healthcheck, takoj ko je povezava vzpostavljena. Skript HealthCheck v datoteki docker je videti takole:

HEALTHCHECK --interval=3s CMD if [ ! -e /healthcheck ]; then false; fi

Ekipa docker inspect Prikazuje normalno stanje, zdravstveno stanje in izhodno kodo za posodo.

Ko Newman zaključi, skript preveri, ali so se vsi vsebniki z odjemalcem zaključili s kodo 0.

Sreča obstaja

Ko smo premagali zgoraj opisane težave, smo imeli niz testov stabilnega delovanja. Pri preizkusih vsaka storitev deluje kot ena enota, ki je v interakciji z bazo podatkov in Amazon LocalStack.

Ti testi ščitijo skupino več kot 30 razvijalcev pred napakami v aplikaciji s kompleksno interakcijo več kot 10 mikrostoritev s pogostimi uvajanji.

Vir: www.habr.com

Dodaj komentar