poniedziałek, 17 listopada 2008

User Acceptance Tests

Ostatnio prowadziłem szkolenie z agile, podczas którego padło bardzo logiczne pytanie: „a co z User Acceptance Tests (UAT – testy akceptacyjne)? kiedy je się wykonuje i jak zapewnia się, że podczas ich trwania ma się dostęp do grupy wykonawczej, aby ewentualnie naprawić zgłoszone przez użytkowników błędy?”. Pytanie jak najbardziej logiczne, dobre i odpowiedź powinna gdzieś być do znalezienia. Niestety po przeszukaniu dostępnej w mojej biblioteczce literatury okazało się, że odpowiedzi na to pytanie nie ma w posiadanej przeze mnie literaturze agile. Dlatego napiszę co ja na ten temat sądzę i jak ta sprawa była rozwiązywana w firmach, w których widziałem duże wdrożenia metod zwinnych. I przy okazji: jak ktoś gdzieś ma opis w literaturze takiego lub podobnego rozwiązania to bardzo proszę o podanie mi źródła.

„Problem” z User Acceptance Tests wynika z faktu, że według procesów agile grupa wykonawcza, pracująca nad projektem powinna cały czas w ramach iteracji dostarczać kolejne działające inkarnacje produktu. Tymczasem w tzw. świecie realnym, w którymś momencie przychodzi moment, kiedy taki działający produkt należy pokazać klientowi, który to klient powinien się zdecydować czy produkt spełnia jego wymagania czy nie. Ta właśnie procedura nazywa się testami akceptacyjnymi (UAT) i zwykle trwa jakiś czas- na pewno nie jeden czy nawet dwa dni. Co więcej z punktu widzenia firmy produkującej oprogramowanie jest to czas, który jest krytyczny. W trakcie testów akceptacyjnych musi być stały dostęp do zespołu, który wytworzył dany produkt aby bardzo szybko reagować na ewentualne błędy (pamiętajmy, że testuje to już klient – użytkownik ostateczny). Jak to więc zrobić, aby mieć dostęp do grupy wykonawczej w środowisku agile?

Ano bardzo prosto. Tylko trzeba się wznieść na „poziom wyżej”. Tak jak już kiedyś pisałem poziom iteracji i poziom wersji rządzi się trochę innymi prawami jeżeli chodzi o planowanie. Przypomnę tylko, że w ramach jednej wersji może być kilka iteracji. Wersja natomiast oznacza wersję produkcyjną, która zostaje zainstalowana u klienta ostatecznego w jego środowisku produkcyjnym. W ramach danej wersji następujące po sobie iteracje stanowią pewną całość i odbywają się jedna po drugiej, bez przerw. Nikt jednak nie powiedział, że tak samo musi być z wersjami. Często jest to w ten sposób rysowane w różnej literaturze (prace nad kolejną wersją rozpoczynają sie natychmiast po zakończeniu prac nad poprzednią), ale praktyka często weryfikuje takie podejście. W praktyce jest tak, że po zakończeniu pracy nad jedną wersja produkcyjną danego produktu następuje jej wypuszczenie i wdrożenie (deployment) w środowisku klienta. Natomiast prace nad kolejną wersją tego samego produktu zaczynają się zwykle z pewnym opóźnieniem (na przykład 2-4 tygodnie). W trakcie tego „opóźnienia” odbywają się dwie rzeczy. Po pierwsze klient przeprowadza UAT wersji, którą dostał. Jako, że w tym czasie nie ma prac rozwojowych nad wersją kolejną to w przypadku jakiejkolwiek awarii zespół wykonawczy jest w stanie bardzo szybko zareagować na ewentualne problemy. Jednak zespół wykonawczy nie przeznacza tego czasu tylko na bycie w gotowości. Oprócz tego, ten czas przeznaczany jest na przygotowanie do rozpoczęcia prac nad kolejną wersją. Zwykle oznacza to w praktyce przejrzenie z kierownikami produktu wszystkich funkcjonalności mających wejść w skład następnej wersji, nadanie im priorytetów oraz przeprowadzenie wstępnej estymacji. I nie jest to wbrew zasadom agile – wręcz przeciwnie, powtórzę po raz kolejny, że agile to nie chaos i kierownicy produktu powinni na początku prac nad wersją wiedzieć co chcą w jej ramach dostać. Zwłaszcza jeżeli chodzi o funkcjonalności o wysokich priorytetach, które wejdą do produkcji w pierwszych iteracjach pracy nad nową wersją.

