Sytuacja kobiet w IT w 2024 roku
31.10.201810 min
Ericsson

Jakub JaniszewskiSenior Software Developer - MasterEricsson

Jak budować jakość oprogramowania?

Dowiedz się, jak monitorować jakość oprogramowania i wcześnie wykrywać błędy w kodzie za pomocą strategii testów, ich automatyzacji czy CI.

Jak budować jakość oprogramowania?

Żyjemy w dobie, gdy telewizory, pralki i lodówki często posiadają bardziej zaawansowane funkcje niż telefony sprzed niecałej dekady. Również pisane przez nas serwisy transakcyjne, aplikacje mobilne czy programy desktopowe muszą obsługiwać coraz więcej urządzeń, standardów i funkcji. Stopień skomplikowania tworzonego przez nas oprogramowania ciągle rośnie, a wraz z nim powiększa się prawdopodobieństwo wystąpienia poważnych błędów.

Pracującemu przy nich programiście może zdarzyć się pomyłka kosztująca miliony.

Żadna organizacja nie chciałaby zmierzyć się z wymianą dziesięciu tysięcy pralek czy też łatania wycieku danych klienta tylko ze względu na drobną pomyłkę programistyczną. Nie dziwi więc fakt, że coraz więcej firm sięga po rozbudowane rozwiązania testowe, starając się szczegółowo i systematycznie badać niezawodność dostarczanych produktów.

Jak zadbać o to, by tworzone przez nas oprogramowanie miało odpowiednią jakość? Jak monitorować to, co dostarczamy i sprawić, by nasza firma zaczęła efektywnie wykrywać błędy - zanim poinformuje nas o nich klient? Właśnie ten temat chciałbym dzisiaj zgłębić.

Kultura testu buduje jakość 

Zapewne każdy z nas spotkał się kiedyś z koncepcją wielopoziomowego testowania produktu, a pojęcia takie jak testy jednostkowe, integracyjne, systemowe czy też akceptacyjne siłą rzeczy obiły nam się o uszy. Ale czy każdy podskórnie rozumie ich znaczenie? Z mojego doświadczenia wynika, że jest wręcz przeciwnie. Kiedyś zadałem proste pytanie:

Jakie rodzaje testów realizujecie w swoich projektach?

Padło ono w gronie specjalistów QA pracujących w tej samej firmie IT przy bardzo zbliżonej grupie produktowej. Zgodnie z moimi oczekiwaniami każdy z testerów odpowiedział mniej więcej zbliżoną formułką:

Testy jednostkowe, integracyjne i akceptacyjne.

Każdy z nich był zgodny co do tego, że testują produkt w zbliżony sposób. Zadałem więc kolejne pytanie:

Co macie na myśli, gdy mówicie o testach integracyjnych?

Rozstrzał odpowiedzi przerósł moje najśmielsze oczekiwania. Dla jednych były to testy integracyjne komponentów, dla innych testy kompatybilności nowo pisanej funkcjonalności lub sprawdzanie działania całego produktu w kontekście większego systemu informatycznego. Nagle okazało się, że każdy zespół testuje produkt zupełnie inaczej - zabrakło wspólnej wizji.

Chcąc tworzyć organizację, która idee jakości i profesjonalizmu ma zapisane w DNA, musimy zacząć od określenia, po co właściwie to robimy. W tym celu powinna nam posłużyć strategia testu - dokument, którego znaczenia nie sposób przecenić. Dobrze napisana strategia testu jasno komunikuje, co chcemy testować, w jakim celu, na jakim etapie oraz jakimi metodami. Dzięki niej - w czytelny i ogólnodostępny dla każdego w firmie sposób - możemy zdefiniować rodzaje i poziomy testu. Możemy też jednoznacznie pokazać, czego w danym przypadku zdecydowaliśmy się nie testować, przybliżając wiążące się z tym ryzyko.

