automatyczne testowanie oprogramowania

background image

Automatyczne testowanie oprogramowania

Łukasz Juszkiewcz

background image

Automatyczne testowanie jest jednym z głównych postulatów eXtreme

Programming (XP). Automatyzacja polega na użyciu oprogramowania do sterowania
wykonaniem testów, porównywania ich wyników z oczekiwanymi, czy raportowania.

Testowanie oprogramowania wymaga dostarczenia przypadku testowego (test

case) – częściej całego zestawu przypadków testowych dla testowanego systemu (test

case suite) – przez programistę, uruchomienia programu z użyciem przygotowanego
zestawu przypadków testowych i sprawdzeniu, czy przy danych wejściowych

pochodzących z przpadku testowego zachowanie programu jest zgodne z oczekiwanym.
Wszystkie te trzy aspekty testowania oprogramowania mogą zostać zautomatyzowane w

mniejszym lub większym stopniu.

Z powyższych aspektów, najprostszym do zautomatyzowania jest wykonanie

przypadków testowych. Dla aplikacji, które nie wchodzą w interakcję z użytkownikami
(lub ich interakcja ogranicza się do komend wpisywanych klawiaturą), łatwo jest napisać

mały program, nazywany skryptem testowym (test script), generujący odpowiednie dane
wejściowe. Nieco trudniej wykonać to dla programów z graficznym interfejsem, jednak

istnieją narzędzia pozwalające testować tego typu aplikacje. Potrafią one nagrywać
sekwencje wykonywanych operacji w testowanym programie, jednak podstawową wadą

tych narzędzi jest zależność wyniku testu od umiejscowienia poszczególnych
komponentów interfejsu na ekranie. W razie zmiany ich ułożenia lub usunięcia

istniejącego komponentu testy przestają działać poprawnie i należy je ponownie
zdefiniować.

Czasami także sprawdzenie, czy dane wyjściowe aplikacji są zgodne z

oczekiwanymi jest łatwe do zautomatyzowania – na przykład w programie wykonującym

jakieś matematyczne obliczenia na danych wejściowych i zapisującym ich rezultat w
pliku wyjściowym. Istnieje jednak wiele sytuacji, kiedy sprawdzenie zachowania

aplikacji nie jest możliwe poprzez zwykłe porównanie z oczekiwanymi danymi –
przykładowo, w programie do kompresji audio test prawidłowego działania aplikacji

polega na wysłuchaniu skompresowanej próbki i subiektywnej ocenie jej jakości.

Jednak w rozwoju sporej części oprogramowania automatyczne testowanie jest

powszechną praktyką, a narzędzia je wspomagające są szeroko dostępne, wiele z nich to
wolne czy otwarte oprogramowanie.

Automatycznie generowane przypadki testowe są jednak dużo bardziej złożonym

problemem i użycie takich automatycznie tworzonych przypadków testowych jest o

wiele rzadsze. Jednym ze sposobów generowania przypadków testowych jest
testowanie na poziomie modelu (model-based testing), gdzie model systemu jest

używany do automatycznego tworzenia przypadków testowych, jednak nie zakończono
badań nad sposobami realizacji tego zadania.

W książce Wzorce projektowe. Elementy oprogramowania obiektowego

wielokrotnego użytku John Vlissides, Ralph Johnson, Erich Gamma, Richard Helm
(“Banda Czworga” - “Gang of Four”) przedstawili wzorzec projektowy Model-Widok-

Kontroler (Model-View-Controler – MVC). Pokazuje on, jak separować warstwy aplikacji
odpowiedzialne za logikę (Model) od warstwy opisującej interfejs użytkownika (Widok,

Kontroler). Jak wiadomo, automatyczne testowanie aplikacji na poziomie Widoku
sprawia problemy, automatycznie testuje się głównie model aplikacji. Standardowa

metoda to stworzenie obiektu testowanej klasy (klas), wykonaine kilku metod i
sprawdzenie wyników ich działania. Dodatkowo powinny być spełnione następujące

warunki:

1. możliwośc uruchomienia poszczególnych testów z całego zestawu;

background image

