Pokrycie przepływu danych

advertisement
Testowanie oprogramowania
metodą badania pokrycia kodu
Artur Rembiszewski
Opiekun: dr inż. Ilona Bluemke
Plan prezentacji

Przypomnienie podstawowych pojęć
–
–

Dostosowanie kryteriów przepływu danych
do programów obiektowych
–
–


Testowanie metodą badanie pokrycia kodu
Pokrycie przepływu danych
Zasięg widoczności zmiennych
Klasyfikacja instrukcji
Prezentacja mojego programu
Pytania
Podział metod testowania
Testowanie
Strukturalne
(white-box)
Statyczne
Dynamiczne
Funkcjonalne
(black-box)
Przypadek testowy (test)
- Zbiór danych wejściowych
- Scenariusz wykonania
- Oczekiwany wynik
Ocena jakości zestawu testów
Po co?
- Czy aplikacja jest przetestowana w wystarczającym
stopniu?
- Znalezienie minimalnego zbioru testów
Jak? - kryteria
- Pokrycie danych
- Pokrycie kodu
- Pokrycie przepływu sterowania
- Pokrycie przepływu danych
Schemat testowania
Pokrycie instrukcji
Wymagane wykonanie co najmniej raz każdej
instrukcji kodu programu
EclEmma
Pokrycie instrukcji - wady
int abs (int x)
{
if (x>=0)
x=0-x;
return x;
}
T= {(x=0)}
Inne kryteria pokrycia przepływu
sterowania







Pokrycie warunków
MCC (ang. Multiple Condition Coverage)
MCDC (ang. Modyfied condition / decision coverage)
JJ-paths (ang. jump-to-jump-paths)
Pełne pokrycie ścieżek
Kryteria związane z pokryciem pętli
...
Pokrycie przepływu danych
S. Rapps, E.J. Weyuker – 1985
Stwierdzenie czy pewna operacja przebiegła
prawidłowo możliwe jest tylko w przypadku, gdy jej
wynik zostanie użyty.
Pokrycie przepływu danych pojęcia



definicja (ang. definition) zmiennej x –
instrukcja w wyniku której do zmiennej
zostaje przypisana wartość.
użycie (ang. use) zmiennej x – instrukcja
której wykonanie wymaga podstawienia
wartości zmiennej x.
Graf DUG (ang. def-use graph)
Pokrycie przepływu danych pojęcia



p-use (ang. predicative) – użycie zmiennej w
predykacie
c-use (ang. computational) – użycie zmiennej
w instrukcji obliczeniowej.
Ścieżka wolna od definicji zmiennej x (ang.
def-clear path)
Pokrycie przepływu danych
Pokrycie przepływu danych
Rodzaje






All defs
All uses
All p-uses
All c-uses
All p-uses/some c-uses
All c-uses/some p-uses
Dostosowanie kryteriów do
programów obiektowych

Zasięg widoczności zmiennych
Które instrukcje traktować jako definicje, a
które jako użycia zmiennych reprezentujących
obiekty ?

Zasięg widoczności zmiennych
Poziomy testowania:
 Intra-method
 Inter-method
 Intra-class
Zasięg widoczności zmiennych
Poziomy testowania:
 Intra-method
 Inter-method
 Intra-class
Zasięg widoczności zmiennych
public class KeyTable {
private static final int NOT_FOUND = -1;
private TableElem[] table;
private int length, maxTabSize;
private int getIndex(String key) {
for (int i=0; i<length; i++)
if (table[i].key.equals(key))
return i;
return NOT_FOUND;
}
private void removeIndex(int index) {
for (int i=index; i<length-1; i++)
table[i] = table[i+1];
table[length-1] = null;
length --;
}
public void add(String key, int value) {
int index = getIndex(key);
if (index == NOT_FOUND) { //add new
table[length] = new TableElem();
table[length].key = key;
table[length].value = value;
length ++;
if (length == maxTabSize)
reallocate();
} else //replace existing
table[index].value = value;
}
public int get(String key)
throws ElementNotFoundException {
int index = getIndex(key);
if (index == NOT_FOUND)
throw new ElementNotFoundException();
return table[index].value;
private void reallocate() {
TableElem[] newT = new TableElem[2*length]; }
for (int i=0; i<length; i++)
public void remove(String key)
newT[i] = table[i];
throws ElementNotFoundException {
table = newT;
int index = getIndex(key);
maxTabSize = 2*length;
if (index == NOT_FOUND)
}
throw new ElementNotFoundException();
public KeyTable(int startTabSize) {
removeIndex(index);
this.maxTabSize = startTabSize;
}
table = new TableElem[maxTabSize];
length = 0;
}
}
Zasięg widoczności zmiennych
Poziomy testowania:
 Intra-method
 Inter-method
 Intra-class
