You are on page 1of 39

Software Engineering I

Objektorientiertes Testen

Martin Pinzger
Software Engineering Research Group
University of Klagenfurt
!
!

Follow: @pinzger #SEI_AAU

Objektorientierung und Testen


Theorien
the use of object-oriented design doesnt change any basic testing principles;
what does change is the granularity of the units tested.
[Booch, 1991]
Both, testing and maintenance, are simplified by an OO approach ...
[Rumbaugh, 1991]

In Wirklichkeit sieht es anders aus ...


... we have uncovered a flaw in the general wisdom about OO languages that
proven (that is well-understood, well-tested, and well-used) classes can be
reused as superclasses without retesting the inherited code. [Perry, 1990]
... it costs a lot more to test OO software than to test ordinary software - perhaps
four or five times as much Inheritance, dynamic binding, and polymorphism
create testing problems that might exact a testing cost so high that it obviates the
advantages. [Beizer, 1994]

Fakt ist, Objektorientierung hat das Testen erschwert!


7-2

Besonderheiten der Objektorientierung


Modularisierung
Module als Klassen
Funktionen als Methoden im Kontext von Klassen/Objekten

Kapselung
Abgeschlossenheit ist nicht einfach zu durchbrechen
Der Zugriff auf den Zustand von Objekten ist nur ber die Schnittstelle mglich

Vererbung
Die Subklassen verweisen auf Attribute und Methoden der Superklassen
Wiederverwendbarkeit von Code

Abstrakte und generische Klassen


Sie bentigen Instanzen fr die Ausfhrung
Parameterisierte Klassen
7-3

Besonderheiten der Objektorientierung (forts.)


Polymorphie und dynamische Bindung
Ermglicht die Behandlung von Objekten unterschiedlicher Klassen
Genaue Version der aufgerufenen Methode ist nur zur Laufzeit bekannt

Konstruktoren/Destruktoren
Erzeugung und Freigabe von Objekten

Exceptions (Ausnahmebehandlung)
Beeinflussen den Kontrollfluss eines Programms
!
!

All diese Besonderheiten erschweren das Testen!


7-4

Ebenen im OO Testen
Algorithmische Ebene
(Methodenebene):
Betrifft die Methoden einer Klasse

Methode

(=Funktionen)

Unit

Klassenebene

Klasse

(mit ihren Methoden)

Die Interaktion von Methoden und Daten


untereinander, die innerhalb einer
bestimmten Klasse gekapselt sind

Klassencluster

(=abhngige
Klassenmenge)

Subsystem

(=Zusammengehri
ge Gruppen von
Klassenmengen)

Clusterebene
Die Interaktion zwischen Klassen, die oft
miteinander verbunden sind, um einen
bestimmten Zweck (eine Aufgabe) zu
erfllen

Systemebene
Die Interaktion zwischen den Clustern (das
gesamte System)

System
7-5

Unit vs Integration Testen


Unit = Klasse oder kleiner Cluster von stark verbundenen Klassen
Unit Testing = Intra-Class Testing
Integration Testing = Inter-Class Testing (Cluster von Klassen)
Mnogi rade Integration T i misle da rade Unit test, jer npr. jednoj metodi se koristi druga klasa ili neka
druga vrijednost koja nije u toj metodi.

7-6

Methodenebene - Testen von einzelnen Methoden


Ziel des Testens auf Methodenebene ist die berprfung von
einzelnen Methoden
Methoden entsprechen den Funktionen einer Klasse

Oft besitzen die Methoden starke Abhngigkeiten untereinander und


zu Attributen des Objektes
Methoden knnen hnlich wie bei klassischen Prozeduren getestet
werden
Anwendung von bekannten Techniken (Black-Box, White-Box: kontrollfluss- und
datenflussbezogenes Testen, etc.)

Man braucht dafr


Erzeugung eines Objektes der Klasse (Aufruf des Konstruktors)
berprfung der einzelnen Methoden
7-7

