Udostępnij za pośrednictwem


Testowanie przesunięcia w lewo z testami jednostkowymi

Testowanie pomaga zapewnić, że kod działa zgodnie z oczekiwaniami, ale czas i wysiłek kompilacji testów zajmuje trochę czasu od innych zadań, takich jak tworzenie funkcji. W przypadku tego kosztu ważne jest wyodrębnienie maksymalnej wartości z testowania. W tym artykule omówiono zasady testowania metodyki DevOps, koncentrując się na wartości testowania jednostkowego i strategii testowania po lewej stronie.

Dedykowani testerzy używali do pisania większości testów, a wielu deweloperów produktów nie nauczyło się pisać testów jednostkowych. Pisanie testów może wydawać się zbyt trudne lub jak zbyt wiele pracy. Istnieje sceptycyzm co do tego, czy strategia testów jednostkowych działa, złe doświadczenia z słabo napisanymi testami jednostkowymi lub obawiają się, że testy jednostkowe zastąpią testy funkcjonalne.

Graphic that describes arguments about adopting unit testing.

Aby wdrożyć strategię testowania Metodyki DevOps, bądź pragmatyczny i skupić się na budowaniu tempa. Mimo że można nalegać na testy jednostkowe dla nowego kodu lub istniejącego kodu, który może być czystą refaktoryzowaną, może to mieć sens, aby starsza baza kodu zezwalała na pewną zależność. Jeśli znaczna część kodu produktu korzysta z języka SQL, co pozwala testom jednostkowym na zależność od dostawcy zasobów SQL zamiast pozorować , że warstwa może być krótkoterminowym podejściem do postępu.

Gdy organizacje DevOps dojrzały, staje się łatwiejsze dla kierownictwa w celu ulepszania procesów. Chociaż może istnieć pewien opór wobec zmian, organizacje Agile wartości zmieniają się, które wyraźnie płacą dywidendy. Powinno być łatwe do sprzedaży wizji szybszych przebiegów testów z mniejszą liczbą błędów, ponieważ oznacza to więcej czasu na inwestowanie w generowanie nowej wartości dzięki tworzeniu funkcji.

Taksonomia testowa metodyki DevOps

Definiowanie taksonomii testowej jest ważnym aspektem procesu testowania Metodyki DevOps. Test metodyki DevOps klasyfikuje poszczególne testy według ich zależności i czas ich uruchomienia. Deweloperzy powinni zrozumieć odpowiednie typy testów do użycia w różnych scenariuszach i które wymagają testów różnych części procesu. Większość organizacji kategoryzuje testy na czterech poziomach:

  • Testy L0 i L1 to testy jednostkowe lub testy, które zależą od kodu w zestawie testowym i nic innego. L0 to szeroka klasa szybkich, w pamięci testów jednostkowych.
  • L2 to testy funkcjonalne, które mogą wymagać zestawu oraz innych zależności, takich jak SQL lub system plików.
  • Testy funkcjonalne L3 są uruchamiane względem wdrożeń usług testowalnych. Ta kategoria testowa wymaga wdrożenia usługi, ale może używać wycinków dla zależności usługi kluczy.
  • Testy L4 to ograniczona klasa testów integracji, które są uruchamiane względem środowiska produkcyjnego. Testy L4 wymagają pełnego wdrożenia produktu.

Chociaż byłoby idealnym rozwiązaniem dla wszystkich testów do uruchamiania przez cały czas, nie jest to możliwe. Zespoły mogą wybrać miejsce w procesie DevOps, aby uruchomić każdy test, i użyć strategii shift-left lub shift-right , aby przenieść różne typy testów wcześniej lub później w procesie.

Na przykład oczekiwania mogą być następujące, że deweloperzy zawsze uruchamiają testy L2 przed zatwierdzeniem, żądanie ściągnięcia automatycznie kończy się niepowodzeniem, jeśli przebieg testu L3 zakończy się niepowodzeniem, a wdrożenie może zostać zablokowane w przypadku niepowodzenia testów L4. Określone reguły mogą się różnić w zależności od organizacji, ale wymuszanie oczekiwań dla wszystkich zespołów w organizacji przenosi wszystkich w kierunku tych samych celów związanych z wizją jakości.

Wytyczne dotyczące testów jednostkowych

Ustaw ścisłe wytyczne dotyczące testów jednostkowych L0 i L1. Testy te muszą być bardzo szybkie i niezawodne. Na przykład średni czas wykonywania na test L0 w zestawie powinien być krótszy niż 60 milisekund. Średni czas wykonywania na test L1 w zestawie powinien wynosić mniej niż 400 milisekund. Żaden test na tym poziomie nie powinien przekraczać 2 sekund.