Na koniec podsumowując w jednym zdaniu: praktyka wskazuje, że User Acceptance Tests w projektach agile przeprowadza się w „przerwie” pomiędzy pracą nad kolejnymi wersjami produktu.

Etykiety: , , , ,

czwartek, 22 listopada 2007

Jak definiować testy?

Problem z praktyki: w jaki sposób definiować testy funkcjonalne w oprogramowaniu rozwijanym metodami zwinnymi? Żeby odpowiedzieć poprawnie najpierw przyjrzyjmy się jak to jest robione w standardowych metodach zarządzania projektami informatycznymi. Według teorii dokumentem na którym się bazuje jest definicja wymagań. Po zatwierdzeniu dokumentu definiującego wymagania przez klienta na podstawie tegoż dokumentu jednocześnie projektanci przystępują do projektowania systemu a osoby odpowiedzialne za testy piszą tzw. specyfikacje testów. Czyli ustalają co ma być testowane, w jaki sposób i w jakich ramach czasowych. Tak jest w teorii. W praktyce bywa nieco inaczej oczywiście. Głównie dlatego, że w znakomitej większości projektów dokument specyfikacji wymagań tak naprawdę zamknięty i zakończony jest dopiero w momencie zakończenia prac nad projektem. Klient przez cały czas wprowadza jakieś poprawki, które potem muszą być odzwierciedlane w testach. Dlatego po każdej zmianie specyfikacji wymagań trzeba zmienić specyfikację testów. Gratulacje dla tych, którym udaje się to utrzymać w porządku przy większych projektach.

W przypadku projektów realizowanych metodami zwinnymi jest trochę inaczej. Jak wiadomo wymagania definiowane są poprzez funkcjonalności, które muszą przynosić wartość dodaną oraz być nie większe niż jedna iteracja. Funkcjonalności mają też taką cechę, że nie mogą się zmieniać tylko i wyłącznie w ramach iteracji, w której są wykonywane. Wcześniej lub później można je dowolnie przedefiniować. W przypadku gdyby chcieć w takich warunkach pisać zwykłą specyfikację testów byłoby to niemożliwe ze względu na narzut na utrzymanie aktualnej dokumentacji. Jak sobie z tym poradzić? W najprostszy możliwy sposób. Otóż testy funkcjonalne zapisywane są i definiowane w obrębie danej funkcjonalności i do nie niezmiennie przypisane. W praktyce wygląda to tak, że funkcjonalność składa się z opisu tego co ma być zrobione oraz sposobu w jaki należy sprawdzić, czy zostało to zrobione poprawnie. Mówiąc językiem bardziej biznesowym funkcjonalność zawiera metodę upewnienia się, że faktycznie wprowadza ona wartość dodaną dla klienta.

Proste? Przede wszystkim zgodne ze zdrowym rozsądkiem. I bardzo dobre dla wszystkich stron zaangażowanych w tworzenie oprogramowania. Klient od razu wie w jaki sposób będzie sprawdzane czy funkcjonalność jest dobra, więc bardzo wcześnie można wykryć wszelkie niedomówienia w definiowaniu wymagań. Unika się w ten sposób sytuacji, kiedy wymaganie zostało inaczej zrozumiane przez zespół - "myśleliśmy, że o to Wam chodziło". Tutaj w sposób jawny definiujemy jak będziemy sprawdzać poprawność wykonania wymagania klienta. Testerzy są bardzo zadowoleni, bo zwykle mają w takim przypadku gotowe specyfikacje testów, które trzeba tylko oprogramować i wykonać. Programiści natomiast doskonale wiedzą czego się od nich oczekuje. Mogą więc na przykład postawić na TDD (Test Driven Development).

