How to: Run maven integration tests against a test environment (database)
Asked Answered
I

4

13

I'm using maven and the maven-failsafe-plugin to start up jetty during the integration-test lifecycle phase. I then execute a number of (*IT.java) junit tests against my running webapp. This is working as expected.

However, I would like to connect to a test database for my integration tests. I am storing its url in

${basedir}/src/test/resources/jdbc.properties  

When the jetty plugin runs (jetty:run), it uses

${basedir}/src/main/resources/jdbc.propertes 

instead. I tried reconfiguring the jetty plugin via the classesDirectory property to use

${project.build.testOutputDirectory}

but the test-classes directory is missing my actual compiled project classes, as well as the resources stored in

${basedir}/src/main/resources 

note: surefire adds the test resources to the classpath, followed by the main resources, such that anything found in both will use the test version because it is found first in the classpath.

Any ideas on how to get this set up correctly?

Thanks!

EDIT:

Well, it seems there are configuration properties on the jetty-plugin to deal with this:

  • testClassesDirectory : The directory containing generated test classes.
  • useTestClasspath : If true, the and the dependencies of test will be put first on the runtime classpath.

Unfortunately, they don't work.

Here is the relevant portion of my pom.xml:

  <testResources>
        <testResource>
            <filtering>true</filtering>
            <directory>src/test/resources</directory>
        </testResource>
    </testResources>
    <plugins>
        <plugin>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>maven-jetty-plugin</artifactId>
            <version>6.1.26</version>
            <configuration>
                <contextPath>/</contextPath>
                <stopPort>8005</stopPort>
                <stopKey>STOP</stopKey>
            </configuration>
            <executions>
                <execution>
                    <id>start-jetty</id>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>run</goal>
                    </goals>
                    <configuration>
                        <daemon>true</daemon>
                        <useTestClasspath>true</useTestClasspath>
                        <testClassesDirectory>${project.build.testOutputDirectory}</testClassesDirectory>
                    </configuration>
                </execution>
                <execution>
                    <id>stop-jetty</id>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>stop</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.6</version>
            <executions>
                <execution>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <useFile>false</useFile>
            </configuration>
        </plugin>
Inspirit answered 18/2, 2011 at 16:6 Comment(2)
Can you add the relevant section of your POM?Brana
Thanks, this helped me out a lot. One thing to note was that I was attempting to use the run-exploded goal (per failsafe documentation), which does not support the useTestClassPath or testClassesDirectory.Absentee
C
2

I have about the same problem, and solved it by using a custom web.xml (jettyweb.xml) see the maven configuarion

    <build>
    <plugins>

        <plugin>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>maven-jetty-plugin</artifactId>
            <configuration>
                <overrideWebXml>./src/main/webapp/WEB-INF/jettyweb.xml</overrideWebXml>
                <scanintervalseconds>3</scanintervalseconds>
            </configuration>
            <dependencies>

            </dependencies>
        </plugin>
    </plugins>

</build>

In my case I use this configuration to use some other spring configuration to manage transactions. But this strategy could also be used to use other property files.

My original web.xml has this spring configuration

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    /WEB-INF/spring-hibernate.xml,
    /WEB-INF/spring-services.xml
    </param-value>
</context-param>

My jettyweb.xml has this spring configuration

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    /WEB-INF/spring-hibernate-jetty.xml,
    /WEB-INF/spring-services.xml
    </param-value>
</context-param>

This should put you on the correct path

Cladoceran answered 23/2, 2011 at 11:58 Comment(0)
E
1

I used

<configuration>
  <jettyEnvXml>src/test/webapp/WEB-INF/jetty-env.xml</jettyEnvXml>
  .
  .

with contents

<?xml version="1.0"?>
    <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
    <New id="MyDb" class="org.mortbay.jetty.plus.naming.Resource">
        <Arg>jdbc/myDS</Arg>
        <Arg>
             <New class="org.apache.commons.dbcp.BasicDataSource">
                <Set name="driverClassName">org.h2.Driver</Set>
                <Set name="url">jdbc:h2:mem:testdb;INIT=CREATE SCHEMA IF NOT EXISTS TESTDB\;SET SCHEMA TESTDB</Set>
                <Set name="username">sa</Set>
                <Set name="password"></Set>
            </New>
         </Arg>
    </New>