Jeden zespół firmy Microsoft uruchamia ponad 60 000 testów jednostkowych równolegle w mniej niż sześć minut. Ich celem jest skrócenie tego czasu do mniej niż minuty. Zespół śledzi czas wykonywania testu jednostkowego za pomocą narzędzi, takich jak poniższy wykres, oraz pliki usterek względem testów, które przekraczają dozwolony czas.

Chart that shows continuous focus on test execution time.

Wskazówki dotyczące testów funkcjonalnych

Testy funkcjonalne muszą być niezależne. Kluczową koncepcją testów L2 jest izolacja. Prawidłowo izolowane testy mogą działać niezawodnie w dowolnej sekwencji, ponieważ mają pełną kontrolę nad środowiskiem, w jakim działają. Stan musi być znany na początku testu. Jeśli jeden test utworzył dane i opuścił go w bazie danych, może uszkodzić przebieg innego testu, który opiera się na innym stanie bazy danych.

Starsze testy, które wymagają tożsamości użytkownika, mogły wywoływać zewnętrznych dostawców uwierzytelniania w celu uzyskania tożsamości. Ta praktyka wprowadza kilka wyzwań. Zależność zewnętrzna może być zawodna lub niedostępna chwilowo, przerywając test. Ta praktyka narusza również zasadę izolacji testowej, ponieważ test może zmienić stan tożsamości, na przykład uprawnienie, co powoduje nieoczekiwany stan domyślny dla innych testów. Rozważ zapobieganie tym problemom, inwestując w obsługę tożsamości w ramach struktury testowej.

Zasady testowania metodyki DevOps

Aby ułatwić przejście portfolio testowego do nowoczesnych procesów DevOps, przedstawienie wizji jakości. Zespoły powinny przestrzegać następujących zasad testowania podczas definiowania i implementowania strategii testowania Metodyki DevOps.

Diagram that shows an example of a quality vision and lists test principles.

Przesunięcie w lewo, aby przetestować wcześniej

Uruchamianie testów może zająć dużo czasu. W miarę skalowania projektów znacznie rośnie liczba testów i typy. Kiedy zestawy testów rosną, aby potrwać godziny lub dni, mogą wypchnąć dalej, dopóki nie będą działać w ostatniej chwili. Korzyści z jakości kodu z testowania nie są realizowane dopiero po zatwierdzeniu kodu.

Długotrwałe testy mogą również powodować błędy, które są czasochłonne do zbadania. Zespoły mogą budować tolerancję dla niepowodzeń, szczególnie na wczesnym etapie przebiegu. Ta tolerancja podważa wartość testowania jako wgląd w jakość bazy kodu. Długotrwałe, w ostatniej chwili testy dodają również nieprzewidywalność oczekiwań dotyczących zakończenia przebiegu, ponieważ nieznana kwota długu technicznego musi zostać zapłacona, aby można było wysłać kod.

Celem przesunięcia testowania w lewo jest przeniesienie jakości nadrzędnej przez wykonanie zadań testowych wcześniej w potoku. Dzięki połączeniu ulepszeń testów i procesów przesunięcie w lewo zmniejsza zarówno czas potrzebny na uruchomienie testów, jak i wpływ awarii w dalszej części cyklu. Przesunięcie w lewo gwarantuje, że większość testów zostanie ukończona przed scaleniem zmiany z gałęzią główną.

Diagram that shows the move to shift-left testing.

Oprócz zmiany pewnych obowiązków związanych z testowaniem w lewo w celu poprawy jakości kodu zespoły mogą zmienić inne aspekty testowe w prawo lub później w cyklu DevOps, aby ulepszyć końcowy produkt. Aby uzyskać więcej informacji, zobacz Shift right to test in production (Przesunięcie w prawo do testowania w środowisku produkcyjnym).

Pisanie testów na najniższym możliwym poziomie

Napisz więcej testów jednostkowych. Faworyzowanie testów przy użyciu najmniejszych zależności zewnętrznych i skupianie się na uruchamianiu większości testów w ramach kompilacji. Rozważ równoległy system kompilacji, który może uruchamiać testy jednostkowe dla zestawu zaraz po upuszczaniu zestawu i skojarzonych testów. Nie jest możliwe przetestowanie każdego aspektu usługi na tym poziomie, ale zasadą jest użycie lżejszych testów jednostkowych, jeśli mogą one uzyskać te same wyniki co cięższe testy funkcjonalne.

