Come configurare JPA per i test in Maven

Esiste un modo per impostare un secondo file persistence.xml in un progetto Maven in modo tale che venga utilizzato per il test anziché per quello normale utilizzato per la distribuzione?

Ho provato a inserire un persistence.xml in src / test / resources / META-INF, che viene copiato in target / test-classs / META-INF, ma sembra target / classs / META-INF (la copia da src / main / risorse) viene preferito, nonostante il mvn -X test elenca le voci del classpath nell’ordine corretto:

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

Mi piacerebbe essere in grado di eseguire test su una semplice configurazione di hsqldb senza dover modificare la versione di implementazione della configurazione JPA, idealmente subito dopo il checkout del progetto senza necessità di tweaking locale.

Quanto segue funzionerà con Maven 2.1+ (prima non c’era una fase tra test e pacchetto a cui si potesse associare un’esecuzione).

È ansible utilizzare il plugin maven-antrun per sostituire il persistence.xml con la versione di prova per la durata dei test, quindi ripristinare la versione corretta prima che il progetto sia impacchettato.

Questo esempio presuppone che la versione di produzione sia src / main / resources / META-INF / persistence.xml e la versione di test sia src / test / resources / META-INF / persistence.xml, quindi verranno copiati in target / classs / META -INF e target / test-classs / META-INF rispettivamente.

Sarebbe più elegante incapsulare questo in un mojo, ma siccome copi solo un file, sembra eccessivo.

  maven-antrun-plugin 1.3   copy-test-persistence process-test-resources          run    restore-persistence prepare-package        run     

In un progetto EE6 / CDI / JPA, un test src/test/resources/META-INF/persistence.xml viene prelevato correttamente senza ulteriori configurazioni.

Quando si utilizza JPA in Spring, il seguente funziona nel contesto dell’applicazione utilizzato per il test:

            

Qui, /src/test/resources/META-INF/persistence.xml (copiato in target/test-classs ) sarebbe preferito su /src/main/resources/META-INF/persistence.xml (copiato in target/classs ) .

Sfortunatamente, la posizione del file persistence.xml determina anche la cosiddetta ” root della persistenza “, che determina quali classi vengono scansionate per @Entity annotazioni @Entity . Quindi, usando /src/test/resources/META-INF/persistence.xml si analizzano le classi in target/test-classs , non le classi in target/classs (dove le classi che devono essere testate potrebbero vivere).

Quindi, per il test, è necessario aggiungere esplicitamente voci a persistence.xml , per evitare java.lang.IllegalArgumentException: Not an entity: class ... La necessità di voci può essere evitata usando un nome di file diverso, come persistence-TEST.xml , e mettilo nella stessa cartella del normale file persistence.xml . Il contesto Spring dalla cartella di test può quindi fare riferimento a e Spring lo troverà per te in src/main .

In alternativa, si potrebbe essere in grado di mantenere persistence.xml lo stesso per l’applicazione effettiva e i test, e solo definirne uno in src/main . La maggior parte delle configurazioni come driver, dialetto e credenziali opzionali possono invece essere eseguite nel contesto Spring. Anche impostazioni come hibernate.hbm2ddl.auto possono essere passate nel contesto :

                    #{myConfig['db.ddl']} #{myConfig['db.showSql']} true    

Sembra che più file persistence.xml rappresentino un problema generale con JPA, risolto solo dalle classloading di trucchi.

Una soluzione alternativa che funziona per me consiste nel definire più unità di persistenza in un file persistence.xml e quindi assicurarsi che la distribuzione e il codice di test utilizzino un binding diverso (in spring è ansible impostare la proprietà “persistenceUnitName” sul factory del gestore quadro). Interagisce il file di distribuzione con la configurazione di test, ma se non ti dispiace che funzioni correttamente.

Aggiungere un /src/test/resources/META-INF/persistence.xml per i test: /src/test/resources/META-INF/persistence.xml Come detto @Arjan, ciò cambierebbe le classi root e entity dell’unità di persistenza verrebbero scansionate nelle classi target / test. Per gestirlo, aggiungi l’elemento jar-file a questo persistance.xml:

/src/test/resources/META-INF/persistence.xml

   org.hibernate.jpa.HibernatePersistenceProvider ${project.basedir}/target/classs         

Quindi, aggiungi il filtraggio delle risorse di test al tuo pom.xml:

  ...  ...   src/test/resources true   ...  ...  

Questo funzionerà perché jar-file può essere indirizzato alle directory, non solo ai file jar.

Ho provato l’approccio ClassLoaderProxy ma ho avuto il problema che le classi annotate JPA non sono gestite come classi persistenti da ibernazione.

Così ho deciso di provarlo senza usare persistence.xml. Il vantaggio è che la generazione di maven e il test JUnit di Eclipse funzioneranno senza modifiche.

