U projektima koji uključuju razvoj mikroservisne arhitekture, CI/CD prelazi iz ugodne značajke u apsolutnu nužnost. Automatizirano testiranje sastavni je dio kontinuirane integracije, a dobro osmišljen pristup može timu pružiti bezbroj ugodnih večeri s obitelji i prijateljima. U suprotnom, projekt riskira da nikada ne bude dovršen.
Moguće je pokriti cijeli kod mikroservisa jediničnim testovima korištenjem lažnih objekata, ali to samo djelomično rješava problem i ostavlja mnoga pitanja i izazove, posebno pri testiranju interakcija podataka. Kao i uvijek, najhitniji su testiranje konzistentnosti podataka u relacijskoj bazi podataka, testiranje interakcija s uslugama u oblaku i netočne pretpostavke pri pisanju lažnih objekata.
Sve ovo i više može se postići testiranjem cijelog mikroservisa u Docker kontejneru. Jasna prednost za osiguranje valjanosti testa je da se testiraju iste Docker slike koje se koriste u produkciji.
Automatizacija ovog pristupa predstavlja niz izazova, čija će rješenja biti opisana u nastavku:
- sukobi paralelnih zadataka na jednom Docker hostu;
- sukobi identifikatora u bazi podataka tijekom iteracija testiranja;
- čekanje da mikroservisi budu spremni;
- kombiniranje i ispis logova na vanjske sustave;
- testiranje odlaznih HTTP zahtjeva;
- testiranje web socketa (korištenjem SignalR-a);
- OAuth autentifikacija i testiranje autorizacije.
Ovaj članak se temelji na na SECR-u 2019. Dakle, za one koji su previše lijeni za čitanje, .

U ovom članku objasnit ću kako koristiti skriptu za pokretanje testirane usluge, baze podataka i Amazon AWS usluga u Dockeru, zatim pokrenuti testove u Postmanu i, nakon završetka, zaustaviti i izbrisati kreirane kontejnere. Testovi se pokreću sa svakom promjenom koda. To osigurava da svaka verzija ispravno radi s bazom podataka i AWS uslugama.
Isti skript pokreću sami programeri na svojim Windows-desktopovi i Gitlab CI poslužitelj pod Linux.
Da bi implementacija novih testova bila opravdana, ne bi trebala zahtijevati instalaciju dodatnih alata ni na računalu programera ni na poslužitelju na kojem se testovi izvode prilikom commit-a. Docker rješava taj problem.
Test se mora izvoditi na lokalnom poslužitelju iz sljedećih razloga:
- Nijedna mreža nije potpuno pouzdana. Jedan od tisuću zahtjeva može propasti;
U tom slučaju, automatski test neće uspjeti, rad će se zaustaviti i morat ćete potražiti uzrok u zapisnicima; - Neke usluge trećih strana ne dopuštaju prečeste zahtjeve.
Osim toga, nije preporučljivo koristiti stalak jer:
- Stalak može biti pokvaren ne samo zbog lošeg koda koji se na njemu izvršava, već i zbog podataka koje ispravan kod ne može obraditi;
- Bez obzira koliko se trudimo vratiti sve promjene napravljene testom tijekom samog testiranja, nešto može poći po zlu (inače, koja je svrha testa?).
O projektu i organizaciji procesa
Naša tvrtka je razvijala web aplikaciju za mikroservis koja se izvodi u Dockeru na Amazon AWS oblaku. Jedinični testovi su već bili instalirani na projektu, ali su se često pojavljivale pogreške koje jedinični testovi nisu otkrili. Trebali smo testirati cijeli mikroservis, uključujući bazu podataka i Amazon servise.
Projekt koristi standardni proces kontinuirane integracije, koji uključuje testiranje mikroservisa sa svakim commitom (potvrdom). Nakon što je zadatak dodijeljen, programer unosi promjene u mikroservis, ručno ga testira i pokreće sve postojeće automatizirane testove. Programer po potrebi modificira testove. Ako se ne pronađu problemi, na granu se izvršava commit za zadatak. Nakon svakog commita, testovi se automatski pokreću na poslužitelju. Spajanje s glavnom granom i pokretanje automatiziranih testova na njoj događa se nakon uspješnog pregleda. Ako testovi na glavnoj grani prođu, usluga se automatski ažurira u testnom okruženju na Amazon Elastic Container Serviceu (testnom platformi). Platforma je ključna za sve programere i testere, a njezino oštećenje nije poželjno. Testeri koriste ovo okruženje za provjeru ispravka ili nove značajke, pokrećući ručne testove.
Arhitektura projekta

