41

Suppose I want to create and use an H2 database for my integration tests.

Maven has a command to run tests: mvn test.

Is there a way to tell maven to start an H2 database server for the tests and stop it when it's done?

I imagine this working similar to how I can run tomcat via a Maven command (mvn tomcat:run).

Sorry if this question is nonsensical, I'm still wrapping my head around new concepts.

Peter Mularien
  • 2,578
  • 1
  • 25
  • 34
roufamatic
  • 18,187
  • 7
  • 57
  • 86
  • Alternately... I see that H2 can be embedded in code. If there's a way to start it purely from unit tests or fancy spring configuration, that's worth a "correct answer" in my book! – roufamatic Feb 05 '10 at 05:21

9 Answers9

20

I was able to get it to work without using an external server just by adding the dependency to H2 via Maven and then using this bean:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver"/>
    <property name="url" value="jdbc:h2:file:h2\db"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>        
</bean>

Then again, this required that I use a file-based DB instead of in-memory. But it does the trick.

roufamatic
  • 18,187
  • 7
  • 57
  • 86
12

you can create 2 small classes with main methods that start and stop the database. the idea is to run the StartServer class before the integration tests are run and then class StopServer after the tests have run.

you should do the same for your DB server as described somewhere in this document (description is for starting and stopping Jetty in integration tests)

in your pom.xml you should define the maven-exec-plugin to run the exec:java goal and create 2 executions (1 for calling StartServer and 1 for StopServer):

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.1.1</version>
  <executions>
    <execution>
      <!-- start server before integration tests -->
      <id>start</id>
      <phase>pre-integration-test</phase>
      <goals>
        <goal>java</goal>
      </goals>
      <configuration>
        <mainClass>com.foo.StartServer</mainClass>
      </configuration>
     </execution>
     <execution>
      <!-- stop server after integration tests -->
      <id>stop</id>
      <phase>post-integration-test</phase>
      <goals>
        <goal>java</goal>
      </goals>
      <configuration>
        <mainClass>com.foo.StopServer</mainClass>
      </configuration>
     </execution>
   </executions>
 </plugin>

hope that's what you want

Stefan De Boey
  • 2,344
  • 16
  • 14
  • To create those two small classes: https://stackoverflow.com/questions/29183503/start-h2-database-programmatically – Ville Myrskyneva Jul 20 '17 at 06:43
  • Instead of using the execution plugin (in the end, after I did some testing), I just created an abstract class with methods annotated with BeforeClass and AfterClass. Those will do the necessary start/stop procedures. Then extend this class for the test classes that require the database. – Ville Myrskyneva Jul 20 '17 at 07:46
8

This plugin works fine to spawn a new H2 DB with tcp mode before integration tests (the default plugin phase): h2-maven-plugin on github

It is not well documented but you can check the Mojo sources to know the configuration options. It is published on maven central.


Basically, for integration tests, you may want Maven to:

  • Reserve randomly available network ports, for your Tomcat server, and your H2 (to avoid port conflicts)
  • Start the H2 server
  • Start the Tomcat server
  • Run integration tests
  • Stop the Tomcat server
  • Stop the H2 server

This can be achieved with a Maven configuration looking like this. Assuming your integration tests are annoted with a custom interface JUnit Category:

@Category(IntegrationTest.class)

This Maven configuration works fine for me:

