Testowanie II

Transkrypt

Testowanie II
Testowanie II
Cel zajęć
Celem zajęć jest zapoznanie studentów z oceną jakości testów przy wykorzystaniu metryk pokrycia
kodu testami (ang. code coverage).
Pokrycie kodu testami
Jak już była mowa na poprzednich zajęciach testy nie są w stanie zagwarantować, że w
oprogramowaniu nie ma błędów. Trzeba jednak się zastanowić, czy można zrobić coś, aby nasze
testy znajdowały jak największą liczbę błędów? Czy można w jakiś sposób sprawdzić jakość
naszych testów (przez jakość rozumiemy tutaj znalezienie jak największej ilości błędów)? Jednym
ze sposobów mierzenia jakości testów jest sprawdzanie pokrycia kodu.
Możemy wyróżnić między innymi:
a) pokrycie wyrażeń/linii (ang. statement/line coverage)
Sprawdza, czy sterowanie przeszło przez każde wyrażenie dostępne w kodzie.
Dla kodu:
public int addItems(List items) {
}
if (items != null) {
list.addAll(items);
}
return list.size();
następujący test gwarantuje 100% pokrycia wyrażeń:
@Test
public void testAddItems() {
assertEquals(3, someObject.addItems(new ArrayList()));
}
b) pokrycie decyzji (ang. decision coverage)
Sprawdza, czy każde wyrażenie logiczne przyjęło wartość true i false. Całe wyrażenie
logiczne jest brane w tym przypadku pod uwagę, bez względu na podwyrażenia logiczne
połączone operatorami AND (&&) lub OR (||).
Dla kodu:
public int addItem(Object item) {
}
if (item != null) {
list.add(items);
}
return list.size();
następujący test gwarantuje 50% pokrycia decyzji, gdyż warunek w instrukcji warunkowej
przyjmuje jedynie wartość true:
@Test
public void testAddItems() {
assertEquals(1, someObject.addItem(new Object()));
}
c) pokrycie warunków (ang. condition coverage)
Podobobne do pokrycia decyzji, bierze jednak pod uwagę wartośći jakie przyjmują
podwyrażenia logiczne.
d) pokrycie ścieżek (ang. path coverage)
Weryfikuje, czy każda możliwa ścieżka wykonania została sprawdzona.
Dla kodu:
public int calculate() {
int result = 0;
if (a > 0) {
result += a;
}
if (b > 0) {
result += b;
}
return result;
}
następujący test gwarantuje 25% pokrycia ścieżek, gdyż tylko jedna z czterech możliwych
ścieżek została sprawdzona:
@Test
public void testCalculate() {
SomeObject object = new SomeObject();
object.a = 2;
object.b = 0;
assertEquals(2, object.calculate());
}
dopiero następujący test gwarantuje 100% pokrycia ścieżek:
@Test
public void testCalculate() {
SomeObject object = new SomeObject();
object.a = 2;
object.b = 0;
assertEquals(2, object.calculate());
object.a = 0;
object.b = 0;
assertEquals(0, object.calculate());
object.a = 3;
object.b = 1;
assertEquals(4, object.calculate());
object.a = 0;
object.b = 1;
assertEquals(1, object.calculate());
}
e) pokrycie metod (ang. methid coverage)
Sprawdza, czy wszystkie dostępne metody zostały wywołane podczas testowania.
f) pokrycie klas (ang. class coverage)
Sprawdza, czy wszystkie klasy zostały zainicjalizowane chociaż raz.
Wybrane problemy
Sprawdzanie pokrycia kodu testami daje nam pewną informację o jakości naszych testów, nie
możemy jednak polegać na nim całkowicie. Poniżej przedstawione jest kilka sytuacji pokazujących,
że takie podejście może być czasem mylące:
a) 100% pokrycia nie gwarantuje jakości!
Dla kodu:
public List addObject(List list, Object o) {
if (list != null) {
list.add(o);
}
return list;
}
następujący test gwarantuje 100% pokrycia wyrażeń, decyzji, warunków, metod, klass, ale
nic tak naprawdę nie testuje, bo nie ma w nim żadnej instrukcji assert*:
@Test
public void testAddObject() {
SomeObject object = new SomeObject();
object.addObject(new ArrayList(), new Object());
object.addObject(null, null);
}
Dla kodu:
public int multiply(int x, int y) {
return x + y;
}
następujący test gwarantuje 100% pokrycia wyrażeń, decyzji, warunków, metod, klass, ale
źle dobrane dane wejściowe powodują, że test nie wykrywa błędu:
@Test
public void testAdd() {
SomeObject object = new SomeObject();
assertEquals(4, object.multiply(2, 2));
}
b) Czasami 100% pokrycie nie może być osiągnięte!
Dla kodu:
public List<String> addObject(List<String> list, String newName) {
if (list != null) {
for (String name : list) {
System.out.println(name);
}
}
}
System.out.println("-----------");
if (list != null) {
list.add(newName);
}
return list;
nie możliwe jest stworzenie testu ze 100% pokryciem ścieżek.
c) Co z testowaniem pętli? Czy wystarczy jeśli kod pętli został wykonany tylko raz?
Badanie pokrycia kodu przy pomocy narzędzia EclEmma
EclEmma to wtyczka do Eclipse'a pozwalająca nam sprawdzić pokrycie kodu testami.
1. Zaimportuj projekt z pliku CodeCoverage4Students.zip (File->Import->Existing Project
Into Workspace).
2. W widoku Package Explorer znajdź plik BootsTest.java, kliknij na nim PPM i wybierz opcję
Coverage As -> JUnit Test:
3. Zostanie uruchomiony test. W widoku JUnit widzimy, że test przeszedł, co jednak z
pokryciek kodu testami? W widoku Coverage (jeśli widok nie pojawił się automatycznie
możemy go otworzyć przez Window->Show View->Other...) widzimy statystyki.
Dodatkowo gdy otworzymy nasze klasy zauważym, że nasz kod został oznaczony kolorami.
I tak:

