Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何配置JPA以在Maven中进行测试_Java_Testing_Maven 2_Jpa_Integration Testing - Fatal编程技术网

Java 如何配置JPA以在Maven中进行测试

Java 如何配置JPA以在Maven中进行测试,java,testing,maven-2,jpa,integration-testing,Java,Testing,Maven 2,Jpa,Integration Testing,有没有办法在Maven项目中设置第二个persistence.xml文件,以便将其用于测试,而不是用于部署的普通文件 我尝试将persistence.xml放入src/test/resources/META-INF,它会被复制到target/test classes/META-INF中,但似乎target/classes/META-INF(src/main/resources的副本)更受欢迎,尽管mvn-X test以正确的顺序列出了类路径条目: [DEBUG] Test Classpath :

有没有办法在Maven项目中设置第二个persistence.xml文件,以便将其用于测试,而不是用于部署的普通文件

我尝试将persistence.xml放入src/test/resources/META-INF,它会被复制到target/test classes/META-INF中,但似乎target/classes/META-INF(src/main/resources的副本)更受欢迎,尽管
mvn-X test
以正确的顺序列出了类路径条目:

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

我希望能够针对简单的hsqldb配置运行测试,而无需更改JPA配置的部署版本,理想情况下在项目签出后立即运行,而无需任何本地调整。

保留persistence.xml文件的两个副本。一个用于测试,另一个用于正常构建

默认生命周期将build persistence.xml复制到src/test/resources/META-INF


创建一个单独的配置文件,当运行该配置文件时,它将把testing persistence.xml复制到src/test/resources/META-INF

看来多个persistence.xml文件是JPA的一个普遍问题,只有通过类加载技巧才能解决


对我来说,一种解决方法是在一个persistence.xml文件中定义多个持久性单元,然后确保部署和测试代码使用不同的绑定(在Spring中,您可以在entity manager工厂上设置“persistenceUnitName”属性)。它会用测试配置污染您的部署文件,但如果您不介意它正常工作的话。

Persistence.xml用作搜索实体类的起点,除非您明确列出所有类并添加。 因此,如果您想用另一个文件(比如src/test/resources)覆盖此文件,那么必须在第二个persistence.xml中指定每个实体类,否则将找不到实体类


另一个解决方案是使用maven资源插件覆盖文件(“copy-resources”目标)。但是,您必须覆盖它两次,一次用于测试(例如,阶段过程测试类),一次用于真正的打包(阶段“准备打包”)。

我正在尝试做同样的事情。我有一个适合我的解决方案-你的可能会有所不同(而且你可能不喜欢这个解决方案…它有点低级)

我在网上看到一篇文章,他们使用一个定制的类加载器来做一些类似的事情,这给了我灵感。如果有人能看到如何改进,那么建议将是受欢迎的。对于部署,我依赖EntityManager的容器注入,但对于测试,我使用以下代码自己创建它:

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

EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("test");
em = emFactory.createEntityManager();
然后,ClassLoaderProxy将尽可能小,只是将META-INF/persistence.xml请求重定向到META-INF/test-persistence.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");
        }
    }
}
public类ClassLoaderProxy扩展类加载器{
公共类装入器代理(最终类装入器父级){
超级();
}
@凌驾
公共枚举getResources(最终字符串名称)引发IOException{
if(!“META-INF/persistence.xml”.equals(name)){
返回super.getResources(name);
}否则{
println(“重定向persistence.xml以测试persistent.xml”);
返回super.getResources(“META-INF/test persist.xml”);
}
}
}
再解释一下:

  • 有两个persistence.xml文件(一个名为persistence.xml,用于测试外部,另一个名为test-persistent.xml,用于测试)
  • 自定义类加载器仅在单元测试时处于活动状态(对于部署,一切正常)
  • 自定义类加载器将对“META-INF/persistence.xml”的请求重定向到测试版本(“META-INF/test persistence.xml”)
    我最初遇到了一些问题,因为Hibernate将(以某种方式)恢复到用于加载Hibernate的类加载器(至少我认为是这样)。我发现将类加载器切换代码(第一个块)作为静态块放在测试用例中,它将在Hibernate之前加载,但是,根据您的单元测试结构,您可能还需要将相同的代码放在其他地方(糟糕)。

    以下内容适用于Maven 2.1+(在此之前,在测试和包之间没有可以绑定执行的阶段)

    您可以使用maven antrun插件在测试期间用测试版本替换persistence.xml,然后在打包项目之前恢复正确的版本

    本例假设生产版本为src/main/resources/META-INF/persistence.xml,测试版本为src/test/resources/META-INF/persistence.xml,因此它们将分别复制到target/classes/META-INF和target/test classes/META-INF

    将其封装到一个mojo中会更加优雅,但由于您只复制了一个文件,这似乎有些过头了

    <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>
    
    
    maven antrun插件
    1.3
    复制测试持久性
    过程测试资源
    跑
    恢复持久性
    准备包装
    跑
    
    使用persistence.xml将测试放在自己的maven项目中

    作为富卖家,我更喜欢使用不同persistence.xml进行测试和生产的解决方案(谢谢!!)

    但需要改变:

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

    
    

    为了使persistence.xml.property不嵌入EE6/CDI/JPA项目中的.jar文件中,测试
    src/test/resources/META-INF/persistence.xml
    在没有任何进一步配置的情况下被很好地拾取

    在Spring中使用JPA时,以下内容在用于测试的应用程序上下文中起作用:

    
    
    这里,
    /src/test/resources/META-INF/persistence.xml
    (复制到
    目标/测试类中<move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
    
    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();
        }
    }
    
    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;
    }
    
    }
    
    <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>
    
        <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>
    
    @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"));
        // ...
    }
    
    <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>
    
    <project>
        ...
        <build>
            ...
            <testResources>
                <testResource>
                    <directory>src/test/resources</directory>
                    <filtering>true</filtering>
                </testResource>
            </testResources>
            ...
        </build>
    ...
    </project>
    
            <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>
    
    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;
        }
    }