<profile>
   <id>it</id>
   <build>
     <plugins>

       <!-- Reserve randomly available network ports -->
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>build-helper-maven-plugin</artifactId>
         <executions>
           <execution>
             <id>reserve-network-port</id>
             <goals>
               <goal>reserve-network-port</goal>
             </goals>
             <phase>process-resources</phase>
             <configuration>
               <portNames>
                 <portName>tomcat.test.http.port</portName>
                 <portName>h2.test.tcp.port</portName>
               </portNames>
             </configuration>
           </execution>
         </executions>
       </plugin>


       <!-- Start H2 before integration tests, accepting tcp connections on the randomly selected port -->
       <plugin>
         <groupId>com.edugility</groupId>
         <artifactId>h2-maven-plugin</artifactId>
         <version>1.0</version>
         <configuration>
           <port>${h2.test.tcp.port}</port>
         </configuration>
         <executions>
             <execution>
               <id>Spawn a new H2 TCP server</id>
               <goals>
                 <goal>spawn</goal>
               </goals>
             </execution>
             <execution>
               <id>Stop a spawned H2 TCP server</id>
               <goals>
                 <goal>stop</goal>
               </goals>
             </execution>
           </executions>
       </plugin>


       <!-- Start Tomcat before integration tests on the -->
       <plugin>
         <groupId>org.apache.tomcat.maven</groupId>
         <artifactId>tomcat7-maven-plugin</artifactId>
         <configuration>
           <systemProperties>
             <spring.profiles.active>integration_tests</spring.profiles.active>
             <httpPort>${http.test.http.port}</httpPort>
             <h2Port>${h2.test.tcp.port}</h2Port>
           </systemProperties>
           <port>${http.test.http.port}</port>
           <contextFile>src/main/java/META-INF/tomcat/webapp-test-context-using-h2.xml</contextFile>
           <fork>true</fork>
         </configuration>
         <executions>
           <execution>
             <id>run-tomcat</id>
             <phase>pre-integration-test</phase>
             <goals>
               <goal>run</goal>
             </goals>
           </execution>
           <execution>
             <id>stop-tomcat</id>
             <phase>post-integration-test</phase>
             <goals>
               <goal>shutdown</goal>
             </goals>
           </execution>
         </executions>
         <dependencies>
           <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>${mysql.version}</version>
           </dependency>
           <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
             <version>${h2.version}</version>
           </dependency>
         </dependencies>
       </plugin>


       <!-- Run the integration tests annotated with @Category(IntegrationTest.class) -->
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-failsafe-plugin</artifactId>
         <!-- Bug in 2.12.x -->
         <version>2.11</version>
         <dependencies>
           <dependency>
             <groupId>org.apache.maven.surefire</groupId>
             <artifactId>surefire-junit47</artifactId>
             <version>2.12.4</version>
           </dependency>
         </dependencies>
         <configuration>
           <groups>com.mycompany.junit.IntegrationTest</groups>
           <failIfNoTests>false</failIfNoTests>
           <junitArtifactName>junit:junit-dep</junitArtifactName>
           <systemPropertyVariables>
             <httpPort>${tomcat.test.http.port}</httpPort>
             <h2Port>${h2.test.tcp.port}</h2Port>
           </systemPropertyVariables>
         </configuration>
         <executions>
           <execution>
             <goals>
               <goal>integration-test</goal>
             </goals>
           </execution>
         </executions>
       </plugin>

     </plugins>
   </build>
 </profile>

You may want to use maven filters on the tomcat context file so that the port is replaced:

   <contextFile>src/main/java/META-INF/tomcat/webapp-test-context-using-h2.xml</contextFile>

With the file content being:

  <Resource name="jdbc/dataSource"
            auth="Container"
            type="javax.sql.DataSource"
            maxActive="100"
            maxIdle="30"
            maxWait="10000"
            username=""
            password=""
            driverClassName="org.h2.Driver"
            url="jdbc:h2:tcp://localhost:${h2.test.tcp.port}/mem:db;DB_CLOSE_ON_EXIT=FALSE;MODE=MySQL"/>

Or if you don't want a JNDI datasource, you can use a Spring declared dataSource, using the the same property...


One extra trip if you want to be able to setup your integration tests tomcat, and run the integration tests from your IDE:

You can use use a property to fork or not the Tomcat server:

<fork>${integrationTestsForkTomcatJvm}</fork>

When you set fork=false, the server will block and maven won't continue, so the integration tests won't be run, but you will be able to run them from your ide.

Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
6

I create a file-based H2 database before unit tests are run. The file lives in the target directory and can be removed at any time using mvn clean.