kolor zielony onacza, że linia została całkowicie pokryta

kolor żółty oznacza, że linia została częściowo pokryta

kolor czerwony oznacza, że linia w ogóle nie została wykonana
W naszym przykładzie testujemy klasę Boots, a dokładniej jej metodę printSummary,
która jest zdefiniowana w klasie Item. Otwórzmy zatem klasę Item i spójrzy na metodę
printSummary:
public String printSummary() {
String summary = "";
if (type == THIS_YEAR) {
summary += "NEW!\n";
}
if (producerName != null && producerName.length() > 0) {
summary += "Producer: " + producerName + "\n";
}
int totalDiscountAmount = 0;
for (Discount discount : currentDiscountsList) {
totalDiscountAmount += discount.getPercentage();
}
if (totalDiscountAmount > 0) {
if (totalDiscountAmount > 30) {
totalDiscountAmount = 30;
}
summary += "Discount: " + totalDiscountAmount + "%\n";
}
summary += "Final Price: "
+ (price * (1 - (totalDiscountAmount / 100))) + "\n";
summary += printDetails();
return summary;
}
Możemy zauważyć, że nasz test nie przechodzi przez wszystkie linie kodu metody oraz nie
sprawdza wszystkich wartości wyrażeń logicznych. Należałoby dopisać dodatkowe testy,
tak aby pokrycie było jak największe.
Ćwiczenie
Wyobraźmy sobie sytuację, że jesteśmy w trakcie pisania systemu dla sklepu interentowego
zajmującego się sprzedażą sprzętu narciarskiego. Częśc systemu mamy już napisaną, brakuje nam
jednak kilku testów. Naszym zadaniem będzie dopisanie testów dla istniejących klas, tak aby
znaleźć istniejące błędy oraz aby pokrycie kody testami było jak największe .
1. Jeśli jeszcze twego wcześniej nie zrobiłeś to zaimportuj projekt z pliku
CodeCoverage4Students.zip (File->Import->Existing Project Into Workspace).
2. Przejrzyj kod znajdujący się w katalogu src, a następnie napisz dla niego testy i sprawdź dla
nich pokrycie kodu. Opis metod znajduje się w komentarzach JavaDoc.
3. Po napisaniu testów popraw znalezione błędy.