Aplikacija se sastoji od više od deset servisa. Neki su napisani u .NET Coreu, a neki u NodeJS-u. Svaki servis radi u Docker kontejneru u Amazon Elastic Container Serviceu. Svaki servis ima vlastitu Postgres bazu podataka, a neki imaju i Redis. Nema dijeljenih baza podataka. Ako više servisa treba iste podatke, podaci se prenose svakom od tih servisa čim se promijene putem SNS-a (Simple Notification Service) i SQS-a (Amazon Simple Queue Service), a servisi ih pohranjuju u vlastite odvojene baze podataka.
SQS i SNS
SQS vam omogućuje stavljanje poruka u red čekanja i čitanje poruka iz reda čekanja pomoću HTTPS protokola.
Ako više servisa čita iz istog reda, svaka poruka se šalje samo jednom od njih. To je korisno kada se pokreće više instanci istog servisa kako bi se opterećenje rasporedilo među njima.
Ako svaku poruku treba dostaviti više servisa, svaki primatelj mora imati vlastiti red čekanja, a SNS je potreban za dupliciranje poruka u više redova čekanja.
U SNS-u stvarate temu i pretplatite se na nju, na primjer, SQS red čekanja. Možete slati poruke temi. Kada to učinite, poruka se šalje svakom redu čekanja pretplaćenom na tu temu. SNS nema metodu za čitanje poruka. Ako trebate znati što se šalje SNS-u tijekom otklanjanja pogrešaka ili testiranja, možete stvoriti SQS red čekanja, pretplatiti ga na željenu temu i pročitati red čekanja.

API pristupnik
Većini usluga nije moguće izravno pristupiti s interneta. Pristup se omogućuje putem API Gatewaya, koji provjerava prava pristupa. Ovo je također naša usluga i imamo testove za nju.
Obavijesti u stvarnom vremenu
Aplikacija koristi za prikaz obavijesti u stvarnom vremenu korisniku. To je implementirano u usluzi obavještavanja. Dostupna je izravno s interneta i radi sa samim OAuthom, jer se integracija podrške za web socket u Gateway pokazala nepraktičnom u usporedbi s integracijom OAutha i usluge obavještavanja.
Dobro poznati pristup testiranju
Jedinični testovi zamjenjuju stvari poput baze podataka lažnim objektima. Ako mikroservis, na primjer, pokuša stvoriti zapis u tablici sa stranim ključem, ali zapis na koji se taj ključ poziva ne postoji, zahtjev se ne može izvršiti. Jedinični testovi to ne mogu otkriti.
В Predlaže se korištenje baze podataka u memoriji i implementacija lažnih objekata.
Baza podataka u memoriji jedan je od sustava za upravljanje bazama podataka koje podržava Entity Framework. Dizajnirana je posebno za testiranje. Podaci u takvoj bazi podataka pohranjuju se samo dok se ne završi proces koji je koristi. Nema potrebe za stvaranjem tablica i ne provjerava se integritet podataka.
Lažni objekti modeliraju klasu koju zamjenjuju samo onoliko koliko programer testova razumije kako ona funkcionira.
Microsoftov članak ne objašnjava kako automatski pokrenuti Postgres i pokrenuti migracije prilikom izvođenja testa. Moje rješenje to radi, a nadalje, samom mikroservisu nije dodan nikakav kod posebno namijenjen testiranju.
Prijeđimo na rješenje
Tijekom razvoja postalo je jasno da jedinični testovi nisu dovoljni za pravovremeno otkrivanje svih problema, pa je odlučeno pristupiti problemu iz drugačije perspektive.
Postavljanje testnog okruženja
Prvi zadatak je implementacija testnog okruženja. Koraci potrebni za pokretanje mikroservisa su:
- Konfigurirajte uslugu koja se testira u lokalnom okruženju; varijable okruženja određuju detalje za povezivanje s bazom podataka i AWS-om;
- Pokrenite Postgres i pokrenite migraciju pokretanjem Liquibasea.
U relacijskim DBMS-ovima, prije pisanja podataka u bazu podataka, potrebno je stvoriti shemu podataka - ili, jednostavnije rečeno, tablice. Prilikom nadogradnje aplikacije, tablice je potrebno pretvoriti u format koji koristi nova verzija, po mogućnosti bez gubitka podataka. To se naziva migracija. Stvaranje tablica u početno praznoj bazi podataka poseban je slučaj migracije. Migracija se može ugraditi u samu aplikaciju. I .NET i NodeJS imaju migracijske okvire. U našem slučaju, iz sigurnosnih razloga, mikroservisima je ograničeno mijenjanje sheme podataka, a migracija se izvodi pomoću Liquibasea. - Pokrenite Amazon LocalStack. Ovo je implementacija AWS usluga za samostalno pokretanje. Na Docker Hubu postoji gotova LocalStack slika.
- Pokrenite skriptu za stvaranje potrebnih entiteta u LocalStacku. Shell skripte koriste AWS CLI.
Za testiranje na projektu se koristi Postojao je i prije, ali je pokretan ručno i korišten za testiranje aplikacije koja je već bila raspoređena na testnoj platformi. Ovaj alat vam omogućuje da napravite proizvoljne HTTP(S) zahtjeve i provjerite ispunjavaju li odgovori očekivanja. Zahtjevi se kombiniraju u kolekciju i cijela kolekcija se može pokrenuti.

