David Farey
W ostatnich latach architektury usługowe, a w szczególności mikrousługi, zdobyły ogromną popularność, tymczasem podejście do testów end-to-end (E2E) często pozostało bez zmian. Słyszymy, iż testy sprawdzające działanie całego systemu są najważniejsze w procesie tworzenia oprogramowania, zwłaszcza przy architekturach rozproszonych. Pojawiają się stwierdzenia w stylu „Musimy udowodnić, iż system działa jako całość. Kiedyś mieliśmy monolit i testy E2E, teraz mamy niezależne mikrousługi, więc testy E2E są tym bardziej potrzebne”.
W tym poście pojęcia testy E2E używam jako testy całego systemu. Chodzi o przypadki gdy działanie testu wymaga uruchomienia wielu usług. W związku z powyższym, dla przykładu testy frontendu z wykorzystaniem przeglądarki nie spełniają tej definicji jeżeli backend jest atrapą usługą, mockiem, stubem itp.
Monolit mentalny
Przecenianie wagi testów całego systemu to objaw myślenia monolitycznego. Gdy musimy testować system jako całość, oznacza to, iż najważniejsze atrybuty architektury rozproszonej, takie jak niezależność zmian i wdrożeń, nie są osiągnięte. Co więcej, niezależność wdrożeń jest wpisana w definicję mikrousług. To zdanie mogłoby zakończyć temat. Przyjrzyjmy się jednak bliżej negatywnym aspektom testów E2E w mikrousługach. W przypadku architektury usługowej, testy E2E nie tylko wiążą się z problemem ich szybkości, stabilności i złożoności, jak jest to opisywane w piramidzie testów, ale także wpływają istotnie na przepływ pracy. Jako, iż ich zadaniem jest testowanie kross-zespołowe, często tworzy się zespół testerów odpowiedzialny za ich tworzenie i utrzymanie. To rozwiązanie nie jest skalowalne i wprowadza opóźnienia, gdyż praca przechodzi przez kolejne zespoły – od deweloperów, przez testerów, aż po wdrożenie. W innym podejściu odpowiedzialność za testy E2E może być współdzielona przez wszystkie zespoły. W tym modelu powszechne jest to, iż zmiany w jednej usłudze mogą doprowadzić do błędów w testach i zablokować wdrożenia usługi innego zespołu. W obydwu konfiguracjach trudno będzie mieć wiele releasów dziennie a ich harmonogram będzie podatny na nieprzewidywalne opóźnienia. Zespoły deweloperskie stracą niezależność i będą poświęcały więcej czasu w komunikację. Wprowadzenie już pierwszego testu E2E wiąże się z pojawieniem szeregu problemów, takich jak decyzja kto będzie utrzymywał testy E2E, jak będą uruchamiane, jak zapewnić niezależności zespołów deweloperskich, jak utrzymać niezależność wdrożeniową itd.
Potrzeba testów E2E może wynikać z dwóch głównych powodów. Pierwszy to sytuacja, gdy mamy solidny system z niezależnymi usługami, ale menadżer utknął w monolicie mentalnym. Nie rozumie idei niezależnych usług z klarownymi granicami w postaci kontraktów. Do tego może dojść strach o stabilność systemu i wzięcie odpowiedzialności za błąd w przypadku awarii. W takim przypadku rozwiązaniem może być edukacja lub choćby zmiana menadżera. W skrajnym przypadku może to jednak oznaczać rewolucję kulturową w firmie. Drugi powód to stan naszej architektury, w której elementy tworzą rozproszony monolit i są silnie ze sobą związane. Warto wtedy przeanalizować kontrakty między usługami, sprawdzić, czy testy E2E nie są wynikiem rezygnacji ze ścisłych kontraktów na rzecz luźnych, czy API usług są konsumowane zgodnie z wzorcem X-as-a-service (XasS), czy zostały zastosowane consumer-driven contracts. Brak kontraktów to nie tylko brak opisu endpointów. Może on wybrzmiewać w sformułowaniach takich jak “system musi być uruchomiony na środowisku przed produkcyjnym lub UAT z danymi produkcyjnymi przez n dni, bo nie jesteśmy w stanie przewidzieć wszystkich przypadków danych i kombinacji zdarzeń”.
Jeśli zdecydujesz się na testy E2E, zadbaj o to, aby były szybkie i stabilne, co samo w sobie stanowi wyzwanie. Dodatkowo wprowadzenie pierwszego testu E2E będzie miało istotny wpływ na cały system. Wyzwaniem będzie określenie, kto będzie utrzymywał testy E2E oraz jak będą wpływały na cykl pracy wszystkich zespołów. Dlatego ogranicz ich zakres tylko do krytycznych obszarów systemu i kontroluj ich liczbę.
Jak żyć bez testów całego systemu
Podobnie, jak pisałem w innym poście, dokumentacja nie jest wymaganiem, tak i tu testy E2E nie są wymaganiem. Należy zastanowić się, jakie ryzyka chcemy zminimalizować i jakie problemy rozwiązać za ich pomocą. Jak inaczej możemy podejść do tych problemów. Wiele zależy od kontekstu, niemniej ostatecznie, w wielu sytuacjach można obejść się bez żadnych testów E2E.
Temat bezpiecznego wdrażania niezależnych usług jest obszerny, więc nie zamierzam go w całości omawiać. Poniżej podnoszę kilka kluczowych kwestii.
Po pierwsze, zdefiniuj granice usług poprzez kontrakty API. Kontrakty te powinny być dokładnie testowane. Przy projektowaniu usług warto również zastanowić się, które procesy wymagają przetestowania w całości i dlaczego. Wybór między orkiestracją a choreografią (https://medium.com/geekculture/microservices-orchestration-vs-choreography-technology-5dbe612cf7e9) ma wpływ na sposób testowania. Innymi słowy, zwykle łatwiej przetestować proces, który jest zarządzany przez jedną usługę, bo testy można ograniczyć to tej jednej usługi.
Aby zminimalizować ryzyko związane z wdrożeniem, warto zastosować praktyki takie jak blue-green deployment, canary release lub feature flags.
Ostatecznie, jeżeli system musi być testowany jako całość, może mikrousługi nie są najlepszym wyborem, może monolit i monorepo sprawdzi się lepiej.
Przekaz na dziś
Nie utknij w monolicie mentalnym. Monolit to nie tylko architektura, to także sposób myślenia o problemie.