Klassenebene - Intraclass Testing


Methoden werden im Kontext ihrer Klasse (Objekt) geprft
Methoden knnen abhngig vom Zustand der Objekte sein

blicherweise verwendet man zustandsbezogenes Testen -> Die zu


testende Klasse wird explizit oder implizit als endlicher Automat
modelliert
Anwendung von Zustandsdiagrammen
Die Zustnde werden durch die Werte der Attribute definiert

Die Sequenz der Methodenaufrufe erlaubt, die Zustnde zu prfen


Die Ausfhrung einer Methode kann je nach Zustand des Objektes ein
unterschiedliches Ergebnis produzieren

Der Fokus beim OO Testen liegt auf dem Design von reprsentativen
Sequenzen von Methodenaufrufen
7-8

State-Based Partitioning
Erstellung von Testfllen auf Basis wie Methoden den Zustand eines Objekts
verndern
Darstellung der verschiedenen Zustnde von Objekten einer Klasse
Darstellung der Methoden einer Klasse als Zustandsbergnge (nur Methoden, welche den
Zustand eines Objekts ndern)

Testflle enthalten Sequenzen von Methodenaufrufen, die den


Zustandsgraphen (State Machine) traversieren
hnlich wie beim Kontrollflussbezogenen Testen

Zustandsgraphen knnen von der Spezifikation (Black-Box) oder dem Source


Code (White-Box) hergeleitet werden
Beispiele von berdeckungskriterien
Alle Zustnde
Alle Transitionen
Alle Prdikate (Bedingungen fr die Ausfhrung einer Transition)
7-9

Beispiel: Slots in einem Computer


Spezifikation
Slots knnen entweder verbunden (belegt) oder nicht verbunden sein
Verbundene Slots enthaltenen eine kompatible Hardware Komponente
Nicht verbundene Slots sind leer

Klasse fr Slots implementiert folgende Funktionen


Install: Slot kann als optional oder nicht optional installiert werden
Bind: Slot kann mit einer kompatiblen Komponente verbunden werden
Unbind: Slot kann entfernt/geleert werden
isBound: liefert die aktuelle Verbindung, wenn verbunden, sonst leer
isBound

incorporate
0

Not present

unBind
Unbound
bind
unBind

Bound
isBound
7-10

Beispiel: Slots in einem Computer


isBound

incorporate
0

Not present

unBind
Unbound
bind

Bound
isBound

unBind

Mgliche Testflle
TF1: incorporate, isBound, bind, isBound
TF2: incorporate, unBind, bind, unBind, isBound

7-11

Attribute-Based Partitioning
Erstellung von Testfllen auf Basis wie Methoden bestimmte Attribute
einer Klasse verwenden und ndern
Weitere Gruppierung von Methoden, die Attribute verwenden und
ndern und Methoden, die das nicht tun
Beispiel: Bank Account mit den Attributen Balance und Kredit Limit
Partition 1: Methoden, die das Kredit Limit verwenden
Partition 2: Methoden, die das Kredit Limit ndern
Partition 3: Methoden, die das Kredit Limit weder verwenden noch ndern

7-12

Category-Based Partitioning
Erstellung von Testfllen auf Basis der Funktion/Rolle einer Methode
in einer Klasse
Initialisierung, Berechnung, Abfragen, Terminierung von Objekten, etc.

Beispiel: Bank Account


Partition 1: Initialisierung (open, setup)
Partition 2: Berechnung (deposit, withdraw)
Partition 3: Abfragen (balance, summarize, credit limit)
Partition 4: Terminierung (close)
!

7-13

Random Testing
Vorgehensweise
Identifizierung der Methoden einer Klasse
Definition der Bedingungen fr die Ausfhrung der Methoden, z.B. eine Klasse
muss immer initialisiert werden bevor method x() aufgerufen werden kann
Festlegung einer minimalen Aufruf-Sequenz
Generierung von zuflligen Aufruf-Sequenzen