Kako funkcionira automatizirani test?
Tijekom testiranja, sve radi u Dockeru: testirana usluga, Postgres, alat za migraciju i Postman, odnosno njegova konzolna verzija, Newman.
Docker rješava niz problema:
- Neovisnost od konfiguracije hosta;
- Instaliranje ovisnosti: Docker preuzima slike s Docker Huba;
- Vraćanje sustava u prvobitno stanje: jednostavno izbrišite kontejnere.
Docker-compose ujedinjuje kontejnere u virtualnu mrežu izoliranu od interneta, u kojoj se kontejneri međusobno pronalaze putem domenskih imena.
Testom upravlja shell skripta. Za pokretanje testa pod Windows Koristimo git-bash. Dakle, jedna skripta je dovoljna za Windows i za LinuxGit i Docker instaliraju svi programeri na projektu. Prilikom instaliranja Gita pod Windows git-bash je instaliran, tako da ga svi imaju.
Skripta izvršava sljedeće korake:
- Izrada Docker slika
docker-compose build - Pokretanje baze podataka i LocalStacka
docker-compose up -d <контейнер> - Migracija baze podataka i priprema LocalStacka
docker-compose run <контейнер> - Pokretanje usluge u fazi testiranja
docker-compose up -d <сервис> - Provođenje testa (Newman)
- Zaustavljanje svih kontejnera
docker-compose down - Objavljivanje rezultata u Slacku
Imamo chat u kojem se šalju poruke sa zelenom kvačicom ili crvenim križićem i poveznicom na dnevnik.
Ovi koraci uključuju sljedeće Docker slike:
- Servis koji se testira je ista slika kao i produkcijski. Konfiguracija za testiranje se daje putem varijabli okruženja.
- Za Postgres, Redis i LocalStack koristimo gotove slike iz Docker Huba. Liquibase i Newman također imaju gotove slike. Gradimo vlastite na njihovim temeljima, dodajući vlastite datoteke.
- Za opskrbu LocalStacka koristi se unaprijed izgrađena AWS CLI slika, a iz nje se stvara slika koja sadrži skriptu.
Korištenje , ne morate izraditi Docker sliku samo da biste dodali datoteke u kontejner. Međutim, volumeni nisu prikladni za naše okruženje jer se sami Gitlab CI zadaci izvode u kontejnerima. Dockerom se može upravljati iz takvog kontejnera, ali volumeni montiraju samo mape s glavnog sustava, a ne iz drugog kontejnera.
Problemi s kojima se možete susresti
Čeka se spremnost
Samo zato što je servisni kontejner pokrenut ne znači da je spreman za prihvaćanje veza. Morate pričekati da se veza nastavi.
Ovaj problem se ponekad rješava uz pomoć skripte. , koji čeka priliku za uspostavljanje TCP veze. Međutim, LocalStack može vratiti grešku 502 Bad Gateway. Nadalje, sastoji se od više servisa, a ako je jedan spreman, to ne znači ništa o ostalima.
odlukaSkripte za pripremu LocalStacka koje čekaju odgovor 200 od SQS-a i SNS-a.
Sukobi između paralelnih zadataka
Više testova može se istovremeno izvoditi na jednom Docker hostu, tako da nazivi kontejnera i mreže moraju biti jedinstveni. Nadalje, testovi iz različitih grana iste usluge također se mogu izvoditi istovremeno, tako da nije dovoljno navesti zasebna imena u svakoj datoteci za sastavljanje.
odlukaSkripta postavlja jedinstvenu vrijednost za varijablu COMPOSE_PROJECT_NAME.
Značajke Windows
Prilikom korištenja Dockera u Windows Želim vam skrenuti pozornost na nekoliko stvari, jer je ovo iskustvo važno za razumijevanje uzroka pogrešaka.
- Shell skripte u kontejneru moraju imati Linux završetke redaka.
Znak CR za ljusku je sintaktička pogreška. Iz poruke o pogrešci teško je zaključiti da je to problem. Prilikom uređivanja takvih skripti u Windows Trebate odgovarajući uređivač teksta. Osim toga, sustav za kontrolu verzija mora biti ispravno konfiguriran.
Ovako je git konfiguriran:
git config core.autocrlf input
- Git-bash emulira standardne mape Linux i prilikom pozivanja exe datoteke (uključujući docker.exe) zamjenjuje apsolutno Linux-putevi do Windows-putnje. Međutim, ovo nema smisla za putove koji nisu na lokalnom računalu (ili putove u spremniku). Ovo ponašanje se ne može onemogućiti.
odluka: dodajte dodatnu kosu crtu na početak puta: //bin umjesto /bin. Linux razumije takve putove; za njega su višestruki slashovi isti kao jedan. Ali git-bash ne prepoznaje takve putove i ne pokušava ih pretvoriti.
Izlaz zapisnika
Prilikom izvođenja testova, želio bih vidjeti zapisnike i iz Newmana i iz testirane usluge. Budući da su događaji u tim zapisnicima međusobno povezani, njihovo kombiniranje u jednoj konzoli puno je praktičnije nego dvije odvojene datoteke. Newman se pokreće putem docker-compose run, i stoga njegov izlaz ide u konzolu. Preostaje samo osigurati da i izlaz servisa ide tamo.
Prvotna odluka bila je učiniti sastavljač bez zastave -d, ali koristeći mogućnosti ljuske, pošaljite ovaj proces u pozadinu:
docker-compose up <service> &
To je funkcioniralo sve dok nije postalo potrebno slati logove s Dockera na uslugu treće strane. sastavljač zaustavljen je ispis logova u konzolu. Međutim, naredba je radila. docker prilog.
odluka:
docker attach --no-stdin ${COMPOSE_PROJECT_NAME}_<сервис>_1 &
Sukob identifikatora tijekom iteracija testiranja
Testovi se izvode u više iteracija. Baza podataka se ne briše. Zapisi baze podataka imaju jedinstvene ID-ove. Ako su u upite uključeni specifični ID-ovi, sukob će se pojaviti u drugoj iteraciji.
Da bi se to izbjeglo, ili ID-ovi moraju biti jedinstveni ili se svi objekti koje je stvorio test moraju izbrisati. Neki objekti se ne mogu izbrisati zbog zahtjeva.
odlukageneriranje GUID-ova pomoću skripti u Postmanu.
var uuid = require('uuid');
var myid = uuid.v4();
pm.environment.set('myUUID', myid);
Zatim upotrijebite simbol u svom upitu {{myUUID}}, koja će biti zamijenjena vrijednošću varijable.
Interakcija putem LocalStacka
Ako testirana usluga čita iz ili zapisuje u SQS red, sam test mora raditi i s tim redom kako bi to provjerio.
odlukazahtjevi od Postmana prema LocalStacku.
AWS usluge imaju dokumentirane API-je, što vam omogućuje slanje zahtjeva bez SDK-a.
Ako servis zapisuje u red čekanja, čitamo ga i provjeravamo sadržaj poruke.
Ako servis šalje poruke SNS-u, LocalStack također stvara red čekanja i pretplaćuje se na ovu SNS temu tijekom faze postavljanja. Od tada sve slijedi isti obrazac kao što je gore opisano.
Ako servis treba pročitati poruku iz reda čekanja, tada u prethodnom koraku testa tu poruku zapisujemo u red čekanja.
Testiranje HTTP zahtjeva iz testiranog mikroservisa
Neke usluge komuniciraju putem HTTP-a s nečim drugim osim AWS-a, a neke AWS značajke nisu implementirane u LocalStacku.
odluka: u ovim slučajevima može pomoći , koji ima gotovu sliku u Očekivani zahtjevi i odgovori konfiguriraju se putem HTTP zahtjeva. API je dokumentiran, pa zahtjeve šaljemo putem Postmana.
Testiranje OAuth autentifikacije i autorizacije
Koristimo OAuth i Za test nam je potreban OAuth provider kojeg možemo pokrenuti lokalno.
Sva interakcija između servisa i OAuth pružatelja usluga svodi se na dva zahtjeva: prvo se traži konfiguracija /.well-known/openid-configuration, a zatim se javni ključ (JWKS) traži pomoću adrese u konfiguraciji. Sve je to statički sadržaj.
odlukaNaš testni OAuth pružatelj usluga je statički poslužitelj sadržaja s dvije datoteke na njemu. Token se generira jednom i šalje Gitu.
Značajke testiranja SignalR-a
Postman ne radi s web socketima. Za testiranje SignalR-a kreiran je poseban alat.
SignalR klijent može biti bilo što, od preglednika do .NET Core klijentske biblioteke. .NET Core klijent uspostavlja vezu, autentificira se i čeka određeni niz poruka. Ako se primi neočekivana poruka ili se veza prekine, klijent se završava s kodom 1. Nakon što primi posljednju očekivanu poruku, završava s kodom 0.
Newman se pokreće istovremeno s klijentom. Pokreće se nekoliko klijenata kako bi se osigurala dostava poruka svima.

