Archive for the 'Test Toccata' Category

Unit-Tests Teil 2: Integrationstests

Thursday, April 16th, 2009

Im Gegensatz zu Mocktests beschränkt sich ein Integrationstest nicht auf die JVM, sondern involviert auch Fremdsysteme wie Datenbanken, LDAPs oder Webservices. Ein Integrationstest ist auf immer gleichbleibende Testdaten im Fremdsystem angewiesen, muss aber ggf. auch den schreibenden Zugriff testen. Hier bieten sich Transaktionen an: Jede Testmethode läuft in einer eigenen Transaktion, die unabhängig vom Testergebnis immer wieder zurückgerollt wird. Somit bleibt das Fremdsystem in einem definierten Zustand. Dennoch sollten Integrationstests nicht auf den späteren Liveinstanzen ausgeführt werden, sondern immer auf dedizierte Testinstanzen. Allerdings sollte sichergestellt sein, dass die Test- und Liveinstanzen sich gleich verhalten.

Integrationstests bieten sich insbesondere für Dao-Klassen an, da hier der Zugriffscode für Fremdsysteme gekapselt wird. Gute Unterstützung bietet hier das Spring Framework mit den Annotationen @RunWith, @Transactional und @TransactionalConfiguration.

Beispiel-Code:

  1. @ContextConfiguration(locations = { "/applicationContext-test-jdbc-ldap-transactional.xml" })
  2. @RunWith(SpringJUnit4ClassRunner.class)
  3. @Transactional
  4. @TransactionConfiguration(defaultRollback = true)
  5. public class LdapUserDaoImplIntegrationTest
  6. {
  7. ...
  8.         @Test
  9.         public void createUser()
  10.         {
  11.                 // create the user
  12.                 LdapUser ldapUser = createLdapUserPeterMueller();
  13.                 MutableLdapUserIdentity identity = ldapUser.getIdentity();
  14.                 String id = identity.getSamAccountName();
  15.                 // method under test
  16.                 ldapUserDaoImpl.createUser(ldapUser);
  17.  
  18.                 LdapUser testUser = ldapUserDaoImpl.getLdapUserBySamAccountName(id);
  19.                 Assert.assertNotNull("user should now exist in ldap.", testUser);
  20.                 // test more
  21.         }
  22. }

Vorteile:

  • Das konkrete Verhalten eines Fremdsystems (z.B. Active Directory, oder einer MSSQL) kann abgeprüft werden.
  • Fremde Webservices können automatisiert auf ihre Konsistenz geprüft werden.

Mögliche Stolperfallen:

  • Integrationstests laufen nicht offline. Bei Mavenprojekten sollten sie für die Offline-Entwicklung abgeschaltet werden, da sonst der Build scheitert. Dies kann durch ein eigenes “mobile”-Profil in Kombination mit dem maven-sunfire-plugin sichergestellt werden.
  • Die Testausführung dauert bei Integrationstest im Vergleich zu “normalen” JUnit-Tests wesentlich länger. Bei steigender Anzahl von länger laufenden Tests ist ein kompletter Durchlauf der Tests auf den Entwicklungsmaschinen nicht mehr sinnvoll.

Unit-Tests Teil 1: Mocks

Friday, February 27th, 2009

Diese neue Artikelserie stellt Variaten von Unit-Tests vor und geht auf die jeweiligen Eigenschaften und Einsatzzwecke ein. Im ersten Teil werden Mock-Tests vorgestellt.

In Mock-Tests wird die Interaktion des zu testenden Objekts mit einem Mock-Objekt geprüft. Konkret werden sogenannte Erwartungen definiert, die das zu testende Objekt in Form von Methodenaufrufen erfüllen muss. Das eigentliche Mock-Objekt wird zur Laufzeit durch Frameworks wie jMock oder EasyMock generiert, eine eigene Implementierung des Interfaces ist dazu nicht notwendig.

Beispiel:
Eine Service-Klasse besitzt zwei Dao (”Data Access Object”) Objekte, die den Zugriff auf eine Datenbank kapseln. Beide Dao-Klassen wurden bereits mit separaten Unit-Tests abgeprüft, nun möchten wir die Service-Klasse selbst testen (ohne dabei die Dao-Klassen nochmals zu testen). Hier greifen wir auf ein Mock-Framework zurück und testen die Service-Klasse mit Mock-Versionen ihrer Daos. Für jede Methode unserer Service-Klasse beschreiben wir nun, welche Dao-Methoden aufgerufen werden sollten (und was sie in diesem Fall zurückgeben sollen).

Vorteile:

  • Mock-Tests erlauben losgelöstes Testen und bringen den Entwickler dazu, sich intensiver mit der Interaktion seiner Objekte zu beschäftigen.
  • Es werden keine Klassen doppelt getestet.
  • Service-Klassen können ohne Abhängigkeit zu Fremdsystemen getestet werden.

Mögliche Stolperfallen:

  • Ein Mock-Test bindet sich stark an die Implementierung. Wenn es alternative Wege zur Zielerreichung gibt (z.B. überladene Methoden), dann scheitert der Test, obwohl das Resultat des Methodenaufrufs weiterhin richtig wäre.
  • Das Mocken von fremden Klassen führt zu falschen Annahmen, da der Entwickler oftmals das genaue Verhalten eines Frameworks nicht immer zu 100% nachbilden kann.
  • Die Erwartungen (sog. Expectations) der Mock-Tests werden zu komplex (Perl-Syndrom: write once - never understand again.)

Empfohlene Artikel zum Vertiefen: