e-Commerce

Transcription

e-Commerce
e-Commerce
Test First !
Automatisiertes Testen mit dem JUnit Framework
Prof. Dr. Nikolaus Wulff
JUnit
• JUnit ist das Opensource Testframework.
• Es existieren Portierungen für fast alle
objektorientierten Sprachen:
– Smalltalk, C++, Java, C#, etc.
• JUnit ist konzipiert als Komponententest in der
Hand des Entwicklers, es eignet sich jedoch auch
für Funktionale- und Lasttests.
• Es gibt Erweiterungen zum Test von Web- und
EJB-Anwendungen (HttpUnit, Cactus).
• Link: http://www.junit.org
e-Commerce
2
Testentwicklung
• Zu jeder Klasse im Projekt wird ein passender
Test geschrieben, der das Verhalten der jeweiligen
Klasse testet.
• JUnit definiert das Rahmenwerk für derartige
Tests, das in allen gängigen IDE's integriert ist.
• Der Entwickler leitet seine Tests von einer JUnit
TestCase Klasse ab und implementiert nur noch
die speziellen, fachlichen Testmethoden.
e-Commerce
3
Alles ist ein Test
Package: junit.framework
TestRunner
runs
<<Interface>> 0..*
Test
JUnit
Framework
•
TestCase
MyClass
aMethod()
•
•
MyClassTest
testAMethod()
TestSuite
Eigenentwicklung
•
Der Test ist das zentrale Objekt.
Der JUnit TestRunner, führt den Test
aus und protokolliert die Ergebnisse.
Ein Test ist entweder ein einzelnes
TestCase oder eine Ansammlung von
Tests zusam-mengefasst in einer
TestSuite.
Zu jeder Klasse "MyClass" wird eine
entsprechende Testklasse
"MyClassTest" geschrieben, die die
JUnit TestCase Basisklasse erweitert.
e-Commerce
4
Testausführung
• JUnit sucht per Reflection alle Methoden in der
Testklasse, deren Namen mit »test« beginnen und
führt diese in einer definierten Sequenz aus:
–
–
–
–
Konstruktor
setUp
run: testXXX
tearDown
Erzeugt das Testobjekt.
Initialisiert den Test.
Führt den Test durch.
Auf- und Abräumen des Tests.
• Die Testergebnisse werden gesammelt und
visualisiert (GUI) oder protokolliert (Ant).
e-Commerce
5
Eclipse JUnit Plugin
e-Commerce
6
Testvalidierung
• Die Ergebnisse der zu testenden Klasse / Methode werden in jeder
Testmethode validiert.
• Hierzu werden Bedingungen formuliert, die den Rückgabewert der zu
testenden Methode mit einem als wahr bekannten Wert vergleichen.
• Dieser Vergleich wird in der JUnit Methode assertEquals oder
assertTrue ausgewertet.
/**
* test the addition operation.
*/
public void testAdd() throws Exception {
double expected = 2 + 4;
double result = calculator.calculate(2, 4, '+');
super.assertEquals(expected,result,DELTA);
}
e-Commerce
7
Das JUnit Framework
<<Interface>>
TestListener
TestRunner
(from ui)
0..*
runs
<<Interface>>
Test
TestResult
0..*
failur e
0..*
0..*
run()
error
0..*
TestCase
TestSuite
TestFailure
setUp()
tearDo wn ()
run()
addTest()
run()
• Das folgende
Klassendiagramm zeigt das
vollständige JUnit Framework.
• Es gibt spezielle Erweiterungen
für wiederholte Test oder
bestimmte Setups, z.B.
Datenbankverbindungen etc.
decorate
TestDecorator
(from extensions)
TestSetup
RepeatedTest
(from extensions)
(from extensions)
e-Commerce
8
Qualitätssicherung
• Stringentes Arbeiten mit JUnit etabliert eine effiziente
Qualitätssicherung im Projekt.
• Angefangen von einzelnen Klassen lassen sich ganze
Testhierarchien bis hin zum gesamten Projekt aufbauen.
• Wird dieser Projekttest in die tägliche Master-Build
integriert, ist immer bekannt, wie es um das Projekt
bestellt ist...
• Zugleich verbessert der Test-First Ansatz die Definition
von Schnittstellen und Architektur.
e-Commerce
9
TestSuite Hierarchie
Project
ProjectTest
1
1..*
1..*
jeder Test ist für sich
alleine in JUnit
lauffähig...
1..*
Package
PackageTest
1
TestSuite
1..*
1..*
<<Interface>>
Test
1..*
1..*
Class
TestCase
ClassTest
1
1..*
1..*
1..*
Method
testMethode
1
1..*
Die Tests folgen der
Package-Struktur, haben jedoch im
Dateisystem eine eigene Wurzel Test,
so daß die Build unabhängig von den
TestCases läuft...
e-Commerce
10
Was ist zu Testen?
• Im Prinzip alles ... :-)
• Aufwand und Nutzen abwägen!
– Testen muß bezahlbar sein.
– Ungetestete Software kann teuer werden ...
– Sicherheitsrelevante Software (Luftfahrt, Medzin etc.) stellt
andere Ansprüche!
•
•
•
•
Inkrementelle und iterative Entwicklung
=> Tests müssen häufig wiederholt werden.
=> Tests müssen automatisierbar sein.
Preisfrage: Wer testet den Test(er)?
e-Commerce
Testpragmatik
• Tests müssen wiederhol- und reproduzierbar sein.
– Definierter Anfangs- und Endzustand.
– Eindeutigkeit des Testergebnis.
•
•
•
•
•
Möglichst alle Klassen und Methoden testen.
Getter und Setter symmetrisch testen.
Bereiche abdecken. Linker und rechter Rand!
Pre- und PostConditions überprüfen.
Sowohl positiv als auch negativ Fälle testen.
– => if-else und switch-case werden abgedeckt.
• Gezielt Exceptions provozieren.
e-Commerce
Testpragmatik cont.
• Test first Ansatz.
– Jede neue Anforderung erfordert einen neuen Test.
– Der Test wird vor der Implementierung geschrieben.
• Wird ein Bug gefunden, so wird dazu ein Test
geschrieben und anschließend der Bug gefixed.
– Das Wesentliche wird getestet.
• Ein einmal entwickelter Test wird an aktuelle
Weiterentwicklungen angepasst und nicht entfernt.
• Die Testüberdeckung wächst dynamisch.
– Die wesentlichen Klassen werden am meisten getestet.
e-Commerce
BookStore Test
TestCase
(from j unit.framew ork)
BookStoreTest
tests
-dri ver
BookStore
<<uses>>
find/store
<<Interface>>
Driver
(from j av a.sql)
<<creates>>
<<creates>>
<<Interface>>
Connection
Book
(from j av a.sql)
Book attributs map
to the ResultSet
<<creates>>
<<Interfac e>>
ResultSet
• Test mit JUnit.
• Typische zu testende Operationen sind:
(from j av a. sql)
–
–
–
–
<<creates>>
<<Interface>>
Statement
(from j av a.sql)
findAll
findByXXX
store per update oder insert
Löschen per delete
e-Commerce
14
Testaufbau
public class BookStoreTest extends TestCase {
private
private
private
private
private
final static String DRIVER = "org.gjt.mm.mysql.Driver";
final static String DBURL = "jdbc:mysql://localhost/bookstore"
final static String USER = "bookstore";
final static String PWD = "bookstore";
BookStore bookstore;
Verwendet wird der MySQL
/**
Treiber für die bookstore Datenbank.
* @param arg0
URL, User und Passwort wie angegeben
*/
public BookStoreTest(String arg0) {
super(arg0);
}
/**
* Setup for the test.
*/
public void setUp() throws Exception {
bookstore = new BookStore(DRIVER, DBURL, USER, PWD);
}
e-Commerce
15
Test von find und store
/**
* test the findAll method.
* @throws Exception
*/
public void testFindAll() throws Exception {
Collection result = bookstore.findAll();
assertTrue("result empty ", result.size() > 0);
}
/**
* test the insert operation.
* @throws Exception
*/
public void testInsert() throws Exception {
String isbn = (new Date()).toString();
Book book = new Book(isbn, "junit", getName(), "JUnit Test");
bookstore.store(book);
Collection result = bookstore.findByIsbn(isbn);
assertTrue("insert not found ", result.size() == 1);
}
e-Commerce
16
BookStore Erzeugung
public class BookStore {
private final Driver driver;
private final String url;
private final String user;
private final String password;
/**
* Constructor to initialize the BookStore database connection.
*
* @param driverName
String classname of the JDBC driver
* @param url
String the url of the database
* @param user
String the user name of the database
* @param password
String the database password
* @throws Exception
any kind of exceptions to be thrown...
*/
public BookStore(String driverName, String url, String user,
String password) throws Exception {
Class clazz = Class.forName(driverName);
this.driver = (Driver) clazz.newInstance();
this.url = url;
this.user = user;
this.password = password;
}
e-Commerce
17
Verbindung zur Datenbank
/**
* Returns a sql connection to the database.
*
* @return java.sql.Connection the connection to use
* @throws java.sql.SQLException in case of an error
*/
private Connection getConnection() throws SQLException {
Properties props = new Properties();
props.setProperty("user", user);
props.setProperty("password", password);
return driver.connect(url, props);
}
• Die Verbindung zur Datenbank wird für autorisierte
User mit entsprechendem Paßwort erzeugt.
• Im Fehlerfall wird eine SQLException geworfen.
• Die Ressourcen müssen nach dem Gebrauch per
close Befehl wieder freigegeben werden.
e-Commerce
18
Resourcenfreigabe
private void releaseResources(Connection con,
Statement stm, ResultSet rs) {
try {
if (rs != null)
rs.close();
} catch (Exception e) {
handleError(e);
}
try {
if (stm != null)
stm.close();
} catch (Exception e) {
handleError(e);
}
try {
if (con != null)
con.close();
} catch (Exception e) {
handleError(e);
}
}
e-Commerce
19
Generische Suche
public Collection findByAuthor(String author) {
String where = " where author='" + author + "'";
return find(where);
}
public Collection findByIsbn(String isbn) {
String where = " where isbn='" + isbn + "'";
return find(where);
}
public Collection findByTitle(String title) {
String where = " where title='" + title + "'";
return find(where);
}
Alle Suchoperationen verzweigen auf
public Collection findAll() { die interne, generische find Operation.
return find("");
}
Zurückgegeben wird eine Collection
gefüllt mit Büchern.
e-Commerce
20
Die generische Suchroutine
private Collection find(String where) {
ResultSet set = null; Statement stm = null; Connection con = null;
ArrayList bookList = new ArrayList();
String sql = "select * from books " + where;
try {
con = getConnection();
stm = con.createStatement();
set = stm.executeQuery(sql);
while (set.next()) {
long id = set.getLong("id");
String isbn = set.getString("isbn");
String author = set.getString("author");
String title = set.getString("title");
String description = set.getString("description");
bookList .add(new Book(id, isbn, author, title, description));
}
} catch (SQLException e) {
handleError(e);
} finally {
releaseResources(con, stm, set);
}
return bookList ;
}
e-Commerce
21
Generisches Speichern
public void store(Book book) {
if (findById(book.id.longValue()).size() == 0) {
insert(book);
} else {
update(book);
}
}
• Je nach dem ob das Buch schon in der Datenbank
vorhanden ist oder nicht muss unterschiedlich
vorgegangen werden:
– Buch ist neu => Insert Statement
– Buch ist alt => Update Statement
e-Commerce
22
Speichern per Insert
private void insert(Book book) {
StringBuffer sql = new StringBuffer("insert into books ");
sql.append("(isbn,author,title,description) ");
sql.append(" values(");
sql.append("'");
sql.append(book.isbn);
sql.append("',");
sql.append("'");
sql.append(book.author);
insert und update Methoden erzeugen
sql.append("',");
ein Kommando, das an die executeSQL
sql.append("'");
Methode übergeben & ausgeführt wird.
sql.append(book.title);
sql.append("',");
sql.append("'");
sql.append(book.description);
sql.append("'");
sql.append(")");
// forward the sql string to the executeSQL method
executeSQL(sql.toString());
}
e-Commerce
23
Speichern per Update
private void update(Book book) {
StringBuffer sql = new StringBuffer("update books ");
sql.append(" set isbn='");
sql.append(book.isbn);
sql.append("', author='");
sql.append(book.author);
sql.append("', title='");
sql.append(book.title);
sql.append("', description='");
sql.append(book.description);
sql.append("' where id=");
sql.append(book.id);
// forward the sql string to the executeSQL method
executeSQL(sql.toString());
}
insert und update Methoden erzeugen
ein Kommando, das an die executeSQL
Methode übergeben & ausgeführt wird.
e-Commerce
24
Löschen per Delete
private void delete(Book book) {
StringBuffer sql = new StringBuffer("delete from books ");
sql.append(" where id=");
sql.append(book.id);
// forward the sql string to the executeSQL method
executeSQL(sql.toString());
}
private void executeSQL(String sql) {
Connection con = null;
Statement stm = null;
try {
con = getConnection();
stm = con.createStatement();
stm.executeUpdate(sql);
} catch (SQLException e) {
handleError(e);
} finally {
releaseResources(con, stm, null);
}
}
e-Commerce
25
Übung
• Erstellen Sie eine BookStore Datenbank.
• Registrieren Sie die Datenbank als ODBC Quelle.
• Implementieren Sie die BookStore Anwendung.
– store(Book b) und delete(Book b)
– findAll
– findByXXX xxx{Author, Isbn,Title, Id}
• Testen Sie alle Operationen mit JUnit.
• Tip: Nehmen Sie die Quellcodeauszüge als
Vorlage.
e-Commerce
26