Metoda jest bardzo prosta, ale istnieją dla niej pewne zagrożenia. Jednym z nich jest to, że nagle zaczną sie pojawiać "testy luzem". To znaczy propozycje wykonania testów, które "muszą być", ale nie będą przypisane do żadnej funkcjonalności. Uwaga na takie sytuacje!!! W technikach zwinnych "testy luzem" akceptuje się tylko i wyłącznie dla wymagań niefunkcjonalnych. Zgodnie ze swoją definicją dotyczą one ogółu systemu (np. wydajność) a nie poszczególnego funkcjonalności. Jeżeli natomiast trafi się propozycja uwzględnienia funkcjonalnego "testu luzem" to najprawdopodobniej jest to błąd wynikający z jednej z dwóch przyczyn. Po pierwsze brak wymagania - sytuacja kiedy ewidentnie brakuje jakiegoś wymagania funkcjonalnego. "Funkcjonalny test luzem" nie istnieje, bo z definicji testuje jakąś funkcjonalność. Jeżeli nam się taki osierocony test przytrafi to przyczyną może być brak definicji któregoś z wymagań - trzeba je dodefiniować. Drugą przyczyną może to być "pozłacanie" (ang. gold plating) produktu - klient nie chciał, ale my mu zrobimy bo jesteśmy fajni. Nie wolno. Jeżeli klient nie chciał to za to nie płaci, dlatego nie robimy. Możemy ewentualnie zasugerować klientowi, że znaleźliśmy możliwość wprowadzenia takiej dodatkowej funkcjonalności i poczekać na jego decyzję.

Etykiety: , ,

niedziela, 9 września 2007

Testowanie w projektach zwinnych

W ciągu ostatnich trzech tygodni byłem trzy razy pytany o to jak testować oprogramowanie powstające z wykorzystaniem procesów zwinnych. Pytały trzy różne, niezależne osoby. Oznacza to, że temat jest ciekawy. To co poniżej napiszę to tylko wstęp, ale na początek powinien wystarczyć.

Opiszę jak w podejściu zwinnym odbywa się (powinno się odbywać) testowanie na poziomach testowania jak zdefiniowanych tutaj.

Testowanie komponentów
To jest najprostsze. Testowanie komponentów musi odbywać się obowiązkowo w każdym projekcie realizowanym z użyciem metod zwinnych. Testowanie komponentów zawsze powinno być automatyczne. W zależności od tego w jaki sposób zespół jest przyzwyczajony pracować absolutnym minimum jest napisanie testów automatycznych pokrywających 100% napisanego kodu. Sytuacją dającą najlepsze rezultaty jest programowanie z nastawieniem "najpierw testy" oraz używanie środowiska ciągłej integracji. Chodzi mniej więcej o to, aby w sposób automatyczny wykonywały się testy całości do tej pory zaimplementowanej funkcjonalności. Po każdej zmianie kodu całość powinna być rekompilowana i testy powinny wykonywać się automatycznie. Najlepiej aby raporty z wykonania tych testów były w jakiś sposób publikowane, tak aby każdy mógł je zobaczyć. Na przykład na jakiejś stronie intranetowej.

Takie podejście jest absolutnie konieczne ze względu na dużą zmienność występującą w systemach wytwarzanych metodami zwinnymi. Jeżeli zaniedba się testy automatyczne to po kilku iteracjach w kodzie zapanuje chaos i nie będzie wiadomo co działa a co nie. Gwarantowane.

Testowanie integracji
Uwaga! Poniższe dotyczy testów integracji komponentów. Nie odnosi sie to testowania integracji systemów.

Testy integracyjne należy również zautomatyzować na ile tylko się da. Podstawowe testy integracyjne powinny być wykonywane w środowisku ciągłej integracji, czyli tak często jak się da. Najrzadziej co noc. Problem pojawia się wtedy kiedy mamy się integrować z niestabilnymi komponentami, na przykład tworzonymi równolegle przez zespoły pracujące metodami zwinnymi. Będzie to problematyczne, ale też należałoby tutaj zautomatyzować takie testy do granic, gdzie się da. Zwykle będzie to wymagało dokładnego dogadania się z innymi zespołami na temat wykorzystywanych interfejsów. Brzmi trudno, ale w praktyce powinno być tak, że mniej więcej w połowie iteracji zespoły powinny znać szczegóły interfejsów zewnętrznych swojego komponentu. To powinno zaś umożliwić zautomatyzowanie testów. Trzeba tylko uważać na ewentualne zależności w funkcjonalności poszczególnych komponentów. Takie zależności powinny być rozwiązywane na poziomie zarządzania zwinnego projektem a dokładnie na poziomie zarządzania wieloma zespołami. Przykładem techniki, która to umożliwia jest na przykład "scrum of scrums" - temat na osobny wpis/kilka wpisów.