7-14

Beispiel: Random Testing


Bank-Account Klasse mit folgenden Methoden
open, setup, deposit, withdrawal, balance, summarize, credit limit, close

Aufruf-Bedingung
Ein Bank-Account muss zuerst erffnet (open) und am Schuss geschlossen
(close) werden

Definition einer minimalen Aufruf-Sequenz


open - setup - deposit - [deposit | withdrawal | balance| summarize | credit limit]n
- withdrawal - close

Generierung der Aufruf-Sequenzen (Testflle)


TF1: open - setup - deposit - deposit - balance - summarize - withdrawal - close
TF2: open - setup - deposit - withdrawal - deposit - balance - credit limit withdrawal - close
7-15

Clusterebene - Interclass Testing


Diese Ebene entspricht der ersten Ebene von Integration Testing in
OO Software
Der Fokus liegt auf der Interaktion zwischen Klassen
Grundlegende Strategie
Bottom-up Integration folgt den depends Beziehungen zwischen Klassen
A depends on B
A referenziert/verwendet B, z.B. A ruft eine Methode von B auf

-> Teste zuerst Klasse B, dann Klasse A


die Klasse B wird gewhnlich zuerst implementiert und getestet

7-16

Auswahl von Interaktionen zwischen Klassen


Sollten alle mglichen Kombinationen von Interaktionen zwischen
Klassen bercksichtigt werden?
-> Kombinatorische Explosion der Anzahl von Testfllen

Nein, es wird eine Untermenge von Interaktionen selektiert


Alle wichtigen Szenarios, welche in dem Design und der Spezifikation
beschrieben sind
Oder auch eine zufllige Auswahl von Interaktionen (-> Random Testing)

Selektion der Interaktionen auf Basis wie die Methoden den Zustand
von Objekten verndern (siehe auch State-Based Partitioning)
Ganzes Objekt wird als Variable gesehen (anstelle von einzelnen Attributen)

7-17

Grundlegende Strategie fr die Auswahl


Es sollte immer mit Functional (Black-Box) Testing begonnen werden
Die Spezifikation eines Systems ist die beste Quelle fr die Erstellung von
Testfllen

Danach wird Structural (White-Box) Testing mit dem Source code


eingesetzt
Testen der design- und implementierungsspezifischen Details
Anwendung der Prinzipien von datenfluss- und kontrollflussbezogenem Testen

7-18

Beispiel: Auswahl Interaktionen aus der Spezifikation


O:Order

C20:Model

ChiMod:ModelDB

C20Comp:Compoment

C20slot:Slots

ChiSlot:SlotDB

ChiComp:ComponentDB

selectModel()
getmodel(C20)

TF1: Hinzufgen
einer
inkompatiblen
Komponente

select()

extract(C20)

addCompoment(HD60)
contains(HD60)
found
isCompatible(HD60)
incompatible
fail

addCompoment(HD20)
contains(HD20)
found

isCompatible(HD20)
compatible
bind

TF2: Hinzufgen
einer
kompatiblen
Komponente

success

7-19

Herausforderungen beim Interclass Testing


Zustand ist in Objekten gekapselt (eines der Grundprinzipen von OO!)
Nicht jedes Objekt bietet Methoden zum Auslesen der verschiedenen Zustnde
eines Objektes zu jeder Zeit

Wie knnen wir feststellen, ob die Ausfhrung einer Methode den


gewnschten Effekt auf den Zustand eines Objektes hat?
Programme sind zu Beginn nicht immer vollstndig (nicht alle Klassen
wurden bereits implementiert)?

7-20

Mgliche Lsungen, um Zugriff zu erhalten


Modifizierung des Source Codes
Equivalent Scenarios Approach
Scaffolding - Verwendung von Stubs und Mocks

7-21

Modifizierung des Source Codes


Eine mgliche Umsetzung
Verwendung der Features von Programmiersprachen (z.B. in C++ friend Klassen)
Hinzufgen von get Methoden zum Auslesen des Zustandes

