środa, 7 listopada 2007

Oczywiste oczywistości czyli o podstawach kompletnych testów

Tym razem chciałbym napisać o rzeczach, które uważam za kompletne podstawy... . Chciałem zdanie dokończyć 'testowania oprogramowania' ale uzmysłowiłem sobie, że to nie dotyczy wyłącznie testowania. A więc... chcę napisać o kompletnych i podstawowych zabiegach, stosowanych w szeroko pojętej inżynierii w mocnym kontekście testów oprogramowania, odchylonych - z kolei - mocno w stronę automatyzacji i/lub powtarzalności.
  • Parametryzacja - separacja danych od logiki ich przetwarzania. Duża część testów polega na mnipulacji samymi danych (klasy równoważności, wartość graniczne, dziedziny itd). W związku z tym o wiele łatwiej jest zarządzać tymi danymi mając je "na zewnątrz" skryptu.
  • Atomizacja - rozbijanie złożonych problemów na proste. Informatyka jest pod tym kątem szczególnie łaskawą dziedziną. Praktycznie każdy złożony problem składa się z mniejszych i prostszych. Trzeba tylko umieć wyłowić powiązania prostych problemów w kontekście złożonego. Dodatkowo, "testowo" obsłużone proste problemy mogą być re-używalne.
  • Logika - nie testować w oparciu o nazwy kontrolek ale w oparciu o logiczne przepływy danych. Każde oprogramowanie składa się z warstwy prezentacyjnej (interfejs użytkownika), warstwy przetwarzania (middleware) i warstwy sterowania (sterowniki). Dobrze napisane oprogramowania ma te warstwy mocno odseparowane co pozwala na wydajne testowanie logiki w oderwaniu od kontrolek i zasilaniu logiki bezpośrednio z zestawów danych testowych np. poprzez API.
  • Narzędzia - co może za nas zrobić narzędzie to niech to zrobi. Pytanie tylko czy potrafimy tym narzędziem się posłużyć. Narzędzi jest całe mnóstwo i nie problem znaleźć takie, które wydaje się przydatne ale wcześniej trzeba wiedzieć do czego chce się wykorzystać takie narzędzie.
  • Abstrakcja - nie związywanie procedur testowych z konkretną wersją (wyglądem) testowanego oprogramowania ale raczej z jego przeznaczeniem. Np. przypadki testowe odwołujące się do konkretnych nazw kontrolek muszą być aktualizowane gdy zmieni się wersja GUI. A z drugiej strony, skoro przypadki już są gotowe to po co czekać i nie uruchomić procedur testowych od razu. Przecież kontrolki na interfejsie użytkownika można potraktować jako swojego rodzaju dane, które się weryfikuje (czy kontrolka jest, wartości kontrolki, funkcjonalności kontrolki).
  • Elastyczność - łatwość rozbudowywania i dostosowywania do konkretnych potrzeb. W przypadku automatyzacji chodzi np. o możliwość zestawiania poszczególnych skryptów czy przypadków testowych w różnych sekwencjach.
  • Generowalność - generowanie scenariuszy i generowanie rezultatów testów. Wybudowanie środowiska testowego pozwalającego na swobodne generowanie scenariuszy testowych o jasno określonym celu jest bardzo trudne jeśli wręcz nie możliwe. Jakkolwiek, należy budować przypadki testowe w taki sposób aby na ich podstawie móc generować scenariusze. Drugą stroną jest możliwość generowania wyników z testów. Każdy przeprowadzony test kończy się określonym wynikiem. Dobrze jest jeśli takie wyniki odnotowywane są automatycznie w sposób umożliwiający ich dalszą weryfikację.

2 komentarze:

Anonimowy pisze...

