Beispiel: DB

Werbung
Beispiel: DB-Mock (1/7)
• Aufgabe: DB, auf die vereinfachend nur lesend zugeriffen
wird mocken
• warum: benötigte keine DB-Lizenz, garantiert gleiche Werte
ohne aufwändiges „reset“, kein Zeitverlust durch
Verbindungsaufbau
• Ansatz: Statement-Interface von JDBC mocken
• Mock in eigene Klasse zur Wiederverwendung auslagern
• Hinweis: Zur Vereinfachung des Beispiels wird SQL nicht
ganz sauber eingesetzt
Software-Qualität
Stephan Kleuker
211
Beispiel: DB-Mock (2/7): Erinnerung JDBC
class DriverManager
Connection con=
Datenbankverbindung
DriverManager.getConnection(...);
herstellen
Statement stmt=
con.createStatement();
Datenbankanfrage
Ergebnisse
verarbeiten
Verbindung zur DB
schließen
Software-Qualität
ResultSet rs =
stmt.executeQuery(...);
rs.next();
int n = rs.getInt("KNr");
con.close();
Stephan Kleuker
212
Beispiel: DB-Mock (3/7): Programm (1/2)
• Hinweis: Programm auf Testbarkeit ausgelegt; einfaches
Einfügen eines Statement-Objekts
package noten;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Statistik {
private Statement statement;
public Statistik(){
}
public void setStatement(Statement statement){
this.statement=statement;
}
Software-Qualität
Stephan Kleuker
213
Beispiel: DB-Mock (4/7): Programm (2/2)
public double studiDurchschnitt(String name)
throws SQLException{
int anzahl=0;
int summe=0;
ResultSet rs= statement.executeQuery(
"SELECT * FROM Noten WHERE Studi ='" + name + "'");
while (rs.next()) {
anzahl++;
summe += rs.getInt(3);
}
if (anzahl == 0)
return 0d;
else
return summe/ (anzahl * 100d);
}
Software-Qualität
Stephan Kleuker
214
Beispiel: DB-Mock (5/7): Mock-Aufbau (1/2)
package noten;
import java.sql.ResultSet;
import java.sql.Statement;
import org.jmock.Expectations;
import org.jmock.Mockery;
public class DBMock {
public Statement dbErstellen() throws Exception {
final Mockery context = new Mockery();
final Statement st = context.mock(Statement.class);
final String[][] pruefungen = { // Beispieltabelle
{ "Ute", "Prog1", "400" },
{ "Uwe", "Prog1", "230" },
{ "Ute", "Prog2", "170" } };
Software-Qualität
Stephan Kleuker
215
Beispiel: DB-Mock (6/7): Mock-Aufbau (2/2)
context.checking(new Expectations() {
{
final ResultSet r = context.mock(ResultSet.class,"r");
context.checking(new Expectations() {
{
oneOf(r).next();
will(returnValue(true));
oneOf(r).next();
will(returnValue(true));
oneOf(r).next();
will(returnValue(false));
oneOf(r).getInt(3);
will(returnValue(Integer.parseInt(pruefungen[0][2])));
oneOf(r).getInt(3);
will(returnValue(Integer.parseInt(pruefungen[2][2])));
}
});
allowing(st).executeQuery(
"SELECT * FROM Noten WHERE Studi='Ute'");
will(returnValue(r));
Stephan Kleuker
216
} Software-Qualität
});
Beispiel: DB-Mock (7/7): Tests (Ausschnitt)
public class StatistikTest {
private Statement db;
private Statistik s;
@Before
public void setUp() throws Exception{
db = new DBMock().dbErstellen();
s = new Statistik();
s.setStatement(db);
}
@Test
public void testSchnittUte() throws SQLException {
Assert.assertTrue(2.85 == s.studiDurchschnitt("Ute"));
}
Software-Qualität
Stephan Kleuker
217
Alternativwerkzeug Mockito
• generell: immer wieder nach Alternativen suchen
• Mockito basierte auf EasyMock (auch Alternative), hat dann
eigenen Weg eingeschlagen
(https://code.google.com/p/mockito/)
• Vor Werkzeugvergleich immer Kriterien überlegen, z. B.
– wirklich benötigter Funktionsumfang
– unterstützte Technologien
– Lizenz, Kosten
– Größe des Entwicklungsteams
– Dokumentation, Support
– …
• Kriterien individuell im Projekt gewichten
Software-Qualität
Stephan Kleuker
218
Beispiel: Buchung, Konto, Logging (1/4)
import org.mockito.Mockito;
…
@Before
public void setUp() throws Exception {
buchung = new Buchung();
}
Direkte MockErstellung;
Objekte direkt
nutzbar, hier noch
keine besonderen
Rückgabewerte
@Test
public void testIstLiquide1(){
final Konto k = Mockito.mock(Konto.class);
Buchung.logging = Mockito.mock(LogDatei.class);
try {
Prüfung, ob
buchung.abbuchen(0, k, BETRAG1);
Methoden so
Assert.fail("fehlender Abbruch");
aufgerufen
} catch (BuchungsException e) {
Mockito.verify(k).istLiquide(BETRAG1);
Mockito.verify(Buchung.logging).schreiben(
"0 abgebrochen, insolvent");
Software-Qualität
Stephan Kleuker
219
}
}
Beispiel: Buchung, Konto, Logging (2/4)
@Test
public void testIstLiquide3(){
Konto k = Mockito.mock(Konto.class);
Buchung.logging = Mockito.mock(LogDatei.class);
Mockito.when(k.istLiquide(BETRAG1)).thenReturn(true);
try {
buchung.abbuchen(0, k, BETRAG1);
} catch (BuchungsException e) {
Assert.fail("nicht erwarteter Abbruch");
}
//context.assertIsSatisfied();
Mockito.verify(k).istLiquide(BETRAG1);
Mockito.verify(k).abbuchen(BETRAG1);
Mockito.verify(Buchung.logging).schreiben("0
Spezifikation des
Rückgabewerts
(Default false)
bearbeitet");
}
Software-Qualität
Stephan Kleuker
220
Beispiel: Buchung, Konto, Logging (3/4)
@Test
public void testIstLiquide4(){
final Konto k = Mockito.mock(Konto.class);
Buchung.logging = Mockito.mock(LogDatei.class);
Mockito.when((k).istLiquide(BETRAG1))
mehrere
.thenReturn(true).thenReturn(false);
Ergebnisse
try {
nacheinander
buchung.abbuchen(0, k, BETRAG1);
} catch (BuchungsException e) {
Assert.fail("nicht erwarteter Abbruch"); geforderter
mehrfacher
}
Methodenaufruf
try {
buchung.abbuchen(0, k, BETRAG1);
Assert.fail("fehlender Abbruch");
} catch (BuchungsException e) {
Mockito.verify(k,Mockito.times(2)).istLiquide(BETRAG1);
Mockito.verify(k).abbuchen(org.mockito.Matchers.anyInt());
Mockito.verify(Buchung.logging,Mockito.times(2))
.schreiben(Mockito.anyString());
}
Mockito-Matcher 221
Stephan Kleuker
} Software-Qualität
Beispiel: Buchung, Konto, Logging (4/4)
import org.hamcrest.Matchers;
@Test
public void testIstLiquide5(){
Konto k = Mockito.mock(Konto.class);
Buchung.logging = Mockito.mock(LogDatei.class);
Mockito.when(k.istLiquide(Mockito
Nutzung von
Hamcrest.intThat(Matchers.greaterThan(42))))
Matchern
.thenThrow(new NumberFormatException());
try {
Werfen von
buchung.abbuchen(0, k, 100);
Exceptions
Assert.fail("fehlender Abbruch");
} catch (NumberFormatException e) {
Mockito.verify(k).istLiquide(Mockito
.intThat(Matchers.greaterThan(42)));
} catch (BuchungsException e) {
Assert.fail("falsche Exception");
}
} Software-Qualität
Stephan Kleuker
222
Kleine Verwirrung
• Mockito hat eigene Matcher-Klasse org.mockito.Matchers
Mockito.verify(k).abbuchen(org.mockito.Matchers.anyInt());
• Hinweis: wenn ein Argument durch Matcher ersetzt,
müssen alle ersetzt werden
verify(mock).someMethod(anyInt(), anyString()
, eq("third argument"));
• Mockito-Matcher ermöglichen auch die Nutzung von
Hamrest-Matchern
Mockito.verify(k).istLiquide(Mockito
.intThat(org.hamcrest.Matchers.greaterThan(42)));
• Methoden : argThat(.), booleanThat(.), byteThat(.),
charThat(.), doubleThat(.), floatThat(.), intThat(.),
longThat(.), shortThat(.)
Software-Qualität
Stephan Kleuker
223
Spying
• Möglichkeit Methoden realer Objekte zu verändern
• sinnvoll bei Legacy-Code oder um Zugriffe auf externe
Systeme zu vermeiden
public static void main(String[] args) {
Spy-Erstellung
List<Integer> list = new LinkedList<Integer>();
list.add(1);
alternative
List<Integer> spy = Mockito.spy(list);
Beschreibung
für Ergebnis
Mockito.doReturn(42).when(spy).get(0);
System.out.println(spy.get(0) +" :: " + spy.size());
@SuppressWarnings("unchecked")
List<Integer> list2 = Mockito.mock(List.class);
42 :: 1
list2.add(1);
42 :: 0
Mockito.doReturn(42).when(list2).get(0);
System.out.println(list2.get(0) +" :: " + list2.size());
}
Software-Qualität
Stephan Kleuker
224
Nicht alles kann gemockt werden (JMock, Mockito)
public static void main(String[] args) {
String s = Mockito.mock(String.class);
Mockito.doReturn(42).when(s).length();
System.out.println(s+ " hat Laenge" + s.length());
}
Exception in thread "main"
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
at MockitoSpielerei.main(MockitoSpielerei.java:8)
Software-Qualität
Stephan Kleuker
225
etwas Spielerei
public static void main(String[] args) {
BigInteger s = Mockito.mock(BigInteger.class);
Mockito.doReturn("42").when(s).toString();
System.out.println("s hat Wert " + s +" :: "+s.longValue());
BigInteger tt = new BigInteger("1234567890123456789");
BigInteger t = Mockito.spy(tt);
Mockito.doReturn("42").when(t).toString();
System.out.println("t hat Wert " + t +" :: "+t.longValue());
System.out.println(t.getClass());
}
s hat Wert 42 :: 0
t hat Wert 42 :: 1234567890123456789
class
$java.math.BigInteger$$EnhancerByMockitoWithCGLIB$$8e32a13f
Software-Qualität
Stephan Kleuker
226
weitere Möglichkeiten / Fazit
• Mockito bietet einige weitere Möglichkeiten, dabei immer
teilweise spezielle Randbedingungen beachten
http://docs.mockito.googlecode.com/hg/latest/org/mockito/
Mockito.html
• Mockito etwas einfacher zu nutzen als JMock; insbesondere
mehr "normales Java"
• Mockito und JMock haben kleine Anteile, die der andere
nicht kann (man kann beide zusammen einsetzten)
• generell kritischer Werkzeugvergleich immer sinnvoll; oft
auch gemeinsame Nutzung eine Lösung
Software-Qualität
Stephan Kleuker
227
Herunterladen