• Akademia
  • Blog
  • O Serverless
  • O stronie

Testowanie serverless jeszcze nigdy nie by艂o tak proste!


Testowanie serverless
Z tego artyku艂u dowiesz si臋, jak wykorzysta艂em popularne narz臋dzia open-source do zbudowania szablonu serverlessowego mikroserwisu, kt贸ry znacz膮co usprawnia testowanie.

Na praktycznych przyk艂adach, nauczysz si臋 jak stosowa膰 pryncypia architektury heksagonalnej, aby zwi臋kszy膰 testowalno艣膰 i dojrza艂o艣膰 kodu. Zobacz, jak mo偶esz wykorzysta膰 wzorce projektowe i inne techniki, z kt贸rych korzystasz od lat. Nie musisz z nich rezygnowa膰 przechodz膮c na serverless!

Wr臋cz przeciwnie, bazuj膮c na moim do艣wiadczeniu, uwa偶am, 偶e wci膮偶 maj膮 one swoje miejsce w nowoczesnych rozwi膮zaniach, poniewa偶 zwi臋kszaj膮 czytelno艣膰, 艂atwo艣膰 utrzymania i testowalno艣膰 kodu 藕r贸d艂owego.

Je艣li uwa偶asz, 偶e s膮 lepsze sposoby na programowanie ni偶 skrypciarstwo (umieszczanie ca艂o艣ci kodu funkcji Lambda w jednym pliku i jednej metodzie) to pokochasz 鉂わ笍 to co dla Ciebie przygotowa艂em.

Brzmi zbyt dobrze, aby by艂o prawdziwe?

Poczekaj, jest wi臋cej! 馃槑

Zautomatyzowane testy integracyjne i end-to-end (e2e) znacz膮co usprawniaj膮 prac臋 dewelopera. Wreszcie mo偶esz uwolni膰 si臋 od cyklu: pisanie kodu -> deployment -> r臋czne uruchomienie Lambdy -> sprawdzenia log贸w -> poprawka b艂臋d贸w -> powr贸t na pocz膮tek.

Ale to nie wszystko!

NAJLEPSZA CZ臉艢膯: ten szablon jest dost臋pny na GitHubie zupe艂nie za darmo! 馃槂
Mo偶esz z niego skorzysta膰 ju偶 teraz!

za darmo

Zanim wyja艣ni臋 rozwi膮zanie, pozw贸l, 偶e podsumuj臋 typowe problemy, kt贸re doprowadzi艂y mnie do stworzenia tego szablonu.

Z艂udna prostota funkcji lambda

