340 likes | 506 Views
Testowanie oprogramowania metodą badania pokrycia kodu. Artur Rembiszewski. Opiekun: dr inż. Ilona Bluemke. Plan prezentacji. Przypomnienie podstawowych pojęć Testowanie metodą badanie pokrycia kodu Pokrycie przepływu danych Dostosowanie kryteriów przepływu danych do programów obiektowych
E N D
Testowanie oprogramowania metodą badania pokrycia kodu Artur Rembiszewski Opiekun: dr inż. Ilona Bluemke
Plan prezentacji • Przypomnienie podstawowych pojęć • Testowanie metodą badanie pokrycia kodu • Pokrycie przepływu danych • Dostosowanie kryteriów przepływu danych do programów obiektowych • Zasięg widoczności zmiennych • Klasyfikacja instrukcji • Prezentacja mojego programu • Pytania
Podział metod testowania Testowanie Strukturalne(white-box) Funkcjonalne (black-box) Statyczne Dynamiczne
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
Pokrycie instrukcji Wymagane wykonanie co najmniej raz każdej instrukcji kodu programu
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 danychRodzaje • 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 publicclass KeyTable { privatestaticfinalintNOT_FOUND = -1; private TableElem[] table; privateintlength, maxTabSize; privateint getIndex(String key) { for (int i=0; i<length; i++) if (table[i].key.equals(key)) return i; returnNOT_FOUND; } privatevoid removeIndex(int index) { for (int i=index; i<length-1; i++) table[i] = table[i+1]; table[length-1] = null; length --; } privatevoid reallocate() { TableElem[] newT = new TableElem[2*length]; for (int i=0; i<length; i++) newT[i] = table[i]; table = newT; maxTabSize = 2*length; } public KeyTable(int startTabSize) { this.maxTabSize = startTabSize; table = new TableElem[maxTabSize]; length = 0; } publicvoid add(String key, int value) { intindex = 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; } publicint get(String key) throws ElementNotFoundException { int index = getIndex(key); if (index == NOT_FOUND) thrownew ElementNotFoundException(); returntable[index].value; } publicvoid remove(String key) throws ElementNotFoundException { int index = getIndex(key); if (index == NOT_FOUND) thrownew ElementNotFoundException(); removeIndex(index); } }
Zasięg widoczności zmiennych Poziomy testowania: • Intra-method • Inter-method • Intra-class
Zasięg widoczności zmiennych publicclass KeyTable { privatestaticfinalintNOT_FOUND = -1; private TableElem[] table; privateintlength, maxTabSize; privateint getIndex(String key) { for (int i=0; i<length; i++) if (table[i].key.equals(key)) return i; returnNOT_FOUND; } privatevoid removeIndex(int index) { for (int i=index; i<length-1; i++) table[i] = table[i+1]; table[length-1] = null; length --; } privatevoid reallocate() { TableElem[] newT = new TableElem[2*length]; for (int i=0; i<length; i++) newT[i] = table[i]; table = newT; maxTabSize = 2*length; } public KeyTable(int startTabSize) { this.maxTabSize = startTabSize; table = new TableElem[maxTabSize]; length = 0; } publicvoid 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; } publicint get(String key) throws ElementNotFoundException { int index = getIndex(key); if (index == NOT_FOUND) thrownew ElementNotFoundException(); returntable[index].value; } publicvoid remove(String key) throws ElementNotFoundException { int index = getIndex(key); if (index == NOT_FOUND) thrownew ElementNotFoundException(); removeIndex(index); } }
Zasięg widoczności zmiennych Poziomy testowania: • Intra-method • Inter-method • Intra-class
Zasięg widoczności zmiennych publicclass KeyTable { privatestaticfinalintNOT_FOUND = -1; private TableElem[] table; privateintlength, maxTabSize; privateint getIndex(String key) { for (int i=0; i<length; i++) if (table[i].key.equals(key)) return i; returnNOT_FOUND; } privatevoid removeIndex(int index) { for (int i=index; i<length-1; i++) table[i] = table[i+1]; table[length-1] = null; length --; } privatevoid reallocate() { TableElem[] newT = new TableElem[2*length]; for (int i=0; i<length; i++) newT[i] = table[i]; table = newT; maxTabSize = 2*length; } public KeyTable(int startTabSize) { this.maxTabSize = startTabSize; table = new TableElem[maxTabSize]; length = 0; } publicvoid 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; } publicint get(String key) throws ElementNotFoundException { int index = getIndex(key); if (index == NOT_FOUND) thrownew ElementNotFoundException(); returntable[index].value; } publicvoid remove(String key) throws ElementNotFoundException { int index = getIndex(key); if (index == NOT_FOUND) thrownew ElementNotFoundException(); removeIndex(index); } }
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 privatestaticvoid 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 zmiennej this. Źródło: 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
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 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); } • hashCode() • 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