Dążenie do niezawodności testów

Zawodny test jest kosztowny w organizacji do utrzymania. Taki test działa bezpośrednio przeciwko celowi wydajności inżynieryjnej, utrudniając wprowadzanie zmian z ufnością. Deweloperzy powinni mieć możliwość wprowadzania zmian w dowolnym miejscu i szybko zyskać pewność, że nic nie zostało przerwane. Zachowaj wysoki słupek pod kątem niezawodności. Zniechęć do korzystania z testów interfejsu użytkownika, ponieważ wydają się być zawodne.

Pisanie testów funkcjonalnych, które mogą być uruchamiane w dowolnym miejscu

Testy mogą używać wyspecjalizowanych punktów integracji zaprojektowanych specjalnie w celu umożliwienia testowania. Jednym z powodów tej praktyki jest brak możliwości testowania w samym produkcie. Niestety testy takie często zależą od wewnętrznej wiedzy i używają szczegółów implementacji, które nie mają znaczenia z perspektywy testu funkcjonalnego. Te testy są ograniczone do środowisk, które mają wpisy tajne i konfigurację niezbędną do uruchamiania testów, co zwykle wyklucza wdrożenia produkcyjne. Testy funkcjonalne powinny używać tylko publicznego interfejsu API produktu.

Projektowanie produktów pod kątem testowania

Organizacje w procesie dojrzewania metodyki DevOps mają pełny wgląd w to, co to znaczy dostarczać produkt jakościowy w cyklach wdrażania chmury. Zmiana równowagi zdecydowanie na korzyść testowania jednostkowego nad testowaniem funkcjonalnym wymaga od zespołów dokonania wyborów projektowych i implementacji, które obsługują testowanie. Istnieją różne pomysły na to, co stanowi dobrze zaprojektowany i dobrze zaimplementowany kod do testowania, tak jak istnieją różne style kodowania. Zasada polega na tym, że projektowanie pod kątem możliwości testowania musi stać się główną częścią dyskusji na temat projektowania i jakości kodu.

Traktuj kod testowy jako kod produktu

Jawnie stwierdzając, że kod testowy jest kodem produktu, jasno pokazuje, że jakość kodu testowego jest równie ważna, jak wysyłka kodu produktu. Zespoły powinny traktować kod testowy w taki sam sposób, w jaki traktują kod produktu, i stosować ten sam poziom opieki do projektowania i implementacji testów i struktur testowych. Ten wysiłek jest podobny do zarządzania konfiguracją i infrastrukturą jako kodem. Aby ukończyć, przegląd kodu powinien rozważyć kod testowy i przechowywać go na tym samym pasku jakości co kod produktu.

Korzystanie z udostępnionej infrastruktury testów

Obniż poprzeczkę używania infrastruktury testowej do generowania zaufanych sygnałów jakości. Wyświetlanie testów jako usługi udostępnionej dla całego zespołu. Zapisz kod testu jednostkowego wraz z kodem produktu i skompiluj go przy użyciu produktu. Testy uruchamiane w ramach procesu kompilacji muszą być również uruchamiane w ramach narzędzi programistycznych, takich jak Azure DevOps. Jeśli testy mogą być uruchamiane w każdym środowisku od lokalnego programowania za pośrednictwem środowiska produkcyjnego, mają taką samą niezawodność jak kod produktu.

Tworzenie właścicieli kodu odpowiedzialnych za testowanie

Kod testowy powinien znajdować się obok kodu produktu w repozytorium. Aby kod był testowany na granicy składnika, wypychaj odpowiedzialność za testowanie do osoby piszącej kod składnika. Nie polegaj na innych użytkownikach, aby przetestować składnik.

Analiza przypadku: Przesunięcie w lewo z testami jednostkowymi

Zespół firmy Microsoft zdecydował się zastąpić starsze zestawy testów nowoczesnymi, testami jednostkowymi DevOps i procesem przesunięcia w lewo. Zespół śledził postępy w przebiegach triweekly, jak pokazano na poniższym wykresie. Wykres obejmuje przebiegi 78-120, które reprezentują 42 sprinty w ciągu 126 tygodni, czyli około dwóch i pół roku wysiłku.

Zespół rozpoczął 27K starszych testów w przebiegu 78 i osiągnął zero starszych testów w S120. Zestaw testów jednostkowych L0 i L1 zastąpił większość starych testów funkcjonalnych. Nowe testy L2 zastąpiły niektóre testy, a wiele starych testów zostało usuniętych.

Diagram that shows a sample test portfolio balance over time.