Samą strategią testu nie sprawimy jednak, że nasze produkty będą lepiej weryfikowane. Chcąc tworzyć wysokiej jakości oprogramowanie musimy zadbać o właściwe nastawienie ludzi do samej tematyki testu. Jeszcze dekadę temu powszechnie panowało pojęcie testera jako informatyka gorszej kategorii - człowieka, który wybrał ten kierunek rozwoju, bo nie było mu po drodze z programowaniem. Sam proces testowania traktowany był przez to jako zadanie pośledniego sortu. Stereotyp ten przetrwał i sprawił, że po dziś dzień wielu developerów podchodzi do kwestii testu jak do zła koniecznego, albo - co gorsza - niepotrzebnego wydatku energetycznego.

Sytuacji nie poprawia fakt, że rzadko która firma dba o szkolenie programistów w dziedzinie podstaw testowania. Wychodzi się tu z założenia jasnego podziału ról: programista dostarcza kod, tester go sprawdza. Takie podejście - choć klarowne - może prowadzić do wielu nieporozumień. Sprzyja też wytworzeniu syndromu braku poczucia odpowiedzialności za zapewnioną zmianę (dostarczę to w takim stanie, w jakim jest, a ewentualne błędy wykryje tester). Takie nastawienie nie sprzyja budowie kultury pracy nastawionej na jakość.

Według mnie dużo lepiej sprawdza się tu model, na jaki zdecydowano się w mojej firmie. Ericsson w wielu przypadkach nie przypisuje jednej bądź drugiej roli pracownikom. Widać to chociażby w ujednoliconym nazewnictwie oficjalnych stanowisk. Większość z pracowników poszczycić się może szeroko pojętym tytułem: „developer” - nie bez powodu. Nazwa ta najbliżej oddaje to, czym osoba zatrudniona w dziale R&D zajmuje się na co dzień.

Pracując w zespole rozwijającym nowe technologie każdy z developerów odpowiada zarówno za tworzenie kodu produkcyjnego, jak i testowanie. To członkowie zespołu sami wybierają, w czym czują się lepiej i w jakiej dziedzinie chcą rozwijać swoje umiejętności. Dobierając swoje codzienne zadania muszą jednak pamiętać o wspólnym celu, jakim jest zapewnienie zarówno nowych funkcjonalności, jak i testów, które udowodnią i zabezpieczą ich poprawne działanie.

Znika dzięki temu magiczna bariera między pracą programisty, a testera - każdy jest po trochu jednym, jak i drugim. Z czasem pracownik wyrabia w sobie zwyczaj myślenia o kodzie i testach jak o jednym produkcie. Dzięki takiemu podejściu tworzy się synergię między programowaniem, a testowaniem - czyli coś, na czym powinno nam najbardziej zależeć, jeśli chcemy tworzyć organizację nastawioną na jakość.

Automatyzacja - klucz do efektywnego zarządzania kodem

Dobre testy pomagają zrozumieć tworzone oprogramowanie, jego możliwości i ograniczenia. Dzięki ich automatyzacji zyskujemy możliwość wprowadzania zmian w kodzie w dowolnym momencie - bez obawy o to, że wraz z nowo dodaną implementacją uszkodzimy którykolwiek z istniejących dotychczas komponentów.

Zgłębiając tematykę QA szybko możemy natknąć się na tzw. piramidę testu. Koncepcja ta dobrze obrazuje, jak rozłożone powinny być siły przerobowe w kwestii automatyzacji. Im bliżej znajdujemy się podstawy piramidy, tym bardziej powinno nam zależeć na wysokim pokryciu testami. Wraz z poruszaniem się w stronę jej czubka opłacalność automatyzacji spada. Idzie to w parze z koncepcją wczesnego wykrywania błędów.