2. niezależne wykonywanie testów: wynik (np. niepowodzenie) jednego testu nie

powinno mieć wpływu na pozostałe – umożliwi to uzyskanie jak najawiększej ilości
informacji na temat działania całego systemu;

3. tworzenie raportów z wykonanych testów ze szczegółowymi danymi i statystykami

– np. w przypadku niepowodzenia powinny zawierać wartości oczekiwane i

otrzymane.

Testowanie w eXtreme Programming.

XP testy dzieli się na trzy rodzaje:

1. jednostkowe – służą do testowania pojedynczej klasy (modułu) odizolowanej od

pozostałych komponentów; pisane przez programistów;

2. akceptacyjne – opisują oczekiwania klienta wobec systemu; tworzone przez

klienta – z reguły w prostym języku skryptowym, rzadziej w języku naturalnym,

kodowane następnie przez programistę;

3. interaktywne – nieautomatyczne testy służące do wykrywania błędów wizualnych:

nieprawidłowego rozmieszczenia elementów interfejsu użytkownika, mało
estetycznego wyglądu komponentów; jeśli testy jednostkowe zostały dobrze

zaprojektowane, w testach interaktywnych nie powinny istnieć błędy modelu, jak
nieprawidłowe wyniki obliczeń, nieprawidłowa implementacja komunikacji z

pozostałymi komponentami systemu.

Standardowo, dodawanie nowej funkcjonalności do systemu jest realizacją

poniższego schematu (cyklu):

1. Zaprojektowanie i zdefiniowanie przypadków testowych dla wymagań – nie należy

od razu opisywać wszystkich cech: należy zacząć od najprostszych i najmniejszych
kroków, stopniowo zwiększając ich zakres. Na tym etapie kompilacja utworzonego

kodu zakończy się niepowodzeniem z powodu braku testowanych obiektów.

2. Opierając się na klasach i metodach opisanych w testach należy stworzyć szkielet

klas, które będą realizowały przypadki testowe. Przed dodaniem jakiejkolwiek
funkcjonalności należy uruchomić testy, aby sprawdzić, czy testy są wykonywane

(bez zdefiniowanych treści testowanych metod, testy zakończą się
niepowodzeniem).

3. Implementacja kolejnych wymagań. Po każdej zmianie należy uruchomić testy –

liczba błędów będzie maleć z każdą poprawną implementacją. Po pozytywnym

wykonaniu wszystkich testów można zdefiniować kolejne przypadki testowe,
bardziej kompleksowe.

Często podczas tworzenia czy rozwijania jakiegoś systemu klient nalega na zmianę

istniejącej już funkcjonalności. Jeśli na tym etapie pominie się stworzenie odpowiednich
testów, podczas modyfikacji kodu mogą powstać nowe, trudne do znalezienia i

poprawienia w późniejszym etapie błędy. Ab tego uniknąć, należy:

1. Poprawić testy, aby w pełni odzwierciedlały zmianę specyfikacji testwoanego

fragmentu aplikacji. Jeśli po uruchomieniu testów, nie występują błędy,
prawdopodobnie zmienione przypadki testowe nie są wykonywane.

2. Poprawiać implementację tak, by testy nie wykazywały już żadnych błędów. W

przypadku zmiany funkcjonalności wymaganej w innej części programu, przypadki

background image

testowe uruchamiane w owym fragmencie systemu zakończą się niepowodzeniem,

co pozwoli uniknąć w dużej mierze psucia istniejącego kodu.

W razie, gdy znaleziono błąd, którego do tej pory nie uwzględniały żadne testy,

najpierw należy dodać przypadki testowe, które powinny go wykryć. Następnie trzeba

uruchomić testy, aby sprawdzić, czy nowy zestaw faktycznie wykrywa błąd. Poprawka
zostaje zakończona, gdy wszystkie testy kończą się sukcesem.

background image

Narzędzia do automatycznego testowania.

Automatyczne testowanie, jako jeden z głównych postulatów XP, doczekał się

wielu narzędzi ułatwiających jego realizację podczas pracy z każdym językiem