Moje uwagi:
Paragraf 1. Rozumiem, że chodzi Ci o przypadek, że piszemy Test Case, który można uruchomić na dla wielu różnych danych testowych. Moje wątpliwości budzi jednak to, że dla dwóch różnych danych testowych możemy mieć do czynienia z dwoma różnymi rezultatami (np. możemy mieć zarówno dane pozytywna i negatywne, czy też: dwie dane pozytywne, ale należące do dwóch różnych klas równoważności.Z tego powodu wyobrażam sobie, że oprócz wektora danych testowych, musimy mieć również odpowiadający mu wektor danych wyjściowych (a co jeszcze z ustawieniami wejściowymi). Dalej, jeśli nawet będziemy dysponować takim zestawem danych testowych i oczekiwanych rezultatów, bo i tak nie będziemy mogli zagwarantować, że przy każdej iteracji testy zostały uruchomione z tym samym podzbiorem danych testowych (chyba, że za każdym razem osoba testująca wykona dany przypadek testowy tylekroć, ile ma danych testowych).
Paragraf 2. Zgadzam się. Niemniej problem starszy od mamuta. Jak mając ograniczoną wiedzę o testowany przedmiocie rozłożyć go na najdrobniejsze cegiełki. Niemniej do ideału należy się przybliżać.
Paragraf 3. Nie spotkałem oprogramowania, które miało by dobrze rozdzielone warstwy architektoniczne (np. tu przytoczone: aplikacja, middleware, sterowniki). Jest to jak dążenie do ideału, na drodze którego zawsze pojawia się "coś". Niemniej to o czym piszesz ma duża rację bytu w systemach, w których aplikacja jest tylko nakładką, która komunikuje się poprzez z góry zdefiniowane interface'y oraz poprzez użycie dobrze zestandaryzowanych protokołów. W świecie gdzie testy wykonuje się na zamkniętym produkcie, o którym wiedzę zdobywa się w większości z własnych obserwacji i doświadczenia, logikę zachowań definiuje się poprzez język aplikacji/inteface'u.
Paragraf 4. Z tymże: narzędzia są drogie, trudne, nie do końca spełniają nasze oczekiwania i potrzeby (zazwyczaj specyficzne).
Paragraf 5. Podobnie jak w paragrafie 3. Należy pamiętać jeszcze, by przypadek testowy nie był na tyle abstrakcyjny, by każdy kto go będzie wykonywał, mógł go wykonać w identyczny sposób (jednoznaczność przypadku testowego)
Paragraf 6. W ideale to zgadam się, tak ma być. Niemniej zdarzają się takie przypadki, kiedy żeby system doprowadzić do stanu pozwalającego przeprowadzić dany test, trzeba poświęcić dużo czasu lub pieniędzy. Wówczas by ograniczyć koszty trzeba tak zorganizować pracę, by okres przygotowywania testowanego systemu do wykonania interesującego nas testu, poświęcić na wykonanie możliwie dużej liczby innych testów.
Paragraf 7. Zgadzam się (z zastrzeżeniem uwag do paragrafu 6.). Automatyczne generowanie oczekiwanych wyników, to zdarza się chyba tylko podczas testowania systemów bazodanowych.

Tomasz Zdanowicz pisze...

Witam,

Serdeczne dzięki za interesujące komentarze i już spieszę w wyjaśnieniami...

Ad uwagi do Paragraf 1
Ja lubię myśleć o przypadkach testowych jak o swojego rodzaju procedurach przetwarzania danych. Uważam, aby po prostu w przypadkach testowych nie "zaszywać" danych (rozumianych bardzo szeroko jako zmienne). Oczywiście, ze nie da się zastosować tego w 100% ale jeśli potraktuje się to jako "sposób pisania" to w pewnym momencie wchodzi w to w nawyk i nie jest takie trudne jak na początku.
Co do ustawień wejściowych to też da się je potraktować jako dane. W moim przypadku lubię to umieszczać w procedurze sprawdzania warunków wstępnych. Wtedy uzyskuję oddzielenie stanu wejściowego od samego przypadku testowego.
Odnosząc się do "gwarancji wykonania każdej iteracji testów, z tym samym podzbiorem danych" to w praktyce, też nie ma takiej gwarancji. Nie za bardzo rozumiem o jaką gwarancję chodzi... osobiście uważam, że każda iteracja testów powinna korzystać z innego podzbioru danych z tej samej dziedziny. W takim przypadku szybko wykrywa się "naprawy dedykowane" gdzie po prostu programista "hardkoduje" konkretne wartości tylko po to aby przejść testy.

Ad uwagi do Paragraf 2
Bynajmniej nie chodzi mi o rozkładanie systemu na drobniejsze elementy ale rozkładnie złożonego problemu testowego na kilka mniejszych.

Ad uwagi do Paragraf 3
Moje doświadczenie jest podobne. A nawet te produkty, które miały dobrze zaprojektowane warstwy, pozostawiały wiele do życzenia na poziomie implementacji. Ale często - tak jak wspomniałeś - badając interfejsy można szacować która końcówka jest za co odpowiedzialna. Ale fakt pozostaje faktem jest to trudne.

Ad uwagi do Paragraf 4
Mówiąc narzędzia mam na myśli nie tylko produkty ale także wiedzę. Np. jeśli testujemy systemy oparte o daną technologię, to warto jest ją poznać. Nawet jeśli nie wykorzysta się tej wiedzy w bieżącym projekcie.
Co więcej - mówiąc o narzędziach do testowania - ja jeszcze nie spotkałem narzędzia, które spełniałoby w pełni oczekiwania i potrzeby organizacji. I takiego nie spotkamy. Chodzi mi - a tego nie powiedziałem - o to aby dobierać narzędzie do potrzeb ale ściśle zdefiniowanych potrzeb. A więc głównym problemem jest definicja potrzeb a nie funkcjonalność narzędzia.

Ad uwagi do Paragraf 5
:-) Zakładam świadomość prac, które się wykonuje. Aczkolwiek, problemem tutaj pozostają testy akceptacyjne. Często klient wraz z produktem, życzy sobie dostarczenia scenariuszy testowych do wykonania przez niego. I - o zgrozo - bardzo często wcale nie oczekuje scenariuszy testowych ale instrukcji w jaki sposób wykonać te scenariusze.

Ad uwagi do Paragraf 6
Tak. Sytuacja którą opisałem to sytuacja idealna! Powiem nawet więcej... dużym sukcesem jest kiedy mamy takich przypadków 10% -15%. Ale muszę tutaj sprostować, że chodziło mi o to aby przy tworzeniu środowiska testowego unikać sytuacji w których, szybciej będzie napisać nowy scenariusz od zera zamiast go skleić z istniejących przypadków testowych.

Ad uwagi do Paragraf 7
Nie koniecznie tylko systemów bazodanowych. Generalnie tam gdzie da się bardzo mocno kontrolować dane wejściowe (rozumiane także jako warunki wstępne).

Pozdrawiam
Tomasz