Chcąc więc zadbać o jakość dostarczanych rozwiązań powinniśmy w pierwszej kolejności zwrócić uwagę na nasze testy jednostkowe. Tu weryfikowane powinny zostać najmniejsze części systemu (metody, klasy, moduły). Testy jednostkowe powinny dawać nam szybki pogląd na efekt naszych bezpośrednich zmian w kodzie. W wielu firmach za ten poziom testów odpowiedzialni są programiści, dlatego też uczulam na kluczowe znaczenie podstawowego szkolenia tej grupy w dziedzinie weryfikacji kodu.

Testy integracyjne są już typowym obszarem zainteresowań specjalistów QA. Jak sama nazwa wskazuje, domeną tego poziomu jest sprawdzenie, jak dostarczana zmiana integruje się z istniejącymi modułami, funkcjami czy też innymi cząstkami systemu. W tym przypadku najczęściej mówimy więc o testowaniu interfejsów między dwoma niezależnymi jednostkami - czymkolwiek są owe jednostki w naszym projekcie. Jest to najszersza pojęciowo grupa testów, która sama w sobie może zawierać wiele podpoziomów integracji. To tu najczęściej sprawdzane są zasady działania poszczególnych, nowo tworzonych funkcji.

Dużo bardziej zaawansowanymi testami są testy systemowe. W tym przypadku mówimy o weryfikacji przeprowadzanej w środowisku jak najbardziej zbliżonym do finalnego środowiska produkcyjnego. Testy wykonywane na tym poziomie powinny koncentrować się na sprawdzeniu, jak dobrze produkowany przez nas system spełnia postawione przed nim założenia funkcjonalne i niefunkcjonalne. Automatyzacja na tym poziomie bywa często bardzo kosztowna, a czas wykonywania poszczególnych testów może dyskwalifikować je ze standardowej formuły codziennego sprawdzania jakości kodu. Należy więc zastanowić się nad stopniem opłacalności takiej inwestycji. W niektórych przypadkach okazać się może, że testy manualne są lepszym rozwiązaniem.

Ostatnim szczeblem klasycznej piramidy są testy akceptacyjne. Ich głównym celem nie jest z założenia znajdowanie nowych błędów, lecz potwierdzenie, że wyprodukowana przez nas funkcja spełnia wymagania oraz potrzeby kontrahenta. W klasycznym podejściu testy akceptacyjne wykonywane są w jednym z ostatnich etapów weryfikacji wdrażanego oprogramowania, najczęściej przez zewnętrzny zespół klienta. Jednak model ten widywany jest coraz rzadziej. Wzrost znaczenia metodyk zwinnych oraz popularyzacja idei takich jak Behavior-Driven Development (BDD) sprawiają, że coraz więcej firm decyduje się na przeprowadzanie części testów tego poziomu jeszcze w ramach własnej organizacji.

Nie ma w tym nic dziwnego. Korzystając z popularnych rozwiązań wspierających BDD (takich jak Cucumber, lub jBehave) możemy w łatwo zrozumiały sposób ustalić z klientem wspólną wizję tego, co powinno zostać zautomatyzowane. Przy zachowaniu proporcji sugerowanych przez piramidę testu możemy dzięki temu uzyskać też czytelną dokumentację naszego produktu. Jeżeli obie strony kontraktu w pełni respektują wyniki takich działań, to takie podejście do testów akceptacyjnych może okazać się fenomenalną metodą na przyspieszenie ostatecznego wdrożenia produktu.

CI