Szczególną uwagę należy zwrócić na testy wydajnościowe. Ja zaliczam je do testów integracyjnych, gdyż poszczególny test wydajnościowy zwykle dotyczy tak naprawdę testowania współdziałania jednego bądź dwóch modułów systemu, rzadko kiedy całego systemu. Jeżeli komuś to nie pasuje to może uznać je za testy systemowe. W obu wypadkach podtrzymuję to co zaraz napiszę. Testy wydajnościowe powinny być wykonywane co najmniej raz na iterację. Powinny być wykonywane względem celu wydajnościowego ustalonego dla końcowego systemu w karcie ograniczeń produktu. Powinny obejmować wszystkie te funkcjonalności które są dostępne w danej iteracji. Powód takiego działania jest dość prosty. Otóż ze względu na iteracyjność tworzenia oprogramowania w sposób naturalny architektura rozwiązania może się zmieniać. W związku z tym powinno nam zależeć na jak najszybszym dowiedzeniu się czy aktualna architektura aby na pewno odpowiada wymaganiom stawianym przed systemem. Stąd testy wydajnościowe należy przeprowadzać z celami dla całego systemu przynajmniej raz na iterację, żeby było wiadome czy aktualna architektura i wykonanie systemu realizują założone cele.

Testowanie systemu
To najciekawszy poziom testowania z punktu widzenia metod zwinnych zarządzania projektami. Szczególnie ciekawy jest w przypadkach kiedy kilka zespołów pracuje nad kilkoma modułami stanowiącymi jeden system. Pytanie brzmi kiedy i jak testować całość systemu. Oczywiście odpowiedź brzmi, że w świecie idealnym powinno się to robić co iterację. Świat rzeczywisty wygląda jednak często tak, że nie ma na to czasu. Testy systemowe zwłaszcza dużych systemów potrafią trwać zbyt długo, aby można było sobie pozwolić na robienie ich w ramach jednej iteracji. Często głównym powodem jest tutaj fakt, że testy te wymagają dużej aktywności użytkownika końcowego i po prostu nie starczyłoby czasu. W związku z tym dla dużych systemów dopuszcza się dokonywanie testów systemu raz na każdą wersję (release) oprogramowania.

Wersja oprogramowania pojawia się w procesach zwinnych co kilka iteracji. Każda z iteracji ma na celu dostarczenie działającej wersji, potencjalnie mogącej być wysłaną do użytkownika ("potentially shipable product increment" - tak to jest w języku Shakespeare'a). Co kilka iteracji powstaje zaś "wersja" oprogramowania, która jest po prostu komercyjnym produktem mogącym iść na sprzedaż bądź być wdrożonym u klienta. Oczywiście zdarzają się przypadki, kiedy wersja powstaje w każdej iteracji, lecz doświadczenie wskazuje, że są to wtedy raczej mniejsze systemy. Systemy większe, takie które będą miały konieczność dokonania testowania systemu zwykle będą planowane na obu poziomach: wersji i iteracji. Dlaczego o tym piszę? Otóż w takich przypadkach zwykle ostatnia iteracja w wersji jest przeznaczana na testy systemowe. Zakłada się, że wartością dodaną w takiej iteracji jest dostarczenie w pełni zintegrowanego systemu. Oczywiście testy systemowe niekoniecznie muszą zajmować całą iterację. Ale na pewno przyczynią się do zmniejszenia ilości zaimplementowanej nowej funkcjonalności.

Testy akceptacyjne
Tutaj sprawa jest prosta: testy akceptacyjne wykonuje klient po przekazaniu mu każdej jednej wersji do testów. Ze względu na bliskie zaangażowanie klienta możliwe jest przekazywanie produktu nie mającego zaimplementowanych wszystkich funkcjonalności i otrzymywanie natychmiastowej informacji zwrotnej o postępach prac. W przypadku bardzo dobrej współpracy między klientem a dostawcą możliwe jest nawet przekazywanie klientowi zawierającego błędy oprogramowania w środku iteracji - po to, aby sprawdził czy koncepcja zaimplementowania którejś z funkcjonalności odpowiada jego potrzebom.

Etykiety: ,