In all diesen Fllen wird die Kapselung aufgebrochen


Wird von manchen Experten aber propagiert, da es die Testbarkeit eines Systems
signifikant verbessert
Ist die bewhrte Methode in der Praxis

7-22

Equivalent Scenarios Approach


Erzeugung von Sequenzen von Methodenaufrufen, die zu gleichen
und verschiedenen finalen Zustnden in einem Objekt fhren
Lschen von zirkulren Methodenaufrufen -> sollte zu quivalenten Zustnden
fhren
Lschen/Vertauschen von essentiellen Methoden -> sollte zu Fehlern und/oder
nicht-quivalenten Zustnden fhren

Vergleich der Ergebnisse, sprich den finalen Zustand des Objekts, der
verschiedenen Test-Sequenzen
Zwei Zustnde sind quivalent, wenn all mglichen Sequenzen von
Methodenaufrufen zum gleichen Resultat fhren

7-23

Beispiel: Equivalent Scenarios


// Testfall TFa (siehe Sequenz-Diagramm)!
selectModel(M1)!
addComponent(S1,C1)!
addComponent(S2,C2)!
isLegalConfiguration()!
deselectModel()!
selectModel(M2)!
addComponent(S1,C1) !
isLegalConfiguration()
// Szenario TFa1!
selectModel(M2)!
addComponent(S1,C1)!
isLegalConfiguration()

quivalent zu TFa

// Szenario TFa2!
selectModel(M2)!
addComponent(S1,C1)!
addComponent(S2,C2)!
isLegalConfiguration()

Nicht quivalent zu TFa


7-24

Scaffolding
Tool example:
JUnit
Driver

Classes to be tested

Tool example:
MockMaker
Stubs, Mocks

7-25

Testen von Klassen in einer Vererbungsstruktur


Allgemein gilt
Nur jenes Verhalten einer Subklasse wird getestet, welches nicht schon
ausreichend in der Superklasse getestet wurde
-> Es ist nicht notwendig Methoden in der Subklasse zu testen, die bereits in der Superklasse
getestet wurden, ausser, das Verhalten wurde in der Subklasse verndert
-> Neue und berschriebene (re-definierte) Methoden werden getestet

Wenn eine Superklasse gendert wird, mssen alle Subklassen getestet werden

Abstrakte Klassen und Schnittstellen-Klassen (Interfaces)


Mssen instanziert werden
Testen erfolgt durch das Testen der Subklassen

7-26

Beispiel einer Vererbungshierarchie

7-27

Verfahren 1: Top-Down
Testen von jeder Basis-Klasse, die nicht eine Subklasse von einer
anderen Klasse ist
Teste jede Methode
Teste die Interaktionen zwischen den Methoden

Testen von Klassen, die bereits getestete Klassen verwenden


Verwende Testflle von Superklassen wieder, um Subklassen zu testen (wenn
mglich)
Finde neue Testflle, um die verbleibenden Funktionen in den Subklassen zu
testen

7-28

Verfahren 2: Flattening
Jede Subklasse wird geprft, als
ob alle geerbten Eigenschaften
neu definiert wurden
Testflle fr die Superklassen
knnen wiederverwendet werden

7-29

Vererbung und Dynamisches Binden von Methoden


abstract class Credit { !
...!
abstract boolean validateCredit(Account a, int amt, CreditCard c); !
...!
}

EduCredit
BizCredit
IndividualCredit

USAccount
UKAccount
EUAccount
JPAccount
OtherAccount

VISACard
AmExpCard
StoreCard

Jede polymorphe Methode sollte in ihrem eigenen Klassenumfeld


und in allen Subklassen getestet werden
Alle Kombinationen: 3 x 5 x 3 = 45 Testflle nur fr diese Methode!
7-30