Zasięg widoczności zmiennych
public class KeyTable {
private static final int NOT_FOUND = -1;
private TableElem[] table;
private int length, maxTabSize;
private int getIndex(String key) {
for (int i=0; i<length; i++)
if (table[i].key.equals(key))
return i;
return NOT_FOUND;
}
private void removeIndex(int index) {
for (int i=index; i<length-1; i++)
table[i] = table[i+1];
table[length-1] = null;
length --;
}
public void add(String key, int value) {
int index = getIndex(key);
if (index == NOT_FOUND) { //add new
table[length] = new TableElem();
table[length].key = key;
table[length].value = value;
length ++;
if (length == maxTabSize)
reallocate();
} else //replace existing
table[index].value = value;
}
public int get(String key)
throws ElementNotFoundException {
int index = getIndex(key);
if (index == NOT_FOUND)
throw new ElementNotFoundException();
return table[index].value;
private void reallocate() {
TableElem[] newT = new TableElem[2*length]; }
for (int i=0; i<length; i++)
public void remove(String key)
newT[i] = table[i];
throws ElementNotFoundException {
table = newT;
int index = getIndex(key);
maxTabSize = 2*length;
if (index == NOT_FOUND)
}
throw new ElementNotFoundException();
public KeyTable(int startTabSize) {
removeIndex(index);
this.maxTabSize = startTabSize;
}
table = new TableElem[maxTabSize];
length = 0;
}
}
Zasięg widoczności zmiennych
Poziomy testowania:
 Intra-method
 Inter-method
 Intra-class
Zasięg widoczności zmiennych
public class KeyTable {
private static final int NOT_FOUND = -1;
private TableElem[] table;
private int length, maxTabSize;
private int getIndex(String key) {
for (int i=0; i<length; i++)
if (table[i].key.equals(key))
return i;
return NOT_FOUND;
}
private void removeIndex(int index) {
for (int i=index; i<length-1; i++)
table[i] = table[i+1];
table[length-1] = null;
length --;
}
public void add(String key, int value) {
int index = getIndex(key);
if (index == NOT_FOUND) { //add new
table[length] = new TableElem();
table[length].key = key;
table[length].value = value;
length ++;
if (length == maxTabSize)
reallocate();
} else //replace existing
table[index].value = value;
}
public int get(String key)
throws ElementNotFoundException {
int index = getIndex(key);
if (index == NOT_FOUND)
throw new ElementNotFoundException();
return table[index].value;
private void reallocate() {
TableElem[] newT = new TableElem[2*length]; }
for (int i=0; i<length; i++)
public void remove(String key)
newT[i] = table[i];
throws ElementNotFoundException {
table = newT;
int index = getIndex(key);
maxTabSize = 2*length;
if (index == NOT_FOUND)
}
throw new ElementNotFoundException();
public KeyTable(int startTabSize) {
removeIndex(index);
this.maxTabSize = startTabSize;
}
table = new TableElem[maxTabSize];
length = 0;
}
}
CCFG - Class Control Flow Graph
Konstrukcja CCFG, źródło: M.J. Harrold, G. Rothermel – „Performing data flow testing on classes”, Proceedings of the 2nd ACM
SIGSOFT symposium on Foundations of software engineering, 154-163, 1994
Dostosowanie kryteriów do
programów obiektowych

Zasięg widoczności zmiennych
Które instrukcje traktować jako definicje, a
które jako użycia zmiennych reprezentujących
obiekty ?