Same testy automatyczne dadzą jednak niewiele, jeśli nie zadbamy o to, by były puszczane regularnie. Tu z pomocą przychodzi nam koncepcja CI (Continuous Integration) - czyli praktyka programistyczna zakładająca ciągłe, systematyczne weryfikowanie i dostarczanie naszych zmian do wspólnego repozytorium. Idea ta zakorzeniła się już na stałe w wielu dużych firmach programistycznych. Sprzyja temu wsparcie wyspecjalizowanego oprogramowania umożliwiającego automatyzację i precyzyjne kształtowanie tego, w jaki sposób dostarczany i testowany jest kod. Dzięki rozwiązaniom takim jak popularny Jenkins z łatwością możemy zadbać o weryfikację pod względem statycznej analizy kodu, testów jednostkowych, integracyjnych czy też systemowych. Możemy też zadecydować, jakie operacje ma podjąć system w przypadku wykrycia błędu. Opcji mamy wiele - od automatycznie generowanego emaila informującego o zaistniałej sytuacji, aż po bardziej kontrowersyjne, całkowite zamrożenie repozytorium. Tylko od nas zależy, jak bardzo restrykcyjni i dokładni chcemy być w kształtowaniu naszej ścieżki integracyjnej.

Dzięki odpowiednio skonfigurowanemu środowisku CI możemy też zaoszczędzić czas potrzebny na tworzenie żmudnej dokumentacji testowej. Właściwie dobierając frameworki oraz plug-iny możemy uniknąć rozpisywania kampanii testowych, wypełniania końcowych raportów potwierdzających efekty weryfikacji czy też ręcznego zbierania statystyk udowadniających np. wystarczające pokrycie wymagań klienta. Wszystkie te dokumenty możemy wygenerować automatycznie i - co ważniejsze - po każdej nawet najmniejszej dostarczonej zmianie.

Jak widać, opcji jest dużo. Aby skutecznie zarządzać maszynerią CI przydać się więc może dedykowany zespół DevOps’ów. Doświadczeni integratorzy - współpracując z test managerem lub jego odpowiednikiem - potrafią połączyć i dostosować ogólnodostępne na rynku rozwiązania w taki sposób, by założona przez nas strategia testu łączyła się wprost z cyklem dostarczania naszych zmian. Dzięki temu możemy między innymi wymusić na użytkownikach systemu konieczność wykonywania review oraz pisania testów jednostkowych. Możemy sprawić, by każda zmiana - poza standardowym przejściem przez testy jednostkowe oraz weryfikację kodu pod względem statycznym - musiała zaliczyć też serię testów integracyjnych różnego szczebla, kończąc weryfikację na długotrwałych testach systemowych. Każdy z tych etapów może tworzyć oddzielny raport oraz decydować osobno, co zrobić z dostarczaną zmianą w przypadku wykrycia błędu.

Kluczowym jest to, by nasza maszyneria CI dawała wiarygodne i relatywnie szybkie wyniki pozwalające zaoszczędzić czas i pieniądze - które normalnie musielibyśmy poświęcić na żmudne testy regresyjne. Trudno tu jednak zdefiniować jeden standard, który będzie uniwersalny dla każdego projektu. To my musimy zdecydować, jak dokładnie chcemy testować nasz produkt i pod tym kątem postarać się stworzyć takie środowisko CI, które pozwoli nam w łatwy sposób określić jak niezawodny jest nasz system po każdej wdrożonej zmianie.

Blaski i cienie kompleksowego zarządzania kodem

Wizja stworzenia idealnego systemu, który będzie samoistnie i szczegółowo nadzorować wprowadzane przez nas funkcje wydaje się bardzo kusząca. Korzyści płynące z faktu jednoznacznego określenia, jak dobrze radzi sobie nasz produkt w dowolnym momencie jego życia są oczywiste i trudne do przecenienia.

Wybierając takie rozwiązanie musimy jednak pamiętać, że jest ono kosztowne. Decydując się na automatyzację testów warto mieć na uwadze, że ich początkowy koszt jest wysoki, a czas dostarczenia pierwszych implementacji potrafi znacząco wydłużyć trwanie projektu. Automatyzacja bywa więc nieopłacalna w rozwiązaniach, które z założenia nie mają być często zmieniane lub długo utrzymywane. Może się więc okazać, że w przypadku prototypów lub projektów tworzonych ad hoc próba wprowadzania tego typu weryfikacji właściwie nie ma sensu.

