DatabasePopulator: Unit-Tests mit einer initialisierten Datenbank

Ausgangslage

In einer Applikation möchte man Unit-Tests durchführen, die direkt auf eine Test-Datenbank zugreifen sollen. Diese Datenbank soll bereits mit Testdaten gefüllt sein, um immer die gleiche Ausgangslage für die Tests sicherzustellen.

DatabasePopulator

Das Interface org.springframework.jdbc.datasource.init.DatabasePopulator wurde für genau diese Aufgabe zur Verfügung gestellt. Nach mehreren Versuchen hat sich die folgende Vorgehensweise als die Geeignetste erwiesen:

  1. In der Testklasse wird eine statische Unterklasse (z.B. TestConfig) mit folgendem Inhalt erstellt:
    @Configuration
    @Profile(Profiles.UNIT_TEST)
    public static class TestConfig {
      @Bean
      public DatabasePopulator databasePopulator(final DataSource dataSource) {
        return DatabaseTestConfig.createDatabasePopulator(dataSource, "schema", "interessent");
      }
    }

    Durch @Configuration wird die Bean aus der Klasse geladen. Wichtig: das @Profile(Profiles.UNIT_TEST) stellt sicher, dass diese Konfiguration nur im Test verwendet wird. Mit der Hilfsmethode DatabaseTestConfig.createDatabasePopulator wird anhand der DataSource und beliebiger SQL-Skripte wird der Initialzustand der Datenbank erzeugt.

  2. Die Hilfsmethode sieht wie folgt aus:
    @SafeVarargs
    public static DatabasePopulator createDatabasePopulator(final DataSource dataSource, final String... scripts) {
      ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
      for (String script : scripts) {
        final String scriptPath = script.startsWith("/db/") ? script : "/db/" + script;
        final String scriptPathWithExtension = scriptPath.endsWith(".sql") ? scriptPath : scriptPath + ".sql";
        populator.addScript(new ClassPathResource(scriptPathWithExtension));
      }
      try {
        populator.populate(dataSource.getConnection());
      } catch (SQLException ignored) {
        LOG.error("Skript konnte nicht ausgeführt werden");
      }
      return populator;
    }

    Auf diese Weise kann sowohl der Standardpfad (/db) als auch die Dateiendung (sql) bei den Skriptnamen entfallen.

  3. Die eigentliche Testklasse muss mit folgenden Einstellungen annotiert werden:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ActiveProfiles(Profiles.UNIT_TEST)
    @SpringApplicationConfiguration(classes = { Application.class, InteressentenRestServiceTest.TestConfig.class, JpaH2EmbeddedConfig.class })
    @Transactional

    Wichtig ist die Annotation @SpringApplicationConfiguration mit der die zu ladenden Konfigurationen definiert werden. Der Parameter InteressentenRestServiceTest.TestConfig.class verweist dann auf die oben beschriebene innere Klasse. Mit der Annotation @Transactional (gemeint ist org.springframework.transaction.annotation.Transactiona) wird sichergestellt, dass nach Durchführung eines Testfalls ein Rollback ausgeführt wird.

Vorteile

Durch dieses Vorgehen wird erreicht, dass die Datenbank vor der Durchführung von allen Testfällen der Testklasse einmal initialisiert wird. Würde der DatabasePopulator in einer @Before-Methode aufgerufen werden, würde er vor jedem Testfall erneut ausgeführt werden und dadurch die Laufzeit enorm verschlechtern.

Durch den Rollback wird vermieden, dass echte Änderungen an der Datenbank vorgenommen werden, so dass die Testfälle weiterhin unabhängig voneinander ausgeführt werden.

Andere Versuche, die zu ladenden Skripte über Konstruktoren von Hilfsklassen zu laden sind gescheitert. Der zusätzliche Aufwand für die hier beschriebene Vorgehensweise sind die 7 zusätzlichen Zeilen in jeder Testklasse, die auf eine Datenbank zugreifen soll.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.