2015-07-21 30 views
5

Mamy projekt Java używający Spring i MAVEN. W tym projekcie używamy bazy danych H2 w pamięci, aby wykonać kilka testów na naszej warstwie DAO/Repository.JUnit test zaczyna się, zanim zakończy się RUNSCRIPT H2S

Po kilku testach prowadzonych, ale nie zawsze, mamy następujący błąd:

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

Jeśli wykonać sam test JUnit, to nigdy nie nie. Nie ma wzoru włączonego po pojawieniu się błędu.

Podejrzewam, że poniższe polecenie RUNSCRIPT na połączeniu URL nie zostało zakończone i po rozpoczęciu testu urządzenia, tj. Wykonanie jest wykonywane asynchronicznie.

Oto komunikat gra:

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'" 

Chodzi o to, że baza danych zostanie zresetowany przy każdym teście.

Oto fragment kodu, aby uzyskać obiekt DataSource:

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; 
} 

A oto fragment dla weatherapi.sql skryptu:

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

Może chcesz umieścić [mcve] (http: // stackoverflow.com/help/mcve) –

+1

Dołożę więcej starań, aby odtworzyć problem w piaskownicy na przyszłość :-) – ajkret

Odpowiedz

3

Podejrzewam, że jest to sytuacja wyścigu. Zgodnie z documentation skrypt jest wykonywany dla każdego klienta, który łączy się z bazą danych. Ponieważ zawsze usuwasz tabelę Weather przed jej odtworzeniem, może się zdarzyć, że gdy test A zostanie już uruchomiony, a drugi klient B połączy się z bazą danych, tabela zostanie upuszczona tuż pod nosem A. B może być innym testem, który działa równolegle lub drugi wątek w tym samym teście.

Jeśli tak jest, można spróbować użyć narzędzia RunScript w swojej metodzie manualCreateDataSource() zamiast parametru INIT w połączeniu URL JDBC:

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); 

Dodatkowo trzeba dokonać getDs() thread-safe , dodając do niego synchronized lub jeszcze lepiej, przez inicjowanie instancji zmiennej ds statycznie:

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

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

odpowiedź od hzpz w rzeczywistości pomóż mi zobaczyć, co się dzieje: warunki wyścigu. Wpisane przeze mnie pytanie nie określało, że używam maven (przepraszam za to), i dowiedziałem się, że dodatek maven surefire był rozwidleniem testów, więc stan wyścigu faktycznie się pojawił. Postanawiam wyłączyć rozwidlone i skonfigurowane wtyczki maven-murowany w ten sposób:

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

Istnieje kilka pytań dotyczących rozwidlone, ale żadna odnosząca H2 za runscript warunków wyścigowych.

Tutaj więcej szczegółów na temat wtyczki murowany:

Maven Surefire Plugin - Class loading and forking