Kolejnym problemem może też być dobór odpowiednich ludzi. W dobie rywalizacji o pracownika coraz trudniej jest znaleźć na rynku pracy zarówno wykwalifikowanych testerów automatycznych, jak i doświadczonych DevOps’ów. Należy też pamiętać o konieczności wyedukowania istniejącego personelu w elementarnych zasadach prawidłowego testowania. Wszystko to niesie ze sobą znaczący wzrost nakładów, które musimy ponieść, próbując wprowadzić ideę CI w życie.

Widać więc, że nie wszędzie rozbudowany system weryfikacji automatycznej jest idealnym rozwiązaniem. Jednak w przypadku dużych projektów, gdy nad jednym produktem potrafi jednocześnie pracować kilkadziesiąt, albo czasem nawet kilkaset ludzi, bez wprowadzenia automatyzacji oraz wspierającej ją maszynerii CI ciężko jest mówić o zarządzaniu jakością. Przedsiębiorstwa mające mając w swym portfolio całe ekosystemy współpracujących ze sobą produktów dobrze rozumieją ten problem.

Za przykład może posłużyć firma, w której pracuję. Główną grupą produktów Ericsson są wyspecjalizowane rozwiązania infrastruktury i usług telekomunikacyjnych, dedykowane pod rynek operatorów sieci komórkowych na całym świecie. Zaliczyć do nich można zarówno systemy ułatwiające nadzorowanie działania poszczególnych węzłów sieciowych, jak i sam fizyczny sprzęt służący do obsługi wykonywanych połączeń. Mówimy tu o systemach o wysokiej niezawodności, muszących spełniać powszechną na rynku telekomunikacyjnym zasadę pięciu dziewiątek (system musi być aktywny przez 99.999% czasu). Starając się sprostać tak wysoko postawionej poprzeczce kładzie się u nas duży nacisk na ciągłą weryfikację dostarczanych rozwiązań. Jednym z kluczowych w tej kwestii elementów jest posiadanie sprawnego procesu CI.

Ericsson Total Quality AssuranceTak to wygląda w Ericsson: Zapewnienie jakości to nie tylko testy E2E, ale też analiza doświadczeń klientów czy jakości i przydatności samych testów.

Dzięki silnemu naciskowi na automatyzację oraz jasno określonym, wielopoziomowym procesom testowym firma potrafi nie tylko koordynować pracę tysięcy programistów, ale również umiejętnie sterować integracją poszczególnych, tworzonych przez siebie produktów. Biorąc pod uwagę specyfikę rynku, ilość oferowanych produktów, stopień ich skomplikowania oraz konieczność ich ciągłego rozwijania nietrudno wyobrazić sobie, dlaczego w Ericsson zdecydowano się znacznie zainwestować w automatyzację procesów testowych.

Nie w każdej firmie wymagany jest aż tak wysoki poziom jakości. Decydując się, jak kompleksowo chcielibyśmy testować nasze produkty musimy wziąć pod uwagę wiele czynników:

  • Jakie rozwiązania dostarczamy?
  • Ile kosztuje nas błąd w środowisku produkcyjnym?
  • Jak dojrzała testowo jest nasza organizacja?
  • Jaki mamy budżet?
  • Jakie są plany co do rozwoju dostarczanych produktów?


Po znalezieniu odpowiedzi na powyższe pytania być może będzie nam łatwiej podjąć decyzję co do tego, jak chcemy, by wyglądała kontrola jakości w naszej organizacji. To indywidualny wybór - musi być przemyślany i dostosowany zarówno do naszych potrzeb, jak i możliwości.

Na koniec słowo przestrogi.

Niezależnie od tego, jak szczegółowo zaplanujemy kwestie zapewnienia jakości, pamiętajmy, by nie przeoczyć siódmej zasady testowania: Absence of errors fallacy. Brak błędów w kodzie nie oznacza jeszcze, że stworzyliśmy dobry produkt.

<p>Loading...</p>