W ramach podróży programowej, która zajmuje ponad dwa lata, istnieje wiele do nauki z samego procesu. Ogólnie rzecz biorąc, wysiłek, aby całkowicie ponownie przeprowadzić system testowy w ciągu dwóch lat, był ogromną inwestycją. Nie każdy zespół funkcji pracował w tym samym czasie. Wiele zespołów w całej organizacji zainwestowało czas w każdy przebieg, a w niektórych sprintach większość tego, co zrobił zespół. Chociaż trudno jest zmierzyć koszty zmiany, było to nienegocjacyjne wymaganie dotyczące jakości i wyników zespołu.

Wprowadzenie

Na początku zespół opuścił stare testy funkcjonalne, nazywane testami TRA, sam. Zespół chciał, aby deweloperzy mogli kupić pomysł pisania testów jednostkowych, szczególnie w przypadku nowych funkcji. Skupiono się na jak najłatwiejszym tworzeniu testów L0 i L1. Zespół musiał najpierw opracować tę funkcję i zbudować rozmach.

Na powyższym wykresie przedstawiono liczbę testów jednostkowych rozpoczynających się wcześnie, ponieważ zespół widział korzyść z tworzenia testów jednostkowych. Testy jednostkowe były łatwiejsze do utrzymania, szybsze do uruchomienia i miały mniej błędów. Łatwo było uzyskać obsługę uruchamiania wszystkich testów jednostkowych w przepływie żądania ściągnięcia.

Zespół nie skupił się na pisaniu nowych testów L2 do sprintu 101. W międzyczasie liczba testów TRA spadła z 27 000 do 14 000 z Sprint 78 do Sprint 101. Nowe testy jednostkowe zastąpiły niektóre testy TRA, ale wiele z nich zostało po prostu usuniętych na podstawie analizy ich przydatności przez zespół.

Testy TRA skoczyły z 2100 do 3800 w przebiegu 110, ponieważ więcej testów zostało odnalezionych w drzewie źródłowym i dodanych do grafu. Okazało się, że testy zawsze były uruchomione, ale nie były śledzone prawidłowo. To nie był kryzys, ale ważne było, aby być uczciwym i ponownie ocenić w razie potrzeby.

Szybsze uzyskiwanie

Gdy zespół miał sygnał ciągłej integracji, który był niezwykle szybki i niezawodny, stał się zaufanym wskaźnikiem jakości produktu. Poniższy zrzut ekranu przedstawia działanie żądania ściągnięcia i potoku ciągłej integracji oraz czas potrzebny na przejście przez różne fazy.

Diagram that shows the pull request and rolling CI pipeline in action.

Od żądania ściągnięcia do scalania trwa około 30 minut, co obejmuje uruchamianie 60 000 testów jednostkowych. Od scalania kodu do kompilacji ciągłej integracji trwa około 22 minut. Pierwszy sygnał jakości z CI, SelfTest, pojawia się po około godzinie. Następnie większość produktu jest testowana z proponowaną zmianą. W ciągu dwóch godzin od scalania do selfhost cały produkt jest testowany, a zmiana jest gotowa do przejścia do środowiska produkcyjnego.

Korzystanie z metryk

Zespół śledzi kartę wyników, jak w poniższym przykładzie. Na wysokim poziomie karta wyników śledzi dwa typy metryk: Kondycja lub dług i szybkość.

Diagram that shows a metrics scorecard for tracking test performance.

W przypadku metryk kondycji lokacji na żywo zespół śledzi czas wykrywania, czasu łagodzenia i liczby elementów naprawy, które prowadzi zespół. Element naprawy to praca, która zespół identyfikuje w retrospektywie na żywo witryny, aby zapobiec cykliowaniu podobnych zdarzeń. Karta wyników śledzi również, czy zespoły zamykają elementy naprawy w rozsądnym przedziale czasu.

W przypadku metryk kondycji inżynieryjnej zespół śledzi aktywne usterki dla każdego dewelopera. Jeśli zespół ma więcej niż pięć usterek na dewelopera, zespół musi ustalić priorytety tych usterek przed utworzeniem nowej funkcji. Zespół śledzi również starzejące się usterki w specjalnych kategoriach, takich jak zabezpieczenia.

Metryki prędkości inżynieryjnej mierzą szybkość w różnych częściach potoku ciągłej integracji i ciągłego dostarczania (CI/CD). Ogólnym celem jest zwiększenie szybkości potoku DevOps: rozpoczęcie od pomysłu, wprowadzenie kodu do środowiska produkcyjnego i odbieranie danych z powrotem od klientów.

Następne kroki