70 votes

Comment configurer JPA pour les tests dans Maven ?

Existe-t-il un moyen de configurer un deuxième fichier persistence.xml dans un projet Maven de manière à ce qu'il soit utilisé pour les tests au lieu du fichier normal qui est utilisé pour le déploiement ?

J'ai essayé de placer un fichier persistence.xml dans src/test/resources/META-INF, qui est copié dans target/test-classes/META-INF, mais il semble que target/classes/META-INF (la copie de src/main/resources) soit préféré, bien que mvn -X test en listant les entrées du classpath dans le bon ordre :

[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
...

J'aimerais pouvoir exécuter des tests avec une configuration hsqldb simple sans avoir à changer la version de déploiement de la configuration JPA, idéalement directement après le checkout du projet sans avoir besoin de faire des ajustements locaux.

26voto

Rich Seller Points 46052

Ce qui suit fonctionne pour Maven 2.1+ (avant cela, il n'y avait pas de phase entre le test et le paquet à laquelle vous pouviez lier une exécution).

Vous pouvez utiliser le plugin maven-antrun pour remplacer le fichier persistence.xml par la version de test pendant la durée des tests, puis rétablir la version correcte avant l'empaquetage du projet.

Cet exemple suppose que la version de production est src/main/resources/META-INF/persistence.xml et que la version de test est src/test/resources/META-INF/persistence.xml, elles seront donc copiées respectivement dans target/classes/META-INF et target/test-classes/META-INF.

Il serait plus élégant d'encapsuler cela dans un mojo, mais comme vous ne copiez qu'un fichier, cela semble superflu.

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

21voto

Arjan Points 7154

Dans un projet EE6/CDI/JPA, un test src/test/resources/META-INF/persistence.xml est récupéré sans autre configuration.

Lorsque vous utilisez JPA dans Spring, ce qui suit fonctionne dans le contexte de l'application utilisée pour les tests :

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

    <!-- As defined in /src/test/resources/META-INF/persistence.xml -->
    <property name="persistenceUnitName" value="myTestPersistenceUnit" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        </bean>
    </property>
</bean>

Ici, /src/test/resources/META-INF/persistence.xml (copié dans target/test-classes ) serait préféré à /src/main/resources/META-INF/persistence.xml (copié dans target/classes ).

Malheureusement, l'emplacement de la persistence.xml détermine également les " Racine de l'unité de persistance "qui détermine ensuite quelles classes sont analysées pour @Entity annotations. Ainsi, en utilisant /src/test/resources/META-INF/persistence.xml balayerait les classes dans target/test-classes et non les classes de target/classes (où se trouvent les classes qui doivent être testées).

Par conséquent, pour les tests, il faut ajouter explicitement l'élément suivant <class> entrées à persistence.xml pour éviter java.lang.IllegalArgumentException: Not an entity: class ... . La nécessité de <class> peuvent être évitées en utilisant un nom de fichier différent, comme par exemple persistence-TEST.xml et placez ce fichier dans le même dossier que celui de la version normale de l'application. persistence.xml fichier. Le contexte Spring de votre dossier de test peut alors simplement faire référence à <property name="persistenceXmlLocation" value="META-INF/persistence-TEST.xml" /> et Spring le trouvera pour vous dans src/main .

En guise d'alternative, on pourrait garder persistence.xml la même pour l'application réelle et les tests, et n'en définir qu'une seule en src/main . La plupart des configurations telles que les pilotes, le dialecte et les informations d'identification facultatives peuvent être effectuées dans le contexte Spring. De même, les paramètres tels que hibernate.hbm2ddl.auto peut être transmis dans le contexte :

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!-- For example: com.mysql.jdbc.Driver or org.h2.Driver -->
    <property name="driverClassName" value="#{myConfig['db.driver']}" />
    <!-- For example: jdbc:mysql://localhost:3306/myDbName or 
        jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 -->
    <property name="url" value="#{myConfig['db.url']}" />
    <!-- Ignored for H2 -->
    <property name="username" value="#{myConfig['db.username']}" />
    <property name="password" value="#{myConfig['db.password']}" />
</bean>

<bean id="jpaAdaptor"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <!-- For example: org.hibernate.dialect.MySQL5Dialect or 
        org.hibernate.dialect.H2Dialect -->
    <property name="databasePlatform" value="#{myConfig['db.dialect']}" />
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="jpaAdapter" />
    <property name="jpaProperties">
        <props>
            <!-- For example: validate, update, create or create-drop -->
            <prop key="hibernate.hbm2ddl.auto">#{myConfig['db.ddl']}</prop>
            <prop key="hibernate.show_sql">#{myConfig['db.showSql']}</prop>
            <prop key="hibernate.format_sql">true</prop>
        </props>
    </property>
</bean>

13voto

Peter Becker Points 2962

Il semble que la multiplicité des fichiers persistence.xml soit un problème général avec JPA, résolu uniquement par des astuces de classloading.

Une solution de contournement qui fonctionne pour moi est de définir plusieurs unités de persistance dans un fichier persistence.xml et de s'assurer ensuite que votre code de déploiement et de test utilise une liaison différente (dans Spring, vous pouvez définir la propriété "persistenceUnitName" sur la fabrique du gestionnaire d'entités). Cela pollue votre fichier de déploiement avec la configuration de test, mais si cela ne vous dérange pas, cela fonctionne bien.

6voto

luzzy Points 51

J'ai essayé l'approche ClassLoaderProxy mais j'ai rencontré le problème suivant : les classes annotées JPA ne sont pas traitées comme des classes persistantes par Hibernate.

J'ai donc décidé de l'essayer sans utiliser persistence.xml. L'avantage est que le build maven et le test JUnit Eclipse fonctionneront sans modifications.

J'ai une classe de support persistant pour les tests JUnit.

public class PersistenceTestSupport {

    protected EntityManager em;
    protected EntityTransaction et;

    /**
     * Setup the the {@code EntityManager} and {@code EntityTransaction} for
     * local junit testing.
     */
    public void setup() {

        Properties props = new Properties();
        props.put("hibernate.hbm2ddl.auto", "create-drop");
        props.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        props.put("hibernate.connection.url", "jdbc:mysql://localhost/db_name");
        props.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
        props.put("hibernate.connection.username", "user");
        props.put("hibernate.connection.password", "****");

        Ejb3Configuration cfg = new Ejb3Configuration();
        em = cfg.addProperties(props)
            .addAnnotatedClass(Class1.class)
            .addAnnotatedClass(Class2.class)
            ...
                    .addAnnotatedClass(Classn.class)
            .buildEntityManagerFactory()
            .createEntityManager();

        et = em.getTransaction();
    }
}

Mes classes de test étendent simplement PersistenceTestSupport et appellent le setup() dans TestCase.setup().

Le seul inconvénient est de devoir maintenir les classes persistantes à jour, mais pour les tests JUnit, c'est acceptable pour moi.

6voto

d.marzo Points 31

Je préfère la solution consistant à utiliser des persistence.xml différents pour les tests et la production comme Rich Seller. poste (merci !!).

Mais il faut changer :

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

pour :

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

Pour que persistence.xml.proper ne soit pas intégré dans le fichier .jar.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X