Kombinatorischer Ansatz
Account
USAccount
USAccount
USAccount
UKAccount
UKAccount
UKAccount
EUAccount
EUAccount
EUAccount
JPAccount
JPAccount
JPAccount
OtherAccount
OtherAccount
OtherAccount

Credit
EduCredit
BizCredit
individualCredit
EduCredit
BizCredit
individualCredit
EduCredit
BizCredit
individualCredit
EduCredit
BizCredit
individualCredit
EduCredit
BizCredit
individualCredit

creditCard
VISACard
AmExpCard
ChipmunkCard
AmExpCard
VISACard
ChipmunkCard
ChipmunkCard
AmExpCard
VISACard
VISACard
ChipmunkCard
AmExpCard
ChipmunkCard
VISACard
AmExpCard

Beschrnkung auf
paarweise
Kombination von
dynamisch
gebundenen Objekten
Keine Kombination
von jeweils zwei
Klassen kommt
doppelt vor
-> 15 Testflle

7-31

Testen von polymorphen Methoden

public abstract class Account {!


public int getYTDPurchased() { !
!
if(ytdPurchasedValid) { return ytdPurchased; }!
!
int totalPurchased = 0; !
!
for(Enumeration e=subsidiaries.elements(); e.hasMoreElements();) {!
Account subsidiary = (Account) e.nextElement(); !
totalPurchased += subsidiary.getYTDPurchased(); ! polymorph
!
}!
!
for(Enumeration e=customers.elements(); e.hasMoreElements();) {
Customer aCust = (Customer) e.nextElement(); !
totalPurchased += aCust.getYearlyPurchase(); !
!
}!
!
ytdPurchased = totalPurchased; !
!
ytdPurchasedValid = true; !
!
return totalPurchased; !
} !
!
}
7-32

Lsung mit Datenfluss-bezogenen Techniken


Identifiziere alle berschriebenen Methoden, deren mglichen
Bindings, und DU-Pfade
Binding = Methode in der Subklasse, welche zur Laufzeit diese Methode
berschreibt

Erstelle einen Testfall fr jeden mglichen polymorphen DU-Pfad


Jede mgliche Bindung von den polymorphen Methoden muss individuell
bercksichtigt werden

7-33

Beispiel: Testen von polymorphen Methoden

public abstract class Account {!


public int getYTDPurchased() { !
!
if(ytdPurchasedValid) { return ytdPurchased; }!
DEF
!
int totalPurchased = 0; !
!
for(Enumeration e=subsidiaries.elements(); e.hasMoreElements();) {!
Account subsidiary = (Account) e.nextElement(); !
totalPurchased += subsidiary.getYTDPurchased(); ! DEF + USES
!
}!
!
for(Enumeration e=customers.elements(); e.hasMoreElements();) {
Customer aCust = (Customer) e.nextElement(); !
totalPurchased += aCust.getYearlyPurchase(); ! DEF + USES
!
}!
!
ytdPurchased = totalPurchased; ! USES
!
ytdPurchasedValid = true; !
!
return totalPurchased; !
USES
} !
!
}

7-34

Testen von generischen Klassen


Die Klasse muss mit verschiedenen Parametern instanziert getestet
werden
berprfung, dass einige Instanzierungen korrekt sind
berprfung, dass das Verhalten der erlaubten Instanzierungen konsistent ist
// Generische Klasse Pari<T>! // Testfall mit String!
public class Pair<T> { !
Pair<String> svar = new Pair("world","Hello");
T x, y; !
svar.swap();!
!
System.out.println(svar.x + svar.y); !
public Pair(T _x, T _y) { ! !
this.x = _x; !
// Testfall mit Integer!
this.y = _y;!
Pair<Integer> ivar = new Pair(10,15); !
}!
ivar.swap();!
!
System.out.println(ivar.x + / + ivar.y);
public void swap() { !
T temp = x;
!
x = y;!
y = temp;!
}!
}
7-35

Exception Handling (Ausnahmebehandlung)


