2015-07-21 6 views
5

Wir haben ein Java-Projekt mit Spring und MAVEN. In diesem Projekt verwenden wir die H2-Datenbank im Speicher, um mehrere Tests auf unserer DAO/Repository-Schicht durchzuführen.JUnit Test beginnt vor H2 RUNSCRIPT beendet

Nach ein paar Tests durchführen, aber nicht immer, haben wir den folgenden Fehler:

org.h2.jdbc.JdbcSQLException: Table "WEATHER" not found; SQL statement: 

Wenn Sie die JUnit-Test allein durchführen, es wird nie versagen. Es gibt kein Muster, wenn der Fehler angezeigt wird.

Ich vermute, dass die RUNSCRIPT-Anweisung unten auf der URL-Verbindung nicht abgeschlossen wurde und wenn der Komponententest beginnt, d. H. Die Ausführung wird asynchron durchgeführt. Hier

ist die Verbindung Statement:

String jdbcUrl = "jdbc:h2:mem:WeatherAPI;MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;TRACE_LEVEL_SYSTEM_OUT=1;INIT=runscript from 'src/test/resources/sql/weatherapi.sql'" 

Die Idee ist, dass die Datenbank bei jedem Test zurückgesetzt werden. Hier

ist der Ausschnitt aus dem Code ein Datasource-Objekt zu erhalten:

private static java.sql.DataSource ds = null; 

public static DataSource getDs() { 
    if(this.ds==null) { 
     try { 
      this.ds = manualCreateDataSource(); 
     } catch (Exception e) { 
      logger.error("Could not initialize Datasource", e); 
      throw new RuntimeException("Could not initialize Datasource"); 
     } 
    } 

    return this.ds; 
} 

public static DataSource manualCreateDataSource() { 

    String driverClass = "org.h2.jdbcx.JdbcDataSource"; 
    String jdbcUrl = "jdbc:h2:mem:WeatherAPI;MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;TRACE_LEVEL_SYSTEM_OUT=1;INIT=runscript from 'src/test/resources/sql/weatherapi.sql'"; 
    int maxPoolSize = 20; 
    int minPoolSize = 5; 
    int unreturnedConnectionTimeout = 10; 
    int idleConnectionTestPeriod = 200; 
    int maxIdleTime = 1000; 
    int maxStatementsPerConnection = 5; 

    ComboPooledDataSource ds = new ComboPooledDataSource(); 
    ds.setJdbcUrl(jdbcUrl); 
    ds.setMaxPoolSize(maxPoolSize); 
    ds.setMinPoolSize(minPoolSize); 
    ds.setInitialPoolSize(minPoolSize); 
    ds.setUnreturnedConnectionTimeout(unreturnedConnectionTimeout); 
    ds.setIdleConnectionTestPeriod(idleConnectionTestPeriod); 
    ds.setMaxIdleTime(maxIdleTime); 
    ds.setMaxStatementsPerConnection(maxStatementsPerConnection); 

    try { 
     ds.setDriverClass(driverClass); 
    } catch (PropertyVetoException e) { 
     logger.error("error setting driver class", e); 
    } 

    return ds; 
} 

Und hier ein Ausschnitt für das weatherapi.sql Skript:

CREATE SCHEMA IF NOT EXISTS `WeatherAPI`; 
USE `WeatherAPI`; 

DROP TABLE IF EXISTS `Weather`; 
CREATE TABLE IF NOT EXISTS `Weather` (
    id int(11) NOT NULL AUTO_INCREMENT, 
    location char(3) NOT NULL, 
    period varchar(8) NOT NULL, 
    duration char DEFAULT NULL, 
    payload TEXT, 
    created timestamp NULL DEFAULT NULL, 
    lastmodified timestamp NULL DEFAULT NULL, 
    version int(11) NOT NULL, PRIMARY KEY (id) 
); 
+0

Sie könnten einen [MCVE] (http stellen: // Stackoverflow.com/help/mcve) –

+1

Ich werde mich bemühen, das Problem auf einer Sandbox in Zukunft zu reproduzieren :-) – ajkret

Antwort

3

Ich vermute, das eine Race-Bedingung ist. Gemäß der documentation wird das Skript für jeden einzelnen Client ausgeführt, der eine Verbindung zur Datenbank herstellt. Da Sie immer die Tabelle Weather fallen lassen, bevor Sie sie neu erstellen, kann es passieren, dass wenn ein Test A bereits ausgeführt wird und ein zweiter Client B mit der Datenbank verbindet, die Tabelle direkt unter der Nase von A fällt. B ist möglicherweise ein anderer Test, der parallel oder ein zweiter Thread im selben Test ausgeführt wird.

Wenn das der Fall ist, können Sie versuchen, das RunScript Werkzeug in Ihrem manualCreateDataSource() Methode anstelle der INIT Parameter in der JDBC-Verbindungs-URL zu verwenden:

String jdbcUrl = "jdbc:h2:mem:WeatherAPI;MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;TRACE_LEVEL_SYSTEM_OUT=1;" 
RunScript.execute(jdbcUrl, sa, "", "src/test/resources/sql/weatherapi.sql", null, false); 

Zusätzlich müssen Sie getDs() Thread-sicher machen durch noch synchronized ihn oder besser das Hinzufügen, durch den Instanz-Variable initialisiert ds statisch:

private static java.sql.DataSource ds = manualCreateDataSource(); 

public static DataSource getDs() { 
    return ds; 
} 
1

die Antwort von hzpz in der Tat helfen Sie mir zu sehen, was passiert ist: Rennen Bedingungen. Die Frage, die ich eingab, spezifizierte nicht, dass ich maven verwende (ich entschuldige mich dafür), und ich fand heraus, dass maven todsichere plugin die Tests abzweigte, so erschien die Rassebedingung tatsächlich. Ich entscheide mich auszuschalten gabeln und konfiguriert Maven-Plugin todsichere auf diese Weise:

<plugin> 
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-surefire-plugin</artifactId> 
    <configuration> 
     <forkCount>1</forkCount> 
     <reuseForks>false</reuseForks> 
    </configuration> 
</plugin> 

Es gibt die Forking mehrere Fragen in Bezug auf, aber keiner im Zusammenhang H2 des RUNSCRIPT mit den Rennbedingungen.

Hier weitere Details zu dem todsicheren Plugin:

Maven Surefire Plugin - Class loading and forking