Dostosowanie kryteriów do
programów obiektowych
private static void test1(boolean b1) {
KeyTable t1 = new KeyTable(10);
KeyTable t2;
t1.add("A", 15);
t1.x = 1;
if (b1) {
System.out.println("A="+t1.get("A"));
}
t2 = t1;
if (b1 == false) {
t2.add("A", 10);
}
System.out.println("A="+t2.get("A"));
}
Reguły dla języka Java - JaBUTi
A.M.R. Vincenzi, J.C. Maldonado
1.Typy agregacyjne traktowane są jako obiekty zawierające referencje do kolejnych
obiektów. Definicja (użycie) któregokolwiek elementu składowego traktowana jest jako
definicja (użycie) całej zmiennej.
2. Dla zmiennej typu a[][] dostęp do elementu jest traktowany jako definicja (użycie)
zmiennej a[][].
3. Każda definicja (użycie) niestatycznego pola innej klasy (lub elementu typu
agregacyjnego) traktowane jest jako użycie referencji do obiektu oraz definicja (użycie) tego
pola.
Przykład: ref_1 oraz ref_2 są referencjami do obiektów klasy C zawierającej pola x oraz y.
W wyrażeniu „ref_1.x = ref_2.y” występują: użycie zmiennych ref_1 i ref_2, użycie
zmiennej ref_2.y oraz definicja zmiennej ref_1.x.
4. W przypadku pól statycznych klas odwołanie traktowane jest jako definicja (użycie) tylko
tego pola. W przykładach „C.x=10” oraz „ref_1.x=10”, gdzie ref_1 jest referencją do obiektu
klasy C, a x jest polem statycznym występuje tylko definicja zmiennej C.x.
5. Wywołania metod traktowane są jako użycie zmiennej będącej referencją obiektu w
kontekście którego wywoływana jest metoda. W przykładzie „ref_1.foo(e1,e2)” występuje
użycie zmiennej ref_1.
6. Wywołanie metody z tej samej klasy traktowane jest jako wywołanie przy użyciu
Źródło:zmiennej
A.M.R. Vincenzi,
this. J.C. Maldonado, W.E. Wong, M.E. Delamaro – „Coverage testing of Java programs and components”,
Science of Computer Programming 56, 211-230, 2005
Określenie definicji i użyć zmiennych w oparciu
o stan obiektu
Mei-Hwa Chen, H.M. Kao
• Stan obiektu – pewna kombinacja wartości zmiennych
będących atrybutami obiektu. Przez wartość dla atrybutu
będącego innym obiektem rozumiany jest jego stan.
• Definicja obiektu – Instrukcja w wyniku której stan obiektu
jest inicjalizowany lub zmieniany.
• Użycie obiektu – Instrukcja w wyniku, której bezpośrednio
lub pośrednio używana jest jedna ze zmiennych określających
stan obiektu.
Źródło: Mei-Hwa Chen, H.M. Kao – „Testing Object-Oriented Programs An Integrated Approach”, Proceedings of the 10th
International Symposium on Software Reliability Engineering, 73-83, 1999
Określenie zbioru metod modyfikujących stan
obiektu
• Na podstawie kodu źródłowego metody (Mei-Hwa Chen,
H.M. Koo)
• Bez znajomości kodu źródłowego (klasy z zewnętrznych
bibliotek)
• C ++ - na podstawie deklaracji :
class Class1 {
public:
int get_value() const;
};
• Java - ?
Określenie zbioru metod modyfikujących stan
obiektu - Java
• Statycznie
Dekompilacja i analiza kodu
• Dynamicznie
1. Instrumentacja kodu
{
stateBefore = get_state(obj);
obj.method1();
if (stateBefore!=get_state(obj))
setDef(obj.getClass(),”method1”);
}
2. Wykonanie programu
Pobranie stanu obiektu
• Wartości wszystkich pól
prywatne – refleksja
• hashCode()
Sample sObj = new Sample();
Class cl = sObj.getClass();
Field[] fields = cl.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object fieldValue =
field.get(sObj);
System.out.println(field +"="+
fieldValue);
}
• Każde wywołanie metody w kontekście tego samego obiektu, musi
zwrócić tą samą wartość (o ile obiekt nie był modyfikowany).
Wartość nie musi być taka sama po ponownym uruchomieniu
programu.
• Jeśli dwa obiekty są równe (wg logiki aplikacji) wywołanie metody
dla obydwu musi zwrócić tą samą wartość.
• Nie jest konieczne, aby wyniki zwrócone przez metodę dla dwóch
różnych obiektów były różne.
Literatura
1. JaBUTi Homepage - http://jabuti.incubadora.fapesp.br/
2. S. Rapps, E.J. Weyuker, - „Selecting software test data using data
flow information.”, IEEE Transactions on Software Engineering 11, 367–
375, 1985
3. A.M.R. Vincenzi, J.C. Maldonado, W.E. Wong, M.E. Delamaro –
„Coverage testing of Java programs and components”, Science of
Computer Programming 56, 211-230, 2005
4. M.J. Harrold, G. Rothermel – „Performing data flow testing on
classes”, Proceedings of the 2nd ACM SIGSOFT symposium on
Foundations of software engineering, 154-163, 1994
Pytania?
Download