Testen der Persistenzschicht: Best-Practice?

XesperantoX

Mitglied
Hallo,

ich hätte da mal eine Frage zum testen einer Webanwendung.

Die Persitenzschicht ist nach besten (Ge)wissen nach dem DAO-Pattern umgestzt worden mit einer Factory um ggf. auch andere DBS zu unterstützen (wird aber nie im leben passieren).

Da wir nach Vorgabe unsereres Betreuers (Dozent) kein O/R Mapper verwendet haben sind die DAO-Klassen alle mit dem nötigen JDBC-Code versehen. Da wir ein Composite Transfer Object verwenden bekommen die DAOs (bis auf einige ausnahmen) immer das entsprechende TO übergeben und können dann die CRUD-Operationen durchführen.

Meine eigentliche Frage ist jetzt wie ich die DAO-Klassen am einfachsten/sinnvollsten testen kann. Mein Problem bei den Überlegungen ist das die Connections zur DB (MySQL) in den DAO-Klassen über eine Helper-Klasse aufgebaut werden und ich mit JUnit ja (meines Wissens) keine Rollbacks machen könnte, da die Connection, Statements und ResultSets immer am Ende der Methoden in einem Finally-Block geschlossen werden.

Möglichkeiten die mir vorschweben:
1.) Mit JUnit testen und immer nach dem testen einen Dump der Datenbank über die Datenbank jagen... hmm...

2.) Mit JUnit testen und den Code der Helper-Klasse so abändern das der TestCase ein Rollback durchführen kann. DAzu müsste ich aber den Code der App verändern um die Tests zu integrieren...

3.) DBUnit? Was ich bisher gelesen habe schien mir immer mehr in Richtung reiner DB-Test zu gehen.


Wäre super wenn mir jemand sagen könnte ob einer meiner Wege sinnvoll ist oder es vllt. doch eine bessere Alternative gibt von der ich keine Ahnung habe... ;-)

Grüße
Sascha
 
Hallo,

wie werden denn bei dir deine Transaktionen gehandelt? Sagst du an der Connection manuell Rollback / Commit?

Dann könntest du für deinen Testcase immer die selbe Connection verwenden und in einem tearDown() jeweils ein rollback() an der Connection machen.

Das Springframework bietet auch eine schicke JDBC Unterstützung an:
http://static.springframework.org/spring/docs/2.5.x/reference/jdbc.html
in Verbindung mit deklarativem Transactionmanagement:
http://static.springframework.org/spring/docs/2.5.x/reference/transaction.html
und entsprechender Testunterstützung:
http://static.springframework.org/spring/docs/2.5.x/reference/testing.html#integration-testing
brauchst du keine so großen Klimmzüge zu machen wie jetzt...

Gruß Tom
 
Hy,

danke für die schnelle Antwort!

Ich commite grundsätzlich bei den DAOs manuell, beim öffnen der Connection wird zuerst der Commit-Modus umgestellt. Wenn dann alles glatt durchlief commite ich am Ende der Methode die Transaktion und in den Exception-Blöcken mache ich Rollbacks.

Ist für das Projekt leider kein Spring geworden, wir verwenden Stuts, JDBC und der Business-Layer sind POJOs.

So wie du es beschrieben hast müsste ich dann meine DAOs abändern um die Kontrolle über die Connection in den Testcase zu verlagern oder?

Grüße
Sascha
 
Hallo,

wenn du schön brav mit einem Connection interface und nicht mit einer konkreten Implementierung gearbeitet hast, dann hast du immernoch die Chance anstatt deinem DAO direkt die Connection zu geben diese erst noch in einen Proxy zu Wrappen.
Alle Methodenaufrufe an die Connection gehen dann erstmal durch diesen Proxy, dieser lässt sich dann über ein Property konfigurieren ob er bei einem commit()/rollback() tatsächlich die entsprechende Aktion durchführt oder gar nichts macht.
Dann gehst du einfach hin un gibts deinen DAO's in den Testszenarios diesen Proxy anstatt direkt die richtige Connection.

Ungefähr so:
Java:
/**
 * 
 */
package de.tutorials;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.Arrays;
import java.util.List;

/**
 * @author Thomas.Darimont
 * 
 */
public class SQLConnectionProxyExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Connection realConnection = null;

		ConnectionWrappingInvocationHandler connectionWrappingInvocationHandler = new ConnectionWrappingInvocationHandler(
				realConnection, Arrays.asList("commit", "rollback"));

		Connection connection = (Connection) Proxy.newProxyInstance(Thread
				.currentThread().getContextClassLoader(),
				new Class[] { Connection.class },
				connectionWrappingInvocationHandler);

		// conection -> DAO ...

	}

	static class ConnectionWrappingInvocationHandler implements
			InvocationHandler {

		private boolean forward;
		private Connection connection;
		private List<String> exceptionalMethodNames;

		public boolean isForward() {
			return forward;
		}

		public void setForward(boolean forward) {
			this.forward = forward;
		}

		public ConnectionWrappingInvocationHandler(Connection connection,
				List<String> exceptionalMethodNames) {
			this.connection = connection;
			this.exceptionalMethodNames = exceptionalMethodNames;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			if (exceptionalMethodNames.contains(method.getName())
					&& !isForward()) {
				return null;
			} else {
				return method.invoke(connection, args);
			}

		}
	}
}

Gruß Tom
 
Hy,

neee hab ich dem Fall natürlich nicht gemacht... :mad: Aber hab verstanden wie du es meinst, könntest du mir aber evtl. noch sagen was das Interface können sollte oder mir eine Adresse geben wo ich es nachschauen kann?

Grüße
Sascha
 
Zurück