(obiektowym). Oto najpopularniejsze rozwiązania dla wybranych języków.

Java – JUnit

JUnit jest najpopularniejszą biblioteką służącą do konstruowania testwów. Jej

popularność i powszechne użycie (w tym wypadku idące w parze z jakością) jest przede
wszystkim wynikiem popularności jej autorów, którymi są m. in. Kent Beck (jeden z

największych autorytetów eXtreme Programming) oraz Erich Gamma (Gang of Four) –
zresztą kostrukcja JUnit jest konsekwencją jej pochodzenia; Kent Beck napisał jej

piwerszą implementację w Smalltalk'u.

Mając wydzielony model aplikacji, można przygotować przypadki testowe.

Załóżmy, że należy napisać klasę operującą na napisach.

public class NapisTest extends TestCase

{

public void runTest()

{

Napis nDu = new Napis(

“du”

);

Napis nPa = new Napis(

“pa”

);

Napis nDupa = new Napis(

“dupa”

);

nDu.zloz(nPa);
assert(nDu.equals(nDupa));

}

}

Stworzona specjalna klasa testowa, która dziedziczy z klasy TestCase nalezącej

do JUnit. Metoda assert(), pochodząca z tej klasy, sprawdza czy wyrażenie będące
jej argumentem jest prawdą.

Napisana klasa zawiera tylko jeden test. Każdy następny wygląda podobnie:

1. stworzenie testowanych obiektów;

2. wykonanie wymaganych operacji;
3. zwolnienie zasobów (jeśli zostały jakieś pobrane).

Kod z podpunktów 1. i 2. może być współdzielony między wszystkimi testami.

Umieszcza się go w metodach odpowiednio setUp() i tearDown(). Zbiór testowanych
obiektów nazywany jest Fixture.

public class

NapisTest extends TestCase

{

Napis nDu;

Napis nPa;

protected void setUp()

{

nDu = new Napis(

“du”

);

nPa = new Napis(

“pa”

);

}

background image

protected void tearDown()

{

/* brak zasobów do zwolnienia */

}

public void testZloz()

{

Napis nDupa = new Napis(

“dupa”

);

nDu.zloz(nPa);

assert(nDu.equals(nPa));

}

public void testPodnapis()

{

Napis wynik = new Napis(

“p”

);

nDu = nPa.podnapis(0,1);

assert(nDu.equals(wynik));

}

}

W celu uruchomienia kodu, należy skonstruować instancje testów z podanymi

nazwami metod z testem. Metoda run(), korzystając z refleksji w Javie, wywołuje
odpowiednią metodę testową.

TestResult result = (new NapisTest(

"testZloz"

)).run();

TestResult result = (new NapisTest(

"testPodnapis"

)).run();

Domyślna implementacja metody suite() tworzy zbiór testów na podstawie nazw
metod klasy testowej - wybierane są wszystkie zaczynające się od "test". Przed każdą

metodą testową wykonywany jest kod metody setUp(), a zaraz po zakończeniu metody
testowej wykonywana jest metoda tearDown(). Dzięki temu przypadki testowe są

odizolowane od siebie i wykonanie jednego nie ma wpływu na pozostałe.

Po przygotowaniu kilku przypadków testowych, można je pogrupować w zbiory

przypadków testowych.

public static Test suite()

{

TestSuite suite= new TestSuite();

suite.addTest(new NapisTest(

"testZloz"

));

suite.addTest(new NapisTest(

"testPodnapis"

));

return suite;

}

Python - PyUnit

PyUnit to bazujący na JUnit moduł do testwoania kodu napisanego w Pythonie,

będący integralną częścią standardowej biblioteki Pythona 2.1.

Podobnie jak JUnit, PyUnit używa klasy TestCase z modułu unittest

reprezentującej przypadki testowe i tak jak w JUnit, aby napisać własne testy, trzeba

rozszerzyć tę klasę. Przykładowa klasa testowa pochodzi z dokumentacji PyUnit:

import unittest

background image

class DefaultWidgetSizeTestCase(unittest.TestCase):

def runTest(

self

):

widget = Widget(

"The widget"

)

assert widget.size() == (50,50),