Za pokretanje više klijenata koristite opciju --ljestvica u naredbenom retku docker-compose-a.
Prije pokretanja Postmana, skripta čeka da svi klijenti uspostave vezu.
Već smo se susreli s problemom čekanja veze. Ali to su bili poslužitelji, a ovo je klijent. Potreban je drugačiji pristup.
odluka: klijent u kontejneru koristi mehanizam kako bi obavijestio host skriptu o svom statusu. Klijent stvara datoteku na određenoj putanji, recimo /healthcheck, nakon što se uspostavi veza. HealthCheck skripta u Docker datoteci izgleda ovako:
HEALTHCHECK --interval=3s CMD if [ ! -e /healthcheck ]; then false; fi
Momčad Docker Inspect Prikazuje normalan status, status ispravnosti i izlazni kod za kontejner.
Nakon što Newman završi, skripta provjerava jesu li svi kontejneri s klijentom završeni, s kodom 0.
Sreća postoji
Nakon što smo prevladali gore opisane izazove, imali smo skup stabilnih testova. U testovima svaka usluga funkcionira kao jedna cjelina, komunicirajući s bazom podataka i Amazon LocalStackom.
Ovi testovi štite tim od 30+ programera od pogrešaka u aplikaciji sa složenim interakcijama između 10+ mikroservisa tijekom čestih implementacija.
Izvor: www.habr.com