I use the maven-sql-plugin as follows:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>sql-maven-plugin</artifactId>
  <version>1.5</version>
  <dependencies>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId> 
      <version>1.3.166</version>
    </dependency>
  </dependencies>
  <configuration>
    <driver>org.h2.Driver</driver>
    <url>jdbc:h2:file:target/db/testdb</url>
    <username>sa</username>
    <password></password>
    <autocommit>true</autocommit>
    <skip>${maven.test.skip}</skip>
  </configuration>
  <executions>
    <execution>
      <id>create-db</id>
      <phase>process-test-resources</phase>
      <goals>
        <goal>execute</goal>
      </goals>
      <configuration>
        <srcFiles>
          <srcFile>${sql.dir}/drop_db.sql</srcFile>
          <srcFile>${sql.dir}/tables.sql</srcFile>
          <srcFile>${sql.dir}/constraints.sql</srcFile>
          ... etc ...
        </srcFiles>
      </configuration>
    </execution>
  </executions>
</plugin>

The database can be created by running mvn process-test-resources. When tests are run, make sure you connect to the database in target/db/testdb via hibernate properties.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
      destroy-method="close"
      p:driverClassName="org.h2.Driver"
      p:url="jdbc:h2:file:target/db/testdb"
      p:username="sa"
      p:password="" />

You will also need to a dependency on com.h2database.h2 in maven's dependencies.

John Q Citizen
  • 321
  • 1
  • 6
  • 15
  • i am amazed that this answer didn't have a vote for 2 years when i found it. it doesn't actually answer the question of how to start a database server but solves it in a much better way; don't start the server create the db state on disk then embed the database into the process being tested. that is going to be faster and more reliable (no problem finding a port to run on) and fits the majority of scenarios where tests have a single process using a database. so it is a great solution. thanks! – simbo1905 Nov 18 '15 at 22:01
5

In my project, for unit testing, I asked Spring to handle this database creation and initialization. As stated in the H2 documentation, you can create a bean for that:

<bean id = "org.h2.tools.Server"
    class="org.h2.tools.Server"
    factory-method="createTcpServer"
    init-method="start"
    destroy-method="stop">
    <constructor-arg value="-tcp,-tcpAllowOthers,true,-tcpPort,8043" />
</bean>

You simply need to start the Spring context with this configuration when you start your unit tests.

Romain Linsolas
  • 79,475
  • 49
  • 202
  • 273
5

I've just started project for H2 plugin for maven @ bitbucket. I will appreciate any help with it.

https://bitbucket.org/dohque/maven-h2-plugin

Hope it will be helpful.

Ruslan Pilin
  • 133
  • 2
  • 4
3

following does the job for me (just using h2 dependency and the exec-maven-plugin):

    <build>
        <plugins>
            <!-- start/stop H2 DB as a server -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <id>start-h2</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <mainClass>org.h2.tools.Server</mainClass>
                            <arguments>
                                <argument>-tcp</argument>
                                <argument>-tcpDaemon</argument>
                            </arguments>
                        </configuration>
                    </execution>
                    <execution>
                        <id>stop-h2</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <mainClass>org.h2.tools.Server</mainClass>
                            <arguments>
                                <argument>-tcpShutdown</argument>
                                <argument>tcp://localhost:9092</argument>
                            </arguments>
                        </configuration>
                    </execution>
                </executions>
                <configuration>
                    <includeProjectDependencies>true</includeProjectDependencies>
                    <includePluginDependencies>true</includePluginDependencies>
                    <executableDependency>
                        <groupId>com.h2database</groupId>
                        <artifactId>h2</artifactId>
                    </executableDependency>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.h2database</groupId>
                        <artifactId>h2</artifactId>
                        <version>1.3.173</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
</build>

please note, in my pom.xml the com.h2database:h2 was not project dependency. In case you'd have it you might don't need to explicitly name it as a plugin dependency.

Peter Butkovic
  • 11,143
  • 10
  • 57
  • 81
3

If you want to make it in memory, then just use a different URL:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver"/>
    <property name="url" value="jdbc:h2:mem:db"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>        
</bean>

You can give additional options, such as: ;DB_CLOSE_DELAY=-1

see: http://www.h2database.com/html/features.html#in_memory_databases

fuemf5
  • 301
  • 3
  • 4
1

Since H2 doesn't provide Maven plugin you should start it using maven-antrun-plugin. Write code for start and stop h2 engine in ant task and call it when your integration test starts and stop.

See details on http://docs.codehaus.org/display/MAVENUSER/Maven+and+Integration+Testing

uthark
  • 5,333
  • 2
  • 43
  • 59