java - ejemplos - jsf jpa login example
Cómo configurar JPA para probar en Maven (16)
¿Hay alguna manera de configurar un segundo archivo persistence.xml en un proyecto Maven de modo que se use para las pruebas en lugar de uno normal que se usa para la implementación?
Traté de poner un persistence.xml en src / test / resources / META-INF, que se copia en target / test-classes / META-INF, pero parece target / classes / META-INF (la copia del src / main / resources) se prefiere, a pesar de que mvn -X test
enumera las entradas de classpath en el orden correcto:
[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
...
Me gustaría poder ejecutar pruebas en una configuración simple de hsqldb sin tener que cambiar la versión de implementación de la configuración de JPA, idealmente después de la finalización del proyecto sin necesidad de ajustes locales.
Agregue un persistance.xml para las pruebas: /src/test/resources/META-INF/persistence.xml
Como dijo @Arjan, eso cambiaría la raíz de la unidad de persistencia y las clases de entidad se escanearían en clases de destino / prueba. Para manejar eso, agregue el elemento jar-file a este persistance.xml:
/src/test/resources/META-INF/persistence.xml
<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_2_0.xsd"
version="2.0">
<persistence-unit name="com.some.project">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jar-file>${project.basedir}/target/classes</jar-file>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test_database" />
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
<property name="javax.persistence.jdbc.user" value="user" />
<property name="javax.persistence.jdbc.password" value="..." />
</properties>
</persistence-unit>
</persistence>
Luego, agregue el filtrado de recursos de prueba a su pom.xml:
<project>
...
<build>
...
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
...
</build>
...
</project>
Esto funcionará porque jar-file puede apuntar a directorios, no solo a archivos jar.
Al usar OpenEJB, persistence.xml puede ser anulado con descripciones alternativas : http://tomee.apache.org/alternate-descriptors.html
En un proyecto EE6 / CDI / JPA, una prueba src/test/resources/META-INF/persistence.xml
se recoge muy bien sin ninguna configuración adicional.
Cuando se utiliza JPA en Spring, lo siguiente funciona en el contexto de la aplicación que se usa para la prueba:
<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>
Aquí, /src/test/resources/META-INF/persistence.xml
(copiado en target/test-classes
) serían preferibles a /src/main/resources/META-INF/persistence.xml
(copiados en target/classes
) .
Desafortunadamente, la ubicación del archivo persistence.xml
también determina la llamada " raíz de la unidad de persistencia ", que luego determina qué clases se analizan para @Entity
anotaciones @Entity
. Por lo tanto, el uso de /src/test/resources/META-INF/persistence.xml
analizaría las clases en target/test-classes
, no las clases en target/classes
(donde las clases que deben probarse vivirían).
Por lo tanto, para las pruebas, uno necesitaría agregar explícitamente <class>
entradas de <class>
a persistence.xml
, para evitar java.lang.IllegalArgumentException: Not an entity: class ...
La necesidad de entradas de <class>
puede evitarse usando un nombre de archivo diferente, como persistence-TEST.xml
, y poner ese archivo en la misma carpeta que el archivo persistence.xml
normal. El contexto de Spring de su carpeta de prueba puede referirse a <property name="persistenceXmlLocation" value="META-INF/persistence-TEST.xml" />
, y Spring lo encontrará en src/main
.
Como alternativa, uno podría mantener persistence.xml
igual para la aplicación real y las pruebas, y solo definir uno en src/main
. La mayoría de las configuraciones como los controladores, el dialecto y las credenciales opcionales se pueden hacer en el contexto de Spring en su lugar. También se pueden pasar configuraciones como hibernate.hbm2ddl.auto
en el contexto :
<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>
Esta es una extensión de la respuesta de Rich Seller con el manejo adecuado de Hibernate encontrando múltiples archivos persistence.xml en el classpath y probando la restauración del estado.
Preparar:
Cree un archivo de persistencia para implementación / empaquetado y uno para probar:
src / main / resources / persistence.xml
src / test / resources / persistence- testing .xml
en tu pom.xml agrega esto a la sección de complementos:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>copy-test-persistence</id>
<phase>process-test-resources</phase>
<configuration>
<tasks>
<echo>renaming deployment persistence.xml</echo>
<move file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
<echo>replacing deployment persistence.xml with test version</echo>
<copy file="${project.build.testOutputDirectory}/META-INF/persistence-testing.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>restore-persistence</id>
<phase>prepare-package</phase>
<configuration>
<tasks>
<echo>restoring the deployment persistence.xml</echo>
<move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Ventajas sobre otras soluciones
- No se requiere código Java adicional
- Solo un persistence.xml en classpath
- Tanto la construcción como las pruebas funcionan como se esperaba
- Descripción de la salida en la consola (eco)
- Para el empaquetado, el estado está 100% restaurado. No hay archivos sobrantes
Esta respuesta puede sonar tonta pero estaba buscando una forma que me permita ejecutar esas pruebas desde eclipse mediante Run As
-> JUnit Test
. Así es como lo hice:
@BeforeClass
public static void setUp() throws IOException {
Files.copy(new File("target/test-classes/META-INF/persistence.xml"), new File("target/classes/META-INF/persistence.xml"));
// ...
}
Solo estoy copiando la prueba / persistence.xml a classes / persistence.xml. Esto funciona.
Estoy tratando de hacer lo mismo. Tengo una solución que funciona para mí: la tuya puede variar (y es posible que no te guste la solución ... tiene un nivel bajo).
Encontré un artículo en la red donde usaban un cargador de clases personalizado para hacer algo similar que sirvió de inspiración. Si alguien puede ver cómo mejorar, entonces las sugerencias serían bienvenidas por cierto. Para la implementación, confío en la inyección de contenedor del EntityManager, pero para probarlo lo creo yo mismo usando este código:
final Thread currentThread = Thread.currentThread();
final ClassLoader saveClassLoader = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(new ClassLoaderProxy(saveClassLoader));
EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("test");
em = emFactory.createEntityManager();
Entonces, el ClassLoaderProxy es tan mínimo como se puede obtener y simplemente redirige las solicitudes de META-INF / persistence.xml a 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");
}
}
}
Solo para explicar esto un poco más:
- Hay dos archivos persistence.xml (uno llamado persistence.xml que se usa fuera de prueba y uno llamado test-persist.xml que se usa para las pruebas).
- El cargador de clases personalizado solo está activo para las pruebas unitarias (para el despliegue todo es normal)
- El cargador de clases personalizado redirige las solicitudes de "META-INF / persistence.xml" a la versión de prueba ("META-INF / test-persist.xml").
Originalmente, estaba teniendo algunos problemas porque Hibernate se revertirá (de alguna manera) al cargador de clases que se usó para cargar Hibernate (al menos creo que eso es lo que estaba pasando). Descubrí que al colocar el código de cambio de ClassLoader (el primer bloque) como un bloque estático en su caso de prueba, se cargará antes de Hibernate, pero eso, dependiendo de la estructura de prueba de su unidad, también puede necesitar poner el mismo código en otros lugares (asco).
Lo siguiente funcionará para Maven 2.1+ (antes de eso, no había una fase entre prueba y paquete a la que pudiera vincular una ejecución).
Puede usar maven-antrun-plugin para reemplazar el persistence.xml con la versión de prueba por la duración de las pruebas, luego restaure la versión correcta antes de empaquetar el proyecto.
Este ejemplo asume que la versión de producción es src / main / resources / META-INF / persistence.xml y la versión de prueba es src / test / resources / META-INF / persistence.xml, por lo que se copiarán en target / classes / META -INF y target / test-classes / META-INF respectivamente.
Sería más elegante encapsular esto en un mojo, pero como solo está copiando un archivo, parece excesivo.
<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>
Mantenga dos copias del archivo persistence.xml. Uno para pruebas y otro para compilación normal.
El ciclo de vida predeterminado copia la compilación persistence.xml a src / test / resources / META-INF
Cree un perfil separado que cuando se ejecute copiará la prueba persistence.xml a src / test / resources / META-INF
Otra opción para este caso de uso sería agregar varias unidades de persistencia, una para permitir producción y otra para probar e inyectar EntityManagerFactory en consecuencia.
Coloque ambas unidades de persistencia en persistence.xml del proyecto real y haga que sus casos de prueba inserten el EntityManager correcto. El siguiente ejemplo ilustra cómo hacer eso con Guice. Tenga en cuenta que me he quedado en algún burla de mockito para completar, el código específico de mockito ha sido marcado en consecuencia y no es necesario para la inyección.
public class HibernateTestDatabaseProvider extends AbstractModule {
private static final ThreadLocal<EntityManager> 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;
}
}
Otro enfoque es usar un persistence.xml separado para probar (test /../ META-INF / persistence.xml pero anular el Scanner de la siguiente manera:
probar persistence.xml debe contener
<property name="hibernate.ejb.resource_scanner" value = "...TestScanner" />
El código para la nueva clase TestScanner es el siguiente.
import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
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 <Class <?> >
getClassesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
{ return super.getClassesInJar (getUpdatedURL (jar), annotations); }
@Override
public Set <NamedInputStream>
getFilesInJar (URL jar, Set <String> patterns)
{ return super.getFilesInJar (getUpdatedURL (jar), patterns); }
@Override
public Set <Package>
getPackagesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
{ return super.getPackagesInJar (getUpdatedURL (jar), annotations); }
private URL getUpdatedURL (URL url)
{
String oldURL = url.toExternalForm ();
String newURL = oldURL.replaceAll ("test-classes", "classes");
URL result;
try {
result = newURL.equals (oldURL) ? url : new URL (newURL);
} catch (MalformedURLException e)
{ // Whatever }
return result;
}
}
Parece que los archivos persistence.xml múltiples son un problema general con JPA, que solo se resuelve mediante trucos de carga de clases.
Una solución alternativa que funciona para mí es definir varias unidades de persistencia en un archivo persistence.xml y luego asegurarse de que su código de despliegue y prueba usa un enlace diferente (en Spring puede establecer la propiedad "persistenceUnitName" en la fábrica del administrador de entidades). Contamina el archivo de implementación con la configuración de prueba, pero si no te importa, funciona bien.
Persistence.xml se utiliza como punto de partida para buscar clases de entidad a menos que enumere todas las clases de forma explícita y agregue. Por lo tanto, si desea sobrescribir este archivo con otro, digamos desde src / test / resources, debe especificar cada clase de entidad en este segundo persistence.xml; de lo contrario, no se encontraría ninguna clase de entidad.
Otra solución sería sobrescribir el archivo utilizando maven-resources-plugin (objetivo ''copy-resources''). Pero luego hay que sobreescribirlo dos veces, una para probar (por ejemplo, fase proceso-prueba-clases) y una vez para el empaque real (fase ''preparar-paquete'').
Prefiero la solución de usar persistence.xml diferente para probar y producir como Rich Seller post (¡gracias!).
Pero necesito cambiar:
<copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
para:
<move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
Para que persistence.xml.proper no esté incrustado en el archivo .jar
Probé el enfoque ClassLoaderProxy pero tuve el problema de que las clases anotadas de JPA no se manejan como clases persistentes por hibernación.
Así que decidí probarlo sin usar persistence.xml. La ventaja es que la compilación maven y la prueba Eclipse JUnit funcionarán sin modificaciones.
Tengo una clase de soporte permanente para las pruebas de 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();
}
}
Mis clases de prueba solo extienden PersistenceTestSupport y llaman a setup () en TestCase.setup ().
El único inconveniente es mantener las clases persistentes hasta el día de hoy, pero para las pruebas JUnit esto es aceptable para mí.
Sugeriría usar diferentes perfiles de maven donde puedas filtrar tus archivos database.proprerties y tener una database.properties por perfil.
De esta forma, no es necesario conservar duplicados de ningún otro archivo de configuración, excepto para .properties.
<properties>
<!-- Used to locate the profile specific configuration file. -->
<build.profile.id>default</build.profile.id>
<!-- Only unit tests are run by default. -->
<skip.integration.tests>true</skip.integration.tests>
<skip.unit.tests>false</skip.unit.tests>
<integration.test.files>**/*IT.java</integration.test.files>
</properties>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!--
Specifies the build profile id, which is used to find out the correct properties file.
This is not actually necessary for this example, but it can be used for other purposes.
-->
<build.profile.id>default</build.profile.id>
<skip.integration.tests>true</skip.integration.tests>
<skip.unit.tests>false</skip.unit.tests>
</properties>
<build>
<filters>
<!--
Specifies path to the properties file, which contains profile specific
configuration. In this case, the configuration file should be the default spring/database.properties file
-->
<filter>src/main/resources/META-INF/spring/database.properties</filter>
</filters>
<resources>
<!--
Placeholders found from files located in the configured resource directories are replaced
with values found from the profile specific configuration files.
-->
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<!--
You can also include only specific files found from the configured directory or
exclude files. This can be done by uncommenting following sections and adding
the configuration under includes and excludes tags.
-->
<!--
<includes>
<include></include>
</includes>
<excludes>
<exclude></exclude>
</excludes>
-->
</resource>
</resources>
</build>
</profile>
<profile>
<id>integration</id>
<properties>
<!--
Specifies the build profile id, which is used to find out the correct properties file.
This is not actually necessary for this example, but it can be used for other purposes.
-->
<build.profile.id>integration</build.profile.id>
<skip.integration.tests>false</skip.integration.tests>
<skip.unit.tests>true</skip.unit.tests>
</properties>
<build>
<filters>
<!--
Specifies path to the properties file, which contains profile specific
configuration. In this case, the configuration file is searched
from spring/profiles/it/ directory.
-->
<filter>src/main/resources/META-INF/spring/profiles/${build.profile.id}/database.properties</filter>
</filters>
<resources>
<!--
Placeholders found from files located in the configured resource directories are replaced
with values found from the profile specific configuration files.
-->
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<!--
You can also include only specific files found from the configured directory or
exclude files. This can be done by uncommenting following sections and adding
the configuration under includes and excludes tags.
-->
<!--
<includes>
<include></include>
</includes>
<excludes>
<exclude></exclude>
</excludes>
-->
</resource>
</resources>
</build>
</profile>
</profiles>
Con la ayuda de surefire para pruebas unitarias y failfe para pruebas de integración, ya está hecho.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<junitArtifactName>org.junit:com.springsource.org.junit</junitArtifactName>
<!--see: https://issuetracker.springsource.com/browse/EBR-220-->
<printSummary>false</printSummary>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<!-- Skips unit tests if the value of skip.unit.tests property is true -->
<skipTests>${skip.unit.tests}</skipTests>
<!-- Excludes integration tests when unit tests are run. -->
<excludes>
<exclude>${integration.test.files}</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.12</version>
<configuration>
<!-- Skips integration tests if the value of skip.integration.tests property is true -->
<skipTests>${skip.integration.tests}</skipTests>
<includes>
<include>${integration.test.files}</include>
</includes>
<forkMode>once</forkMode>
<!--
<reuseForks>false</reuseForks>
<forkCount>1</forkCount>
-->
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
Ahora necesita solo mvn test
para las mvn test
de su unidad y mvn verify -Pintegration
para sus pruebas de integración. Obviamente, debe crear los archivos database.properties en las rutas especificadas (en los perfiles) (o en cualquier otro lugar y cambiar las rutas)
Referencia basada en: http://www.petrikainulainen.net/programming/tips-and-tricks/creating-profile-specific-configuration-files-with-maven/
poner pruebas en el propio proyecto maven con persistence.xml