Ka偶dy programista stawiaj膮c swoje pierwsze kroki w serverless ma ju偶 du偶y baga偶 do艣wiadcze艅 zdobytych na wcze艣niejszych projektach. Najcz臋艣ciej oznacza to, 偶e tworzy艂 aplikacji w architekturze monolit贸w w ci臋偶kich j臋zykach (Java czy C#). Oczywi艣cie, cz臋艣膰 os贸b ma ju偶 du偶e do艣wiadczenie w mikroserwisach, ale to nadal wi臋ksze komponenty ni偶 funkcje.

Przeskakuj膮c na serverless pisany w JavaScript czy Python 艂atwo zach艂ysn膮膰 si臋 wolno艣ci膮 oferowan膮 przez te technologie. Funkcje s膮 na tyle ma艂e i proste, a brak (wymuszania u偶ycia) typ贸w sprawiaj膮 razem, 偶e wydaje si臋 i偶 nie trzeba pami臋ta膰 o dobrych praktykach rzemios艂a programistycznego. Nie ma nic z艂ego w eksperymentowaniu i bawieniu si臋. Niestety, zbyt cz臋sto spotykam si臋 z osobami, kt贸re zastosowa艂y skrypciarskie podej艣cie w systemach produkcyjnych, a teraz cierpi膮 z powodu s艂abej utrzymywalno艣ci i braku test贸w.

Bardzo kusz膮ce jest zaimplementowanie funkcji Lambda w zaledwie kilku linijkach. Niestety, na d艂u偶sz膮 met臋 to si臋 nie op艂aca.

Brak test贸w

Bezpo艣rednim efektem skrypciarskiej implementacji jest s艂aba testowalno艣膰. Monolityczny kod jest naprawd臋 trudny do przetestowania, wi臋c ludzie nie pisz膮 偶adnych test贸w. Po prostu tak jest. Konsekwencje braku test贸w s膮 raczej oczywiste dla do艣wiadczonych programist贸w, wi臋c nie b臋d臋 si臋 tutaj rozpisywa艂 na ten temat.

Jednak偶e, niekt贸rzy ludzie testuj膮 swoje aplikacje serverless. Pisz膮 zautomatyzowane testy jednostkowe dla logiki biznesowej lub kodu operuj膮cego na us艂ugach AWS u偶ywaj膮c mock贸w.

O ile mocki nie s膮 z艂e (sam ich u偶ywam), to trzeba wiedzie膰, kiedy powinno si臋 stosowa膰 t臋 technik臋. I co wa偶niejsze, kiedy nie 馃槈

Mockowanie wszystkich us艂ug AWS nie da Ci 偶adnej gwarancji, 偶e Tw贸j kod b臋dzie dzia艂a艂 po wdro偶eniu do chmury. Mocki daj膮 Ci fa艂szywe poczucie pewno艣ci dzia艂ania. To samo tyczy si臋 localstack i podobnych narz臋dzi, kt贸re emuluj膮 AWS w kontenerach.

Dlaczego testujemy?

Zastanawia艂e艣 si臋 mo偶e dlaczego w og贸le testujemy? W mojej opinii z dw贸ch powod贸w:

  • aby mie膰 zaufanie, 偶e nasz kod dzia艂a tak, jak nam si臋 wydaje
  • aby uchroni膰 si臋 przed b艂臋dami regresji po wprowadzeniu nowych zmian

Zautomatyzowany zestaw test贸w (test suite) da nam natychmiastow膮 informacj臋 zwrotn膮, 偶e co艣 jest nie tak z naszym kodem.

Jego brak zmusza do manualnego testowania po ka偶dej zmianie. Mo偶na te偶 by膰 odwa偶nym i wdro偶y膰 nowy kod prosto na prod. Nie no - ja tylko 偶artowa艂em 馃ぃ

Nie mam nic przeciwko manualnym testom, ale nie skaluj膮 si臋, wymagaj膮 znajomo艣ci systemu (np. nowa osoba nie b臋dzie wiedzia艂a jak i co testowa膰), i s膮 powolne. Ponadto, nie mo偶na ich wyegzekwowa膰. Mam na my艣li to, 偶e nie mo偶na uruchomi膰 test贸w manualnych w pipeline CI/CD.

Jest jeszcze jedna irytuj膮ca rzecz. Zbyt cz臋sto w projektach bez test贸w lub z kiepskimi testami s艂ysz臋, jak moi koledzy m贸wi膮 鈥溾le to dzia艂a艂o lokalnie na mojej maszynie鈥. Na prawd臋, nic mnie to nie obchodzi! 馃槫

Jako programista i osoba, kt贸ra bierze odpowiedzialno艣膰 za dostarczenie dzia艂aj膮cego rozwi膮zania, kt贸re jest wolne od b艂臋d贸w, musz臋 pisa膰 kod 艂atwy do przetestowania i utrzymania. I mie膰 pewno艣膰, 偶e dzia艂a on na prod w chmurze, a nie na czyim艣 laptopie.

Rozwi膮zanie: Jak testowa膰 serverless?

Aby rozwi膮za膰 powy偶sze problemy, przygotowa艂em szablon projektu dla Serverless Framework, kt贸ry stosuje zasady heksagonalnej architektury w 艣wiecie serverless.

Projekt szablonu zosta艂 stworzony aby osi膮gn膮膰 dwa cele: usprawniony flow pracy programisty oraz 艂atwe testowanie, poniewa偶 obie te rzeczy nie s膮 jeszcze powszechne w 艣wiecie serverless.

M贸j szablon dost臋pny jest na GitHubie pod nazw膮 serverless-hexagonal-template.

Jak z niego korzysta膰?

Aby zacz膮膰 go u偶ywa膰, nale偶y stworzy膰 nowy projekt na podstawie tego szablonu:

1
2
sls create --template-url https://github.com/serverlesspolska/serverless-hexagonal-template \ 
--name <nazwa-twojego-projektu>

Je艣li nie masz Serverless Framework, zainstaluj go: npm i -g serverless. Powy偶sza komenda stworzy Tw贸j nowy projekt na bazie szablonu. Wi臋cej informacji w dokumentacji na GitHubie.

Podej艣cie do testowania

Ca艂a konfiguracje boilerplate, w szczeg贸lno艣ci frameworka testowego jest, plugin贸w oraz innych narz臋dzi open-source jest zawarta w szablonie. Nowy projekt jest od razu gotowy do dzia艂ania.

Szablon zawiera dwie przyk艂adowe funkcje Lambda oraz zbi贸r:

  • test贸w jednostkowych (unit tests)
  • test贸w integracyjnych
  • test贸w end-to-end (e2e).

Podzia艂 ten zosta艂 wprowadzony, poniewa偶 r贸偶ne rodzaje test贸w spe艂niaj膮 r贸偶ne potrzeby.

r贸偶ne rodzaje test贸w

Testy jednostkowe s膮 wykonywane lokalnie (na komputerze dewelopera lub serwerze CI/CD) i nie wymagaj膮 dost臋pu do 偶adnych zasob贸w w chmurze AWS lub w Internecie.

Natomiast testy integracyjne oraz e2e wymagaj膮 rzeczywistych us艂ug AWS wdro偶onych w chmurze. Dlatego przed ich rozpocz臋ciem nale偶y zdeployowa膰 projekt wykonuj膮c sls deploy.

Testy integracyjne

Teraz wykonaj komend臋 npm run integration, aby uruchomi膰 testy integracyjne.

Specjalny plugin Serverless Framework (serverless-export-env) po艂膮czy si臋 z Twoim kontem AWS i zapisze lokalnie w pliku .awsenv wszystkie zmienne 艣rodowiskowe wszystkich funkcji Lambda zdefiniowanych w projekcie.

1
2
3
4
5
6
stage=dev
region=eu-central-1
service=serverless-hexagonal-template
httpApiGatewayEndpointId=qjc1234u19
tableName=serverless-hexagonal-template-dev
message=Hello World!
Przyk艂adowa zawarto艣膰 pliku .awsenv

Nast臋pnie, warto艣ci z tego pliku s膮 wstrzykiwane do kontekstu testowego jest. Oznacza to, 偶e ilekro膰 Tw贸j kod b臋dzie odwo艂ywa艂 si臋 do zmiennej 艣rodowiskowej np. process.env.MY_ENV_VAR w czasie wykonywania testu, otrzyma dok艂adnie tak膮 sam膮 warto艣膰 jak gdyby by艂 uruchamiany w chmurze.

W ten spos贸b kod aplikacji (mikroserwisu) mo偶e by膰 testowany lokalnie r贸wnocze艣nie u偶ywaj膮c prawdziwych zasob贸w w chmurze. Najlepsz膮 rzecz膮 jest to, 偶e pisz膮c czysty kod w my艣l architektury heksagonalnej, kod implementacji nie ma 艣wiadomo艣ci kontekstu testowego. Nie musisz do niego dodawa膰 偶adnych specjalnych rzeczy, aby da艂o rad臋 go przetestowa膰 (to by by艂o brzydkie, nieprawda偶?)

Testy automatyczne jest s膮 wykonywane lokalnie. Testuj膮 one twoje lokalne pliki z zasobami w chmurze. Na przyk艂ad, w serverless-hexagonal-template, zaimplementowa艂em testy, kt贸re u偶ywaj膮 tabeli DynamoDB w chmurze. 殴r贸d艂a test贸w znajdziesz tu i tu.

Testy integracyjne

Kolejny z test贸w (藕r贸d艂a) skupia si臋 na integracji AWS API Gateway i funkcji Lambda. Jest to o tyle istotne, 偶e rozwi膮zania serverless mocno zale偶膮 od wielu zasob贸w w chmurze. Wiele b艂臋d贸w wynika z niew艂a艣ciwej konfiguracji. Posiadanie takich test贸w integracyjnych pozwala na dok艂adne sprawdzenie tego obszaru.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const { default: axios } = require('axios')

axios.defaults.baseURL =
`https://${process.env.httpApiGatewayEndpointId}.execute-api.${process.env.region}.amazonaws.com`

describe('createItem function', () => {
it('should respond with statusCode 200 to correct request', async () => {
// GIVEN
const payload = {
a: 10,
b: 5,
method: 'add'
}

// WHEN
const actual = await axios.post('/item', payload)

// THEN
expect(actual.status).toBe(200)
})
...
Fragment testu integracyjnego.

Problemy z integracj膮 i konfiguracj膮 us艂ug s膮 g艂贸wnym motorem zmian dotycz膮cych tego, jak bran偶a patrzy na testowanie.

Teoria testowania

Po lewej klasyczna piramida test贸w. Po prawej plaster miodu zaproponowany przez Spotify. Opracowanie w艂asne.

W艂a艣nie dlatego tak du偶y nacisk k艂ad臋 na testy integracyjne, gdy偶 s膮 one po prostu wa偶niejsze w aplikacjach serverless.

Szczerze m贸wi膮c, nie tylko w serverless. W ka偶dym systemie rozproszonym testy jednostkowe to po prostu za ma艂o.

Testy end-to-end (e2e)

Czasami testy integracyjne nie wystarcz膮, poniewa偶 musimy przetestowa膰 ca艂y 艂a艅cuch komunikacji pomi臋dzy zestawem komponent贸w.
test e2e
Przyk艂adem takiego testu mo偶e by膰 偶膮danie POST wys艂ane do endpointu AWS API Gateway /item i sprawdzenie czy funkcja Lambda processItem zosta艂a wywo艂ana przez DynamoDB Streams w wyniku zapisania nowego elementu przez funkcj臋 Lambda createItem wywo艂an膮 przez 偶膮danie. Takie podej艣cie testuje 艂a艅cuch zdarze艅, kt贸re dziej膮 si臋 w chmurze i daje pewno艣膰, 偶e integracja pomi臋dzy wieloma us艂ugami jest dobrze skonfigurowana.

Wspomniane 艂a艅cuchy zdarze艅 to oczywi艣cie nic innego jak Architektura Sterowana Zdarzeniami (Event Driven Architecture) w praktyce. To w艂a艣nie one stanowi膮 o sile podej艣cia cloud-native i r贸wnocze艣nie powoduj膮, 偶e korzystanie z rozwi膮za艅 typu localstack jest ryzykowne (brak jakiejkolwiek gwarancji, 偶e te integracja dzia艂aj膮 lokalnie tak samo jak w AWS).

Przy okazji, testy e2e wspaniale si臋 sprawdzaj膮, gdy chcemy przetestowa膰 kod, kt贸ry 艂膮czy si臋 do baz RDS, do kt贸rych nie ma dost臋pu spoza VPC.

Architektura heksagonalna

W naturalny spos贸b wprowadza 艂ad do naszego kodu, gdy偶 podzia艂 na niezale偶ne modu艂y staje si臋 intuicyjny. Pozwala na lepsz膮 separacj臋 problem贸w i u艂atwia pisanie kodu zgodnego z zasad膮 SRP (Single Responsibility Principle). S膮 to kluczowe cechy architektury 艂atwej w utrzymaniu, rozbudowie i testowaniu.

Wyb贸r tego konkretnego stylu architektonicznego 艂膮czy si臋 z zaproponowan膮 struktur膮 katalog贸w projektu i konwencj膮 nazewnicz膮. Wi臋cej na ten temat mo偶na przeczyta膰 w dokumentacji.

Do艣膰 powiedzie膰, 偶e definiuje ona gdzie co powinno by膰 umieszczone (np. kod 藕r贸d艂owy w folderze src/, testy w __tests__/ itd.), dzi臋ki czemu nie trzeba traci膰 czasu na zastanawianie si臋 nad tym, za ka偶dym razem, gdy rozpoczyna si臋 nowy projekt. Dodatkowo, konwencja tworzy wsp贸lny j臋zyk dla cz艂onk贸w zespo艂u. W ten spos贸b zmniejsza si臋 przeci膮偶enie poznawcze programist贸w podczas prze艂膮czania si臋 pomi臋dzy projektami rozpocz臋tymi z tego szablonu.

Jak stworzy艂em szablon?

Szablon zosta艂 wypracowany w wyniku wieloletniej pracy z funkcjami Lambda z wykorzystaniem Serverless Framework. Czerpie te偶 ze zbiorowego do艣wiadczenia spo艂eczno艣ci (kt贸rej jestem wdzi臋czny) uciele艣nionego w ksi膮偶kach, wyst膮pieniach, nagraniach i artyku艂ach.

Osobi艣cie mia艂em do艣膰 kiepskiego, nieefektywnego workflow programisty serverless:

  • pisanie kodu
  • deployment
  • r臋czne uruchomienie Lambdy
  • sprawdzenia log贸w
  • poprawka b艂臋d贸w
  • powr贸t na pocz膮tek.

Znasz to sk膮d艣?

Postanowi艂em, 偶e chc臋 naprawi膰 ten problem. Skupi艂em si臋 na testowaniu, poniewa偶 wiedzia艂em, 偶e jego rozwi膮zanie pozwoli mi pracowa膰 w du偶o bardziej dojrza艂y spos贸b. Wiele lat temu by艂em programist膮 Java i wiedzia艂em, 偶e flow programisty mo偶e by膰 znacznie lepszy. 馃槂

Sp臋dzi艂em wiele wieczor贸w czytaj膮c o testowaniu serverless i eksperymentuj膮c. Na szcz臋艣cie ju偶 od jakiego艣 czasu korzysta艂em z heksagonalnej architektury, wi臋c 艂atwo by艂o mi my艣le膰 o testowaniu w kontek艣cie pojedynczych modu艂贸w, a nie ca艂ych funkcji Lambda. W ko艅cu znalaz艂em kilka artyku艂贸w na temat wtyczki serverless-export-env, kt贸ra okaza艂 si臋 by膰 brakuj膮cym ogniwem, kt贸re pozwoli艂o mi w 艂atwy, zautomatyzowany spos贸b dopi膮膰 wszystko razem. To by艂o dla mnie najwa偶niejsze. Wiedzia艂em, 偶e proces ten musi by膰 prosty i w pe艂ni generyczny, tak abym m贸g艂 go wykorzysta膰 w ka偶dym projekcie.

Kiedy zacz膮艂em stosowa膰 to podej艣cie, od razu zauwa偶y艂em, jak bardzo poprawi艂 si臋 m贸j workflow programisty. Wreszcie mog艂em wprowadza膰 zmiany w locie!

By艂em w stanie napisa膰 od 70 do 90 procent kodu bez ci膮g艂ych re-deployment贸w. To by艂a ogromna poprawa! W niekt贸rych przypadkach u偶ywa艂em TDD (Test Driven Development), kt贸re mo偶na stosowa膰 bez problemu w takim podej艣ciu.

Po wdro偶eniu kilku mikroserwis贸w (projekt贸w) mia艂em pewno艣膰, 偶e ta metoda si臋 sprawdza. Postanowi艂em, 偶e chc臋 si臋 podzieli膰 tym podej艣ciem ze spo艂eczno艣ci膮. Uwielbiam pomaga膰 ludziom na ca艂ym 艣wiecie budowa膰 systemy przy u偶yciu serverless. Pomaga膰 im uczy膰 si臋 i stawa膰 si臋 lepszymi programistami. To by艂a logiczna rzecz do zrobienia. Nie chc臋 tej wiedzy trzyma膰 tylko dla siebie 馃槑

Jednak zamiast pisa膰 zwyk艂y artyku艂, postanowi艂em stworzy膰 szablon Serverless Framework, kt贸ry zawiera wszystkie rzeczy i praktyki, kt贸re zna艂em, gotowe do u偶ycia natychmiast. Dzi臋ki temu, ka偶dy mo偶e z nich skorzysta膰.

Dlaczego powiniene艣 u偶y膰 tego szablonu?

W skr贸cie, u偶ycie serverless-hexagonal-template da Ci:

  • Gotowy do wdro偶enia na produkcji (production-ready) szablon mikroserwisu serverless
  • Wi臋ksze zaufanie do swojego rozwi膮zania (testy!)
  • Efektywny i powtarzalny workflow programisty
  • Dobrze przemy艣lan膮 struktur臋 projektu
  • Zwi臋kszon膮 reu偶ywalno艣膰 kodu
  • Czysty kod i dojrza艂y design - wykorzystanie wzorc贸w i dobrych praktyk, kt贸rych nauczy艂e艣 si臋 przez lata
  • Mo偶liwo艣膰 testowania w pipelinach CI/CD.

Ponadto:

  • Koniec z ci膮g艂ymi re-deploymentami w celu testowania kodu.
  • Koniec z r臋cznym testowaniem
  • Koniec ze skrypciarstwem
  • Koniec z b艂臋dami regresji
  • Koniec z wym贸wkami typu to dzia艂a艂o na moim komputerze 馃槈

Przetestowane na w艂asnej sk贸rze鈩 ;-)

Moja przygoda z serverless trwa od 2016 roku. Zanim zacz膮艂em stosowa膰 opisane tutaj podej艣cie mia艂em wiele projekt贸w z testami jednostkowymi lub bez test贸w w og贸le. Ci臋偶ko by艂o dodawa膰 do nich nowe funkcjonalno艣ci bez wprowadzania nowych bug贸w (b艂臋d贸w regresji) lub przynajmniej obawy takiej ewentualno艣ci. Testy jednostkowe po prostu nie wystarcza艂y, a ja nie wiedzia艂em jak napisa膰 poprawne testy na moje b艂臋dy. Ka偶da zmiana musia艂a by膰 zdeployowana i r臋cznie przetestowana.

Obecnie, wdra偶anie i modyfikowanie projekt贸w to zupe艂nie inna historia. Dodanie test贸w integracyjnych i e2e pozwoli艂o mi komfortowo wprowadza膰 zmiany bez obaw o b艂臋dy regresji. Mojej pracy nie przerywaj膮 ju偶 ci膮g艂e re-deploymenty projekt贸w do chmury. Oczywi艣cie, nadal s膮 one potrzebne, ale prawie wszystko mo偶na przetestowa膰 od razu po pierwszym deploymencie i poprawnym zdefiniowaniu zmiennych 艣rodowiskowych.

Podsumowuj膮c, to podej艣cie oszcz臋dza to sporo czasu i u艂atwia 偶ycie dewelopera.

Try it out!

Wi臋c, je艣li chcesz mie膰 super workflow programisty i dobrze przetestowany system spr贸buj tego rozwi膮zania. Zajmie Ci to nie wi臋cej ni偶 5 minut.

  1. Stw贸rz sw贸j projekt z szablonu sls create --template-url https://github.com/serverlesspolska/serverless-hexagonal-template --name <nazwa twojego projektu>.
  2. Zainstaluj zale偶no艣ci npm i.
  3. Wykonaj testy jednostkowe npm run test.
  4. Zr贸b wdro偶enie do chmury sls deploy.
  5. Uruchom testy integracyjne npm run integration
  6. Uruchom testy end-to-end npm run e2e.

Nast臋pnie przeanalizuj m贸j kod i sprawd藕 jak pisa膰 testy do aplikacji serverless. Zacznij u偶ywa膰 tego szablonu w swoich projektach, a nast臋pnie daj mi gwiazdk臋 猸愶笍 na GitHubie: serverless-hexagonal-template. Powodzenia i mi艂ego testowania!



Chcesz zobaczy膰 wideo o tym, jak u偶y膰 szablonu?

Temat testowania Ci臋 wci膮gn膮艂 i masz ochot臋 na wi臋cej. 艢wietnie. Mam co艣 dla Ciebie. 馃巵
Specjalna seria wiedzy w 5-ciu mailach.