Ho una persistente class di supporto per i test di 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(); } } 

Le mie classi di test estendono semplicemente PersistenceTestSupport e chiamano setup () in TestCase.setup ().

L’unico svantaggio è di mantenere le classi persistenti aggiornate, ma per JUnit test questo è accettabile per me.

Preferisco la soluzione di utilizzare diversi persistence.xml per il test e la produzione come post Rich Seller (grazie !!).

Ma devono cambiare:

  

per:

  

In ordine persistence.xml.proper non incorporato nel file .jar

Conserva due copie del file persistence.xml. Uno per i test e un altro per la build normale.

Il ciclo di vita predefinito copia la build persistence.xml in src / test / resources / META-INF

Creare un profilo separato che, una volta eseguito, copierà il test persistence.xml in src / test / resources / META-INF

Persistence.xml viene utilizzato come punto di partenza per cercare le classi di entity framework a meno che non vengano elencate esplicitamente e aggiunte tutte le classi. Pertanto, se si desidera eseguire l’override di questo file con un altro, ad esempio da src / test / resources, è necessario specificare ogni singola class di quadro in questo secondo persistence.xml altrimenti non verrà trovata alcuna class di quadro.

Un’altra soluzione sarebbe quella di sovrascrivere il file usando maven-resources-plugin (objective ‘copia-risorse’). Ma poi è necessario sovrascriverlo due volte, una volta per il test (ad es. Fasi di test del processo di fase) e una volta per il packaging reale (fase “pacchetto di preparazione”).

