oopm, ralf lämmellaemmel/oopm/slides/testing.pdf · @before ausführung vor jeder testmethode...
TRANSCRIPT
Testen von ProgrammenOOPM, Ralf Lämmel
http://www.constructionphotography.com
In Verfeinerung der
Vorlesung zum einfachen
Programmtesten
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen mit JUnit
330
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Ein naiver Testfall
331
public class PrimitiveRecursive {public static int add(int m, int n) {
return m==0 ? n : add(m-1,n)+1;}public static void main(String[] args) {
System.out.println(add(42,46)); // prints 88}
}
Wie wird dies kontrolliert?
Sind damit alle Fälle getestet?
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 332
import org.junit.Test;import static org.junit.Assert.*;
public class TestPrimitiveRecursive {@Testpublic void testAddNormal1() {
assertEquals(88,PrimitiveRecursive.add(42,46));}
}
Import der JUnit-Funktionalität
Hilfsklasse zur Aufbewahrung von Testfällen Markierung
der Testfall-Methode
Erwartetes Ergebnis
Zu testende Berechnung
Systematischer Testfall mit JUnit
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Ausführen von JUnit-Tests
333
“Grün” bedeutet, dass alle Tests
erfolgreich waren.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Systematisches Testen mit dem JUnit-Framework
334
http://junit.org
Automatisierung von Unit-Tests in Java
Sehr gute Tool-Unterstützung bei
Erstellung
Ausführung
Protokollierung
Testausführung auf Knopfdruck
Kent Beck
Erich Gamma
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Markierung von Methoden durch Java-Annotations@Test Testmethode mit Assertions@Before Ausführung vor jeder Testmethode (z.B. zum Erstellen von Testdaten)@After Ausführung nach jeder Testmethode...
Ausführung der markierten Methoden über Java-Reflection
JUnit-Annotationen
335
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Mehr JUnit-Assert-Methoden
assertEquals(int expected,int actual)assertEquals(double expected,double actual,double delta)assertEquals(Object expected, Object actual)assertTrue(<condition>)assertFalse(<condition>)assertNull (Object o)assertNotNull(Object o)assertSame(Object expected, Object actual)assertNotSame(Object expected, Object actual)
336
Reihenfolgebeachten!
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Hinweise zur Verständlichkeit
Sprechende Testnamen (Vergl. testAddUserWithoutPasswd() vs. test1().)
Einfacher und kurzer Code für Testmethoden (Komplexe Tests testen oft mehrere Dinge auf einmal.)
Nähe zwischen Eingaben und erwartetem Ergebnis (Damit sind Tests besser nachvollziehbar.)
337
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Test-Driven Development (TDD)
338
Eine Idee des „Extreme Programming“
Tests sind genau so wichtig wie Funktionalität.
Testfälle zuerst, danach die Implementation.
„Constantly testing“
„Daily Build“
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Mehr JUnit-Beispiele
339
Normalfälle Grenzfälle Fehlerfälle
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Fehlerfall zur Addition
340
@Test(expected=java.lang.StackOverflowError.class)
public void testAdd1stNegative() {
PrimitiveRecursive.add(-1,0);
}
Dokumentation der erwarteten Ausnahme für den Fehlerfall
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Fehlerfall zum MergeSort
341
@Test(expected=java.lang.NullPointerException.class)
public void testMergeSortWithNull() {
mergeSort(null);
}
Dokumentation der erwarteten Ausnahme für den Fehlerfall
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Alternative:Grenzfall zum MergeSort
342
public void testMergeSortWithNull() {
int[] x = null;
mergeSort(x);
assertNull(x);
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Fehlerfälle zum ggT
343
Dokumentation der erwarteten Ausnahme für den Fehlerfall
@Test(expected=java.lang.ArithmeticException.class)public void testGcdOf1and0() {
IterativeMod.gcd(1,0);}@Test(expected=java.lang.ArithmeticException.class)public void testGcdOf0and1() {
IterativeMod.gcd(0,1);}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Grenzfälle
344
Operationen am Anfang/am Ende von Listen und Feldern
“Null” falls dies nicht ohnehin ein Fehlerfall ist
Eine nicht erzielbare Genauigkeit
Grenzen des Definitionsbereichs
Einzelnes Element
Leere Eingabe
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Grenzfälle für lineare Suche
345
public class TestLinear {public static int[] a = {1,2,3,4,5,6,7,9,10};@Testpublic void testFirst() {
assertEquals(0,Program.linear(a, 1));}@Testpublic void testLast() {
assertEquals(8,Program.linear(a, 10));}
...}
Das sind 9 Werte auf den Indizes 0--8.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Normalfälle für lineare Suche
346
public class TestLinear {public static int[] a = {1,2,3,4,5,6,7,9,10};@Testpublic void testSecond() {
assertEquals(1,Program.linear(a, 2));}@Testpublic void testMissing() {
assertEquals(-1,Program.linear(a, 8));}
...}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Fakultätsfunktion
347
/** * @param n non-negative operand * @return factorial (1 * ... * n) */public static int factorial(int n) {
int r = 1;for (int i=1; i <= n; i++)
r *= i;return r;
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Normalfälle für Fakultätsfunktion
348
@Testpublic void testFactorialOf1to4() {
assertEquals(1, factorial(1));assertEquals(2, factorial(2));assertEquals(6, factorial(3));assertEquals(24, factorial(4));
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Grenzfall oder Normalfall?
349
@Test
public void testFactorialOf0() {
assertEquals(1, factorial(0));
}
Aus programmiertechnischer Sicht könnte dies als Grenzfall gelten.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Fehlerfall zur Fakultätsfunktion
350
@Test
public void testFactorialForNegativeOperand() {
assertEquals(1, factorial(-1));
}
Beachte: Fehlerfälle können auf verschiedene Arten und Weisen behandelt werden: Ausnahmen,
Boolesche Werte oder ‚normale‘ Ergebnisrückgabe.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Grenzfall zur Fakultätsfunktion
351
@Test
public void testFactorialWithRangeError() {
assertEquals(-288522240, factorial(17));
}
Beachte: Es ist wichtig, zu versuchen alle Grenzfälle auszumachen. Im konkreten Fall wäre es denkbar, dass die Funktion anders reagiert.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Spezifikationen beim Blackbox-Testen
352
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Methode zur Addition mit “Spezifikation”
353
/** * @param m first operand of addition (non-negative) * @param n second operand of addition (non-negative) * @return the sum of the two operands */public static int add(int m, int n) {
return m==0 ? n : add(m-1,n)+1;}
Was passiert für negative Operanden?Die Spezifikation ist also nicht vollständig.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Anforderungen an Spezifikation
354
Natürlichsprachlich, semiformal oder formal
Eindeutig
Präzise
Korrekt
Vollständig
Widerspruchsfrei
In der Praxis sind diese Anforderungen möglicherweise zu “teuer”. Allerdings helfen Tests als
indirekte Spezifikation.
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Methode zur Addition mit geänderter “Spezifikation”
355
/** * @param m first operand of addition (non-negative) * @param n second operand of addition * @return the sum of the two operands * @throws java.lang.StackOverflowError if m is negative */public static int add(int m, int n) {
return m==0 ? n : add(m-1,n)+1;}
Negative Operanden sind nun erklärt.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Bestandteile einer Methodenspezifikation
356
Vorbedingung
Nachbedingung
Eingabeparameter
Ergebnis
Ausnahmen / Fehler
Seiteneffekte
...
Danksagung: Teile dieser Vorlesung gehen auf Dr. Volker Riediger’s OOPM 2008 Vorlesung zum Testen zurück.
z.B.: Argument amount ist positiv.
z.B.: balance ist um amount erhöht.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Mehr Beispiele für das Testen
FakultätReferenzimplementation als Orakel
PotenzierungReferenzimplementation als Orakel
Reihentwicklung für SinusGenauigkeitsschranken für Vergleich
SortieralgorithmenErschöpfende Fallunterscheidung
Türme von HanoiHerausforderung an die Testbarkeit
357
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Fakultät
358
public class PrimitiveRecursive {public static int factorial(int n) {
return (n == 0) ?1
: n * factorial(n-1);}
}
public class TailRecursive {public static int factorial(int n) {
return factorial(1,n);}private static int factorial(int x, int n) {
return (n==0) ? x : factorial(n*x,n-1);}
}
@Testpublic void testFactorialOf0to10() {
for (int i = 0; i <= 10; i++)assertEquals( PrimitiveRecursive.factorial(i),
TailRecursive.factorial(i));}
Referenzimplementation
Optimierte Implementation
Test
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen der Potenzierung
359
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Potenzierung(Ineffiziente Referenzimplementation)
360
public static int power(int x, int n) {int result = 1;for (int i=1; i<=n; i++)
result *= x;return result;
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Potenzierung(Effizientere Implementation)
361
public static int power(int x, int n) {int k = n;int p = x;int y = 1;while (k>0)
if (k % 2 == 0) {p = p * p;k = k / 2;
}else {
y = y * p;k = k - 1;
}return y;
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen mittels Referenzimplementation
362
public class TestEfficient {@Testpublic void testNormal() {
assertEquals(Inefficient.power(2,1),Efficient.power(2,1));assertEquals(Inefficient.power(2,2),Efficient.power(2,2));assertEquals(Inefficient.power(2,3),Efficient.power(2,3));assertEquals(Inefficient.power(2,4),Efficient.power(2,4));assertEquals(Inefficient.power(3,1),Efficient.power(3,1));assertEquals(Inefficient.power(3,2),Efficient.power(3,2));assertEquals(Inefficient.power(3,3),Efficient.power(3,3));assertEquals(Inefficient.power(3,4),Efficient.power(3,4));
}}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen der Taylor-Reihe für sin
363
@Testpublic void testSinNormal() {
assertTrue(Math.abs(Math.sin(3.1415/2)
- Efficient.sin(3.1415/2))< 1e-6);
}
Beachte: Der Vergleich der Ausgaben mit den Erwartungen muss eventuell akzeptable
Abweichungen einplanen.
Oder verwende:
assertEquals( double expected, double actual, double delta)
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen von Sortieralgorithmen
4 Sortieralgorithmen
BubbleSort
InsertionSort
MergeSort
SelectionSort
Was sind relativ erschöpfende Testdaten?
Betrachte alle möglichen Listen der Länge 0..n.
364
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testdaten für das Sortieren
365
@Beforepublic void setUp() throws Exception {
input0 = new int[0];output0 = new int[0];input1 = new int[] { 42 }; // array of length 1output1 = new int[] { 42 }; // array of length 1input2a = new int[] { 1, 2 }; // sorted array of length 2input2b = new int[] { 2, 1 }; // unsorted array of length 2output2 = new int[] { 1, 2 }; // sorted array of length 2input3a = new int[] { 1, 2, 3 }; // sorted array of length 3input3b = new int[] { 2, 3, 1 }; // unsorted array of length 3input3c = new int[] { 3, 1, 2 }; // unsorted array of length 3input3d = new int[] { 1, 3, 2 }; // unsorted array of length 3input3e = new int[] { 3, 2, 1 }; // unsorted array of length 3input3f = new int[] { 2, 1, 3 }; // unsorted array of length 3output3 = new int[] { 1, 2, 3 }; // sorted array of length 3
}
Die Testdaten werden neu aufgebaut vor jedem Testfall.
Die Testdaten sind Attribute der Testklasse damit alle Testmethoden
darauf zugreifen können.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testfälle zum Sortieren
366
public void testIsSorted();
public void testIsPermutation();
public void testBubbleSort();
public void testInsertionSort();
public void testMergeSort();
public void testSelectionSort();
public void testBubbleSortWithNull();
public void testInsertionSortWithNull();
public void testMergeSortWithNull();
public void testSelectionSortWithNull();
Eigenschaften zur Problembeschreibung des Sortierens
Normalfälle und Grenzfälle des Sortierens mit kombinatorische
Erschöpfung bis zu gewisser Länge
Fehlerfälle mit “null”
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen des Sortiertheitstestes
367
@Testpublic void testIsSorted() {
// Sorted inputassertTrue(isSorted(input0));assertTrue(isSorted(input1));assertTrue(isSorted(input2a));assertTrue(isSorted(input3a));
// Sorted outputassertTrue(isSorted(output0));assertTrue(isSorted(output1));assertTrue(isSorted(output2));assertTrue(isSorted(output3));
// Unsorted inputassertFalse(isSorted(input2b));...
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen des Permutationstestes
368
@Test
public void testIsPermutation() {
assertTrue(isPermutation(input0,output0));
assertTrue(isPermutation(input1,output1));
assertTrue(isPermutation(input2a,output2));
assertTrue(isPermutation(input2b,output2));
...
}
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testen von BubbleSort
369
@Testpublic void testBubbleSort() {
bubbleSort(input0);assertTrue(Arrays.equals(input0,output0));bubbleSort(input1);assertTrue(Arrays.equals(input1,output1));bubbleSort(input2a);assertTrue(Arrays.equals(output2,input2a));bubbleSort(input2b);assertTrue(Arrays.equals(output2,input2b));bubbleSort(input3a);assertTrue(Arrays.equals(output3,input3a));
...}
Mutation der @Before-
vorbereiteten Daten.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Wie testet man Hanoi?
370
public static void move(int n, String from, String temp, String to) {
if (n == 0)
return;
move(n - 1, from, to, temp);
System.out.println("Move disc " + n + " from " + from + " to " + to);
move(n - 1, temp, from, to);
}
Man könnte die Ausgabe unmittelbar in einer Datei sichern und eine Baseline zum Vergleich mit der
Ausgabe vereinbaren. Eventuell ziehen wir aber eine strukturierte Darstellung der Ausgabe vor.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 371
Eine Verbundtyp für Turmbewegungen
public final class Move {public int disc;public String from;public String to;public Move(int disc, String from, String to) {
this.disc = disc;this.from = from;this.to = to;
}}
Der Konstruktor ist optional.Er ermöglicht etwas kompakteren Code.
Warum?
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Türme von Hanoi mit dem Füllen eines Feldes von Bewegungen
372
public static void move(Move[] r, int n, String from, String temp, String to) {
move(r, 0, n, from, temp, to);}
private static int move(Move[] r, int p, int n, String from, String temp, String to) {if (n == 0)
return p;p = move(r, p, n - 1, from, to, temp);Move m = new Move(n, from, to);r[p++] = m;return move(r, p, n - 1, temp, from, to);
}
Fühlstand des Feldes r
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Testanwendung
373
public static void main(String[] args) {Move[] r = new Move[7];move(r, 3, "A", "B", "C");System.out.println(Arrays.toString(r));
}
[Move(1,A,C), Move(2,A,B), Move(1,C,B), Move(3,A,C), ...]
Wie können wir solchen Text erzeugen?
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Einschub: toString() per Datentyp
374
public final class Move {
...
public String toString() {
return "Move(" + disc + "," + from + "," + to + ")";
}
}
Diese Methode kann für jeden Klassentyp implementiert
werden. Es gibt keinen brauchbaren Default.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau 375
Testen des Normalfalls für die Türme von Hanoi
public class TestArrayBased {@Testpublic void testNormal() {
Move[] expected = { new Move(1,"A","C"), new Move(2,"A","B"), new Move(1,"C","B"), new Move(3,"A","C"), new Move(1,"B","A"), new Move(2,"B","C"), new Move(1,"A","C") };
Move[] actual = new Move[expected.length];ArrayBased.move(actual, 3,"A","B","C");assertArrayEquals(expected, actual);
}}
Wann sind zwei Bewegungen gleich?
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Einschub: Strukturelle Gleichheit
376
public final class Move {...public boolean equals(Object o) {
if (!(o instanceof Move))return false;
Move m = (Move)o;return this.disc == m.disc
&& this.from.equals(m.from)&& this.to.equals(m.to);
}}
Wir vergleichen Bewegungen komponentenweise. Dabei fallen Typtest und Down-Cast an, um den
Parameter von equals(...) anzupassen.
Diese Methode kann für jeden Klassentyp
implementiert werden. Der Default ist “==”.
Nur beiläufig erwähnt.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Der Vertrag von equals
377
public boolean equals(Object obj)
Indicates whether some other object is "equal to" this one.
The equals method implements an equivalence relation on non-null object references:• It is reflexive: for any non-null reference value x, x.equals(x) should return true.• It is symmetric: for any non-null reference values x and y, x.equals(y) should return
true if and only if y.equals(x) returns true.• It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns
true and y.equals(z) returns true, then x.equals(z) should return true.• It is consistent: for any non-null reference values x and y, multiple invocations of
x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
• For any non-null reference value x, x.equals(null) should return false.
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html
Nur beiläufig erwähnt.
(C) Ralf Lämmel, OOPM, Universität Koblenz-Landau
Zusammenfassung Methodenspezifikationen
Vorbedingungen für Argumente (und Zustand)Nachbedingungen für Resultat (und Zustand)
Testfälle für Normal-, Grenz- und FehlerfälleSystematische Verwendung von Unit-Testen (JUnit)
Ausblick Statische Verifikation von Spezifikationen