Ausnahmen (Exceptions) stellen implizite Kontrollflsse im Programm
dar, die getestet werden sollten
public static Account newAccount(...) throws InvalidRegionException {!
! Account thisAccount = null; !
! String regionAbbrev = Regions.regionOfCountry(!
! ! mailAddress.getCountry()); !
! if (regionAbbrev == Regions.US) {!
!
thisAccount = new USAccount(); !
! } else if (regionAbbrev == Regions.UK) {!
!
....!
! } else if (regionAbbrev == Regions.Invalid) {!
!
throw new InvalidRegionException(mailAddress.getCountry()); !
! } !
...
!
}

7-36

Testen von Ausnahmebehandlung


Allgemeine Design-Regeln fr Ausnahmebehandlung
Ausnahmen sollten nur in wirklichen Ausnahmefllen verwendet werden -> wenn die
normale Ausfhrung einer Methode aufgrund eines Fehlers nicht mehr mglich ist
Eine Ausnahme wird nur abgefangen, wenn sie behandelt wird (keine leeren Catch
Blcke!)

Beim Testen werden Ausnahmen nicht zum normalen Kontrollfluss


gerechnet
Es ergeben sich zu viele mgliche Kombinationen bei der Bercksichtigung von
Ausnahmen

Daher werden Ausnahmen separat getestet


Jede Ausnahmebehandlung (Exception Handler)
Jede throw und re-throw Anweisung
Jede Anweisung, die eine Ausnahme erzeugt, mit der entsprechenden Anweisung,
welche die Ausnahme behandelt
7-37

Example: IndexOutOfBoundException in jUnit


// Checks that an IndexOutOfBoundsException is thrown by this method!
!
@Test(expected= IndexOutOfBoundsException.class) !
public void empty() { !
! new ArrayList<Object>().get(0); !
}
// Asserts that the message of the exception is Index: 0, Size: 0)!
!
@Test!
public void testExceptionMessage() {!
try {!
new ArrayList<Object>().get(0);!
fail("Expected an IndexOutOfBoundsException to be thrown");!
} catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {!
assertThat(anIndexOutOfBoundsException.getMessage(), !
! ! ! is("Index: 0, Size: 0"));!
}!
}
7-38

Literatur
Beizer, B., Testing Technology The Growing Gap, American Programmer, 7(4), 1994, pp. 3-12.
Booch, G., Object Oriented Design With Applications. Benjamin/Cummings, 1991.
Burnstein, I., Practical Software Testing, Springer-Verlag, 2003.
Binder, R., Testing Object-Oriented Software: a Survey, Journal of Software Testing, Verification and Reliability, John Wiley & Sons,
6(3/4), Dec. 1996, pp. 125-252.
Binder, R., Testing Object-oriented Systems: Models, Patterns, and Tools, Addison-Wesley, 4th ed., 2003.
Geetha, B.G., Palanisamy, V., Duraiswamy, K., Singaravel, G., A Tool for Testing of Inheritance Related Bugs in Object-Oriented
Software, Journal of Computer Science 4(1), 2008, pp. 59-65.
Jorgensen, P., Software Testing: A Craftsmans Approach, 2nd ed., CRC Press, 2002.
Kaner, C., Falk, J., Nguyen, Q., Testing Computer Software, Wiley &Sons, 1999.
Perry, D., Kaiser, G.: Adequate Testing and Object-Oriented Programming, Journal of Object-Oriented Programming, 2(5), Jan./Feb.
1990, pp. 13-19.
Pezz, M., Young, M., Software Testing and Analysis: Process, Principles, and Techniques, Wiley & Sons, 2008.
Rumbaugh, J. et al., Object-Oriented Modeling and Design, Prentice Hall, 1991.
Sneed, H., Winter, M., Testen objektorientierter Software, Hanser, 2001.
Vigenschow, U., Objektorientiertes Testen und Testautomatisierung in der Praxis, dpunkt.Verlag, 2005.
7-39