Questa risposta potrebbe sembrare sciocca, ma stavo cercando un modo che mi consenta di eseguire i test di Eclipse da Run As -> JUnit Test . Ecco come l’ho fatto:

 @BeforeClass public static void setUp() throws IOException { Files.copy(new File("target/test-classs/META-INF/persistence.xml"), new File("target/classs/META-INF/persistence.xml")); // ... } 

Sto solo copiando il test / persistence.xml in classs / persistence.xml. Questo funziona.

Sto provando a fare la stessa cosa. Ho una soluzione che funziona per me – il tuo può variare (e potresti non amare la soluzione … è un po ‘di basso livello).

Mi sono imbattuto in un articolo sulla rete in cui stavano usando un caricatore di classi personalizzato per fare qualcosa di simile che serviva da ispirazione. Se qualcuno può vedere come migliorare allora i suggerimenti sarebbero benvenuti btw. Per l’implementazione faccio affidamento sull’iniezione di container di EntityManager ma per il test l’ho creato io stesso utilizzando questo codice:

 final Thread currentThread = Thread.currentThread(); final ClassLoader saveClassLoader = currentThread.getContextClassLoader(); currentThread.setContextClassLoader(new ClassLoaderProxy(saveClassLoader)); EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("test"); em = emFactory.createEntityManager(); 

Quindi ClassLoaderProxy è quanto meno ansible e puoi semplicemente redirect le richieste per META-INF / persistence.xml a META-INF / test-persist.xml:

 public class ClassLoaderProxy extends ClassLoader { public ClassLoaderProxy(final ClassLoader parent) { super(); } @Override public Enumeration 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"); } } } 

Giusto per spiegarlo un po ‘di più:

  1. Esistono due file persistence.xml (uno denominato persistence.xml che viene utilizzato al di fuori dei test e uno denominato test-persist.xml utilizzato per i test).
  2. Il programma di caricamento classi personalizzato è attivo solo per i test di unità (per l’implementazione tutto è normale)
  3. Il programma di caricamento classi personalizzato reindirizza le richieste per “META-INF / persistence.xml” alla versione di test (“META-INF / test-persist.xml”).

Inizialmente stavo colpendo alcuni problemi perché Hibernate tornerà indietro (in qualche modo) al classloader che è stato usato per caricare Hibernate (almeno penso che sia quello che stava succedendo). Ho scoperto che inserendo il codice di commutazione ClassLoader (il primo blocco) come blocco statico nel tuo caso Test, verrà caricato prima di Hibernate ma, a seconda della struttura del test dell’unità, potrebbe essere necessario inserire lo stesso codice in altri luoghi (che schifo).

Un altro approccio consiste nell’usare un persistence.xml separato per il test (test /../ META-INF / persistence.xml ma sovrascrive lo Scanner come segue: –

test che persistence.xml deve contenere

Il codice per la nuova class TestScanner è il seguente.

 import java.lang.annotation.Annotation; import java.net.MalformsdURLException; import java.net.URL; import java.util.Set; import org.hibernate.ejb.packaging.NamedInputStream; import org.hibernate.ejb.packaging.NativeScanner; public class TestScanner extends NativeScanner { @Override public Set  > getClassesInJar (URL jar, Set  > annotations) { return super.getClassesInJar (getUpdatedURL (jar), annotations); } @Override public Set  getFilesInJar (URL jar, Set  patterns) { return super.getFilesInJar (getUpdatedURL (jar), patterns); } @Override public Set  getPackagesInJar (URL jar, Set  > annotations) { return super.getPackagesInJar (getUpdatedURL (jar), annotations); } private URL getUpdatedURL (URL url) { String oldURL = url.toExternalForm (); String newURL = oldURL.replaceAll ("test-classs", "classs"); URL result; try { result = newURL.equals (oldURL) ? url : new URL (newURL); } catch (MalformsdURLException e) { // Whatever } return result; } } 

Quando si utilizza OpenEJB, persistence.xml può essere sostituito da descrittori alternativi : http://tomee.apache.org/alternate-descriptors.html

Un’altra opzione per questo caso d’uso sarebbe l’aggiunta di più unità di persistenza, una per consentire la produzione di commenti e un’altra per testare e iniettare di conseguenza EntityManagerFactory.

Collocare entrambe le unità di persistenza nel persence.xml del progetto effettivo e fare in modo che i casi di test inseriscano il EntityManager corretto. L’esempio seguente illustra come farlo con guicing. Tieni presente che mi sono lasciato sfuggire un po ‘di derisione per completezza, il codice specifico del mockito è stato contrassegnato di conseguenza e non è necessario per l’iniezione.

 public class HibernateTestDatabaseProvider extends AbstractModule { private static final ThreadLocal ENTITYMANAGER_CACHE = new ThreadLocal<>(); @Override public void configure() { } @Provides @Singleton public EntityManagerFactory provideEntityManagerFactory() { return Persistence.createEntityManagerFactory("my.test.persistence.unit"); } @Provides public CriteriaBuilder provideCriteriaBuilder(EntityManagerFactory entityManagerFactory) { return entityManagerFactory.getCriteriaBuilder(); } @Provides public EntityManager provideEntityManager(EntityManagerFactory entityManagerFactory) { EntityManager entityManager = ENTITYMANAGER_CACHE.get(); if (entityManager == null) { // prevent commits on the database, requires mockito. Not relevant for this answer entityManager = spy(entityManagerFactory.createEntityManager()); EntityTransaction et = spy(entityManager.getTransaction()); when(entityManager.getTransaction()).thenReturn(et); doNothing().when(et).commit(); ENTITYMANAGER_CACHE.set(entityManager); } return entityManager; } } 

mettere i test nel proprio progetto Maven con il suo persistence.xml

Ti suggerirei di utilizzare diversi profili maven in cui puoi filtrare i tuoi file database.proprerties e avere un database.properties per profilo.

In questo modo non è necessario conservare duplicati di altri file di configurazione ad eccezione di .properties.

   default  true false **/*IT.java    default  true    default true false     src/main/resources/META-INF/spring/database.properties     true src/main/resources        integration   integration false true     src/main/resources/META-INF/spring/profiles/${build.profile.id}/database.properties     true src/main/resources        

Con l’aiuto di surefire per unit test e failfe per i test di integrazione, il gioco è fatto.

   org.apache.maven.plugins maven-surefire-plugin 2.12  org.junit:com.springsource.org.junit  false true  ${skip.unit.tests}   ${integration.test.files}     org.apache.maven.plugins maven-failsafe-plugin 2.12   ${skip.integration.tests}  ${integration.test.files}  once     integration-test  integration-test    verify  verify     

Ora hai bisogno solo di mvn test per i tuoi test unitari e mvn verify -Pintegration per i test di integrazione. Ovviamente è necessario creare i file database.properties nei percorsi specificati (sui profili) (o altrove e modificare i percorsi)

Basato su: http://www.petrikainulainen.net/programming/tips-and-tricks/creating-profile-specific-configuration-files-with-maven/

Questa è un’estensione della risposta di Rich Seller con la corretta gestione di Hibernate che trova più file persistence.xml sul classpath e il ripristino dello stato pre-test.

Impostare:

Creare un file di persistenza per la distribuzione / l’imballaggio e uno per il test:

  • src / main / risorse / persistence.xml

  • src / test / resources / persistence- testing .xml

nel tuo pom.xml aggiungilo alla sezione dei plugin:

   maven-antrun-plugin 1.3   copy-test-persistence process-test-resources   renaming deployment persistence.xml  replacing deployment persistence.xml with test version     run    restore-persistence prepare-package   restoring the deployment persistence.xml     run     

Vantaggi rispetto ad altre soluzioni

  • Nessun codice Java aggiuntivo richiesto
  • Solo un persistence.xml su classpath
  • Sia la costruzione che i test funzionano come previsto
  • Descrizione dell’output su console (echo)
  • Per il packaging lo stato è ripristinato al 100%. Nessun file rimanente