</Configure>

I also needed in my web.xml

<resource-ref>
    <res-ref-name>jdbc/myDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

I used spring config

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/myDS" />
</bean>
Ekaterina answered 21/8, 2012 at 13:29 Comment(0)
A
1

I tried both the authors as well as @thehpi suggestion. Both are credible, but had some issues when I tried them. My setup was the following, maven project, using integration tests with maven-failsafe-plugin, using JPA with a persistence.xml file used to tell the container how to connect to the database.

Long story short, what is detailed below is how to use a JVM property to tell your real code to simply use a different persistence-unit. Other solutions gave me trouble using say Hibernate 4.1 with Jetty 7 (issues finding the datasource).

The only drawback is that you'll end up with a useless configuration in the released version of your project. The secondary persistence unit will never be used outside of the maven integration test. I'm sure there's a way to split it out, but for me, I'm ok with this approach. I've even pulled hsqldb.jar out of the build and made it a dependency only for the plugin execution.

Relevent from POM

<plugin>
    <inherited>true</inherited>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.8.1</version>
</plugin>
<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>7.2.0.v20101020</version>
    <dependencies>
        <dependency>
            <groupId>hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>1.8.0.10</version>
        </dependency>
    </dependencies>
    <configuration>
        <scanIntervalSeconds>10</scanIntervalSeconds>
        <stopPort>8005</stopPort>
        <stopKey>STOP</stopKey>
        <contextPath>/</contextPath>
    </configuration>

    <executions>
        <execution>
            <id>start-jetty</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>run</goal>
            </goals>
            <configuration>
                <scanIntervalSeconds>0</scanIntervalSeconds>
                <daemon>true</daemon>
                <systemProperties>
                    <systemProperty>
                        <name>RUNNING_TESTS</name>
                        <value>true</value>
                    </systemProperty>
                </systemProperties>
            </configuration>
        </execution>
        <execution>
            <id>stop-jetty</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>stop</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Note the System property called "RUNNING_TESTS".

HibernateUtil.java

public class HibernateUtil {
    private static final EntityManagerFactory emfInstance;
    static {
        String istest = System.getProperty("RUNNING_TESTS");
        System.out.println("RUNNING_TESTS: " + istest);
        if (istest != null && istest.equalsIgnoreCase("true")) {
            emfInstance = Persistence.createEntityManagerFactory("integration-tests");
        }
        else {

            emfInstance = Persistence.createEntityManagerFactory("productionname");
        }
    }

    public static EntityManagerFactory getInstance() {
        return emfInstance;
    }

    private HibernateUtil() {
    }
}

persistence.xml (in /src/main/resources/META-INF)

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">

    <!-- persistence.xml -->
    <persistence-unit name="productionname">

        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <non-jta-data-source>java:comp/env/jdbc/selectivemailpush</non-jta-data-source>
        <properties>
            <property name="hibernate.archive.autodetection" value="class, hbm" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
        </properties>
    </persistence-unit>

    <persistence-unit name="integration-tests">

        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>...</class>
        <class>...</class>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
            <property name="hibernate.connection.username" value="sa" />
            <property name="hibernate.connection.password" value="" />
            <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:integration-tests" />
            <property name="hibernate.showSql" value="true" />
            <property name="hibernate.format_sql" value="true" />
        </properties>

    </persistence-unit>

</persistence>
Absentee answered 8/11, 2012 at 4:17 Comment(0)
S
0

I'm struggling with the same problem, but I think the reason that useTestClasspath doesn't seem to work lies actually in Spring.

You probably have a configuration like the following:

<context:property-placeholder location="classpath*:**/*.properties"/>

If you remove the first * I think it works because with this config it loads the same property file from different locations (both main and test if it exists in both). Removing the * will only load the test one. So change it to

<context:property-placeholder location="classpath:**/*.properties"/>
Stamm answered 20/5, 2011 at 22:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.