views:

5196

answers:

9

Is there a way to set up a second persistence.xml file in a Maven project such that it is used for testing instead of the normal one that is used for deployment?

I tried putting a persistence.xml into src/test/resources/META-INF, which gets copied into target/test-classes/META-INF, but it seems target/classes/META-INF (the copy from the src/main/resources) gets preferred, despite mvn -X test listing the classpath entries in the right order:

[DEBUG] Test Classpath :
[DEBUG]   /home/uqpbecke/dev/NetBeansProjects/UserManager/target/test-classes
[DEBUG]   /home/uqpbecke/dev/NetBeansProjects/UserManager/target/classes
[DEBUG]   /home/uqpbecke/.m2/repository/junit/junit/4.5/junit-4.5.jar
...

I would like to be able to run tests against a simple hsqldb configuration without having to change the deployment version of the JPA configuration, ideally straight after project checkout without any need for local tweaking.

+3  A: 

Keep two copies of persistence.xml file. One for testing and another for normal build.

The default life cycle copy the build persistence.xml to src/test/resources/META-INF

Create a separate profile which when run will copy the testing persistence.xml to src/test/resources/META-INF

TRF
+5  A: 

It seems multiple persistence.xml files is a general problem with JPA, solved only by classloading tricks.

A workaround that works for me is to define multiple persistence units in one persistence.xml file and then make sure that your deployment and test code use a different binding (in Spring you can set the "persistenceUnitName" property on the entity manager factory). It pollutes your deployment file with the test configuration, but if you don't mind that it works ok.

Peter Becker
A: 

Unfortunately I cannot use only one persistence.xml file cuz I define tag to load some entities from another jar file in JEE env (WEB-INF/lib/framework.jar)

So I am using different persistence.xml but for some reason, surefire picks up the one in src\main\resources\META-INF\persistence.xml and complains about the path (WEB-INF/lib/framework.jar) mentioned in persistence.xml

any suggestions how to avoid this ?

+1  A: 

Persistence.xml is used as a starting point to search for entity classes unless you list all classes explicitly and add . So if you want to override this file with another one, say from src/test/resources, you have to specify every single entity class in this second persistence.xml otherwise no entity class would be found.

Another solution would be to overwrite the file using the maven-resources-plugin ('copy-resources' goal). But then you have to overwrite it twice, once for testing (e.g. phase process-test-classes) and once for the real packaging (phase 'prepare-package').

+1  A: 

I'm trying to do the same thing. I have a solution that works for me - yours may vary (and you might not love the solution... it's a bit low-level).

I came across an article on the net where they were using a custom class loader to do something similar which served as inspiration. If anyone can see how to improve then suggestions would be welcome btw. For deployment I rely on container injection of the EntityManager but for testing I create it myself using this code:

final Thread currentThread = Thread.currentThread();
final ClassLoader saveClassLoader = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(new ClassLoaderProxy(saveClassLoader));

EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("test");
em = emFactory.createEntityManager();

Then the ClassLoaderProxy is about as minimal as you can get and just redirects requests for META-INF/persistence.xml to META-INF/test-persist.xml:

public class ClassLoaderProxy extends ClassLoader {

 public ClassLoaderProxy(final ClassLoader parent) {
  super();
 }

 @Override
 public Enumeration<URL> getResources(final String name) throws IOException {
  if (!"META-INF/persistence.xml".equals(name)) {
   return super.getResources(name);
  } else {
   System.out.println("Redirecting persistence.xml to test-persist.xml");
   return super.getResources("META-INF/test-persist.xml");
  }
 }
}

Just to explain this a bit more:

  1. There are two persistence.xml files (one named persistence.xml that is used outside testing and one named test-persist.xml that is used for tests).
  2. The custom class loader is only active for unit tests (for deployment everything is normal)
  3. The custom class loader redirects requests for "META-INF/persistence.xml" to the test version ("META-INF/test-persist.xml").

I was originally hitting some problems because Hibernate will revert back (somehow) to the classloader that was used to load Hibernate (at least I think that is what was going on). I've found that putting the ClassLoader switching code (the first block) as a static block in your Test case it will get loaded before Hibernate but that, depending on your unit test structure you may also need to put the same code in other places (yuck).

macbutch
+4  A: 

The following will work for Maven 2.1+ (prior to that there wasn't a phase between test and package that you could bind an execution to).

You can use the maven-antrun-plugin to replace the persistence.xml with the test version for the duration of the tests, then restore the proper version before the project is packaged.

This example assumes the production version is src/main/resources/META-INF/persistence.xml and the test version is src/test/resources/META-INF/persistence.xml, so they will be copied to target/classes/META-INF and target/test-classes/META-INF respectively.

It would be more elegant to encapsulate this into a mojo, but as you're only copying a file, it seems like overkill.

<plugin>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.3</version>
  <executions>
    <execution>
      <id>copy-test-persistence</id>
      <phase>process-test-resources</phase>
      <configuration>
        <tasks>
          <!--backup the "proper" persistence.xml-->
          <copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
          <!--replace the "proper" persistence.xml with the "test" version-->
          <copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
    <execution>
      <id>restore-persistence</id>
      <phase>prepare-package</phase>
      <configuration>
        <tasks>
          <!--restore the "proper" persistence.xml-->
          <copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
  </executions>
</plugin>
Rich Seller
This is an awesome solution IMHO! Thanks :)
javamonkey79
Indeed, it is a neat solution. However, one might also want to add the overwite="true" attribute to the last Ant task, to ensure that the proper XML file is copied back. In my environment, it seems to fail due to timestamps being the same for the destination and the target.
Vineet Reynolds
A: 

put tests in own maven project with its persistence.xml

balmaster
A: 

I prefer the solution of using different persistence.xml for testing and production as Rich Seller post (thanks!!).

But need to change:

<copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>

for:

<move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>

In order persistence.xml.proper not embedded in .jar file

d.marzo
A: 

When using Spring, the following works in the application context used for testing:

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <!--
        JPA requires META-INF/persistence.xml, but somehow prefers the one
        in classes/META-INF over the one in test-classes/META-INF. Spring
        to the rescue, as it allows for setting things differently, like by
        referring to "classpath:persistence-TEST.xml". Or, simply referring
        to "META-INF/persistence.xml" makes JPA use the test version too: 
    -->
    <property name="persistenceXmlLocation" value="META-INF/persistence.xml" />
    <property name="persistenceUnitName" value="myPersistenceUnit" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        </bean>
    </property>
</bean>
Arjan