'incorrect default size'

Aby utworzyć instancję tak zdefiniowanego przypadku testowego, trzeba wywołać

konstruktor stworzonej klasy testowej bez argumentów:

testCase = DefaultWidgetSizeTestCase()

Analogicznie do JUnit tworzy się kod odpowiedzialny za tworzenie testowanych

obiektów, zwalnianie zasobów i implementuje część Fixtures:

import unittest

class WidgetTestCase(unittest.TestCase):

def setUp(

self

):

self.widget = Widget(

"The widget"

)

def tearDown(

self

):

self.widget.dispose()
self.widget =

None

def testDefaultSize(

self

):

assert self.widget.size() == (50,50),

'incorrect default size'

def testResize(

self

):

self.widget.resize(100,150)

assert self.widget.size() == (100,150), \

'wrong size after resize'

Stworzenie instancji testów w PyUnit wygląda następująco:

defaultSizeTestCase = WidgetTestCase(

"testDefaultSize"

)

resizeTestCase = WidgetTestCase(

"testResize"

)

Tak zdefiniowane testy można dodać do zbioru przypadków testowych:

widgetTestSuite = unittest.TestSuite()

widgetTestSuite.addTest(WidgetTestCase(

"testDefaultSize"

))

widgetTestSuite.addTest(WidgetTestCase(

"testResize"

))

Wygodnie jest stworzyć obiekt zwracający zestaw testów z wcześniej zdefiniowanych

przypadków testowych:

def suite():
suite = unittest.TestSuite()

suite.addTest(WidgetTestCase(

"testDefaultSize"

))

suite.addTest(WidgetTestCase(

"testResize"

))

return suite

background image

Podsumowanie

Jak widać na przykładach, automatyczne testowanie nie jest zadaniem

wymagającym ponadprzeciętnych zdolności programistycznych, zwłaszcza, gdy używa

się narzędzi testujących dla używanego języka, bazujących na JUnit – wówczas
tworzenie testów polega na stosowaniu tego samego, powszechnego schematu z

uwzględnieniem jedynie składni danego języka.

Podstawowym “mankamentem” jest zwiększona ilość kodu potrzebna na dodanie

nowej funkcjonalności (każdorazowe tworzenie przypadków testowych przed
implementacją poszczególnych założeń specyfikacji). Jednak ów “mankament” zostanie

niejednokrotnie wynagrodzony dzięki uniknięciu wielu godzin poszukiwania i naprawiania
błędów, które dzięki automatyzacji testowania są natychmiastowo wyłapywane.

Dodatkowo programowanie ukierunkowane testami (Test-Driven Development

TDD) opierające się na technice automatycznego testowania, nie jest jedynie “kolejnym

sposobem samego programowania”, ale jest także swego rodzaju sposobem
projektowania systemu i gwarantem spełniania przez niego założonej specyfikacji, która

jest zdefiniowana w przypadkach testowych.


Document Outline


Wyszukiwarka

Podobne podstrony:
TEST2, Studia - Automatyka, Inżynieria oprogramowania, zaliczenie
TEST4, Studia - Automatyka, Inżynieria oprogramowania, zaliczenie
BYT 2006 Testowanie oprogramowania
Testowanie oprogramowania
BYT 2003 Testowanie oprogramowania
SCIAGA3, Studia - Automatyka, Inżynieria oprogramowania, zaliczenie
Projekt plan testowania oprogramowania (2)
BYT 2005 Testowanie oprogramowania
Testowanie oprogramowania
Sztuka testowania oprogramowania 2
Sztuka testowania oprogramowania
Testowanie oprogramowania Podrecznik dla poczatkujacych szteop 2
Sztuka testowania oprogramowania artteo 2
Sztuka testowania oprogramowania artteo
Testowanie oprogramowania Podrecznik dla poczatkujacych
BYT 2003 Testowanie oprogramowania
Testowanie oprogramowania Podrecznik dla poczatkujacych 2
Sztuka testowania oprogramowania artteo
Leszek Kantorek Automatyzacja testów akceptacyjnych prezentacja

więcej podobnych podstron