JUnit,JPA,Hibernate和Postgres:如何进行测试?

4

我现在陷入了困境。我已经进行了大量搜索,但是我找不到最简单的方法来测试实体类或针对Postgres数据库的JPA操作。我已经找到了使用Spring、Mockito和其他工具的方式,但是我找不到使用纯Java的最简单的方式。

我有以下JUnit测试:

public class ModelConverterTest {

    private static EntityManagerFactory emf;
    private static EntityManager em;


    public ModelConverterTest() {
    }

    @BeforeClass
    public static void setUpClass() throws Exception {
        emf = Persistence.createEntityManagerFactory("PU");
        em = emf.createEntityManager(); // Retrieve an application managed entity manager
    }

    @AfterClass
    public static void tearDownClass() throws Exception {
        em.close();
        emf.close(); //close at application end
    }

    @Before
    public void setUp() {
         ...    
    }

    @After
    public void tearDown() {
    }

    /**
     * Test of SIMModelToModel method, of class ModelConverter.
     */
    @Test
    public void testSIMModelToModel() {
        System.out.println("SIMModelToModel");
        SIMModel simModel = new PESMModel();
        simModel.addState(testState);
        Model expResult = null;
        Model result = ModelConverter.SIMModelToModel(em, simModel);
        assertTrue(expResult!=null);
        // TODO review the generated test code and remove the default call to fail.
        //fail("The test case is a prototype.");
    }
}

当我运行它时,会出现以下错误:
java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/persistence/PersistenceContextType
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:787)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:447)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2442)
    at java.lang.Class.getDeclaredMethods(Class.java:1808)
    at sun.reflect.annotation.AnnotationType$1.run(AnnotationType.java:104)
    at sun.reflect.annotation.AnnotationType$1.run(AnnotationType.java:101)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.reflect.annotation.AnnotationType.<init>(AnnotationType.java:100)
    at sun.reflect.annotation.AnnotationType.getInstance(AnnotationType.java:84)
    at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:221)
    at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:88)
    at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:70)
    at java.lang.reflect.Field.declaredAnnotations(Field.java:1033)
    at java.lang.reflect.Field.getDeclaredAnnotations(Field.java:1026)
    at java.lang.reflect.AccessibleObject.getAnnotations(AccessibleObject.java:196)
    at org.junit.runners.model.FrameworkField.getAnnotations(FrameworkField.java:26)
    at org.junit.runners.model.TestClass.addToAnnotationLists(TestClass.java:52)
    at org.junit.runners.model.TestClass.<init>(TestClass.java:45)
    at org.junit.runners.ParentRunner.<init>(ParentRunner.java:73)
    at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:55)
    at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:13)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:29)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24)
    at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:51)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
    at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
    at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)

我的 persistence.xml 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
  <persistence-unit name="PU" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>jdbc/modelsystemdb</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties/>
  </persistence-unit>
</persistence>

我该怎么做才能让测试运行起来?


4
如果你使用真正的JPA提供者而不是模拟接口,那么这不是一个单元测试,而是一个集成测试。这完全没问题 - 但你不应该称其为单元测试。 - Tom Anderson
你说得对,我编辑了这个问题。 - Christian Vielma
5个回答

3

以下是Adam Bien的文章《在Maven仓库中使用残缺的Java EE 6 API的问题及解决方案》的引述:

不要使用

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-web-api</artifactId>
  <version>6.0</version>
  <scope>provided</scope>
</dependency>

你应该使用替代的依赖项(如Geronimo、JBoss等):
<dependency>
   <groupId>org.apache.geronimo.specs</groupId>
   <artifactId>geronimo-ejb_3.1_spec</artifactId>
   <version>1.0</version>
   <scope>provided</scope>
</dependency>
<dependency>
   <groupId>org.apache.geronimo.specs</groupId>
   <artifactId>geronimo-jpa_2.0_spec</artifactId>
   <version>1.0</version>
   <scope>provided</scope>
</dependency>

谢谢!但是我在我的pom文件中没有包含那个依赖项。我有javaee-api。尝试删除它,但找不到完全替代它的东西。此外,我尝试使用你给我的那些依赖项和Jboss,但都没有成功。虽然我正在使用Glassfish,但也找不到替代包 :S - Christian Vielma
javaee-api 适用于编译,但在运行时不能使用它。您必须从提供者那里获取一个正确的API实现。我使用 org.hibernate.javax.persistence:hibernate-jpa-2.0-api,其中包含JPA 2 API,仅此而已。即使您不使用Hibernate,也可以使用它,因为它只是API。 - Tom Anderson

2

我遇到了类似的问题。即使Hibernate JPA jar通过传递依赖项可用,并且提供了javaee 6.0依赖项,但JUNIT测试用例仍然失败了。我只需将javaee依赖项移动到pom文件中的依赖项定义的末尾,以便在类路径解析期间出现Hibernate JPA api jar之前出现Javaee jar。这似乎起了作用,我能够运行测试用例。希望这有所帮助。

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate.version}</version>
    </dependency>

在 Hibernate 依赖项之后出现的 JavaEE。

<dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>6.0</version>
        <scope>provided</scope>
    </dependency>

+1:我也曾经使用过这种方法,在我的POM底部加上了javaee-api。很高兴现在能够将问题缩小到Hibernate的排序上,现在想起来似乎很明显。 :-) - David Victor

1
如果您想在测试中使用JPA,则运行测试时需要在类路径上拥有一个JPA提供程序。您可以编译针对javaee-api,但是在运行时必须有一个真正的、实时的提供程序。
您提到您正在使用GlassFish;它使用EclipseLink作为其提供程序,因此您在测试中也应该这样做。他们的维基上有有关通过Maven使用EclipseLink的信息

当然!只需添加相关的Hibernate依赖项即可。我有org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final作为编译依赖,以及org.hibernate:hibernate-entitymanager:4.1.4.Final作为运行时依赖。这些加上它们的传递依赖就足够使用Hibernate了。嗯,还需要一个数据库驱动程序。还有一种日志记录后端,虽然实际上,我还没有弄清楚。 - Tom Anderson
你确定在你的运行时类路径中没有javaee-api吗?我认为只有当它存在时,你才会收到“缺失代码属性”错误。 - Tom Anderson
1
所有的注解也都在 org.hibernate.javax.persistence:hibernate-jpa-2.0-api 中,所以如果你的编译类路径中有它,它们就会被解析。如果它们没有被解析,那么它实际上并不在你的类路径中。 - Tom Anderson
好的,我成功找到了依赖项,但是在同一个包中,我还有 Stateless 和 LocalBeans 注释需要 javaee-api。 - Christian Vielma
1
啊哈。在这种情况下,您需要找到其他来源,或确保hibernate-jpa-2.0-api在类路径上比javaee-api更优先。但实际上,javaee-api只适用于编译,而不适用于运行时使用。 - Tom Anderson
显示剩余4条评论

0
在我的情况下,进入项目的“运行配置/类路径”,点击编辑并勾选“仅包括已导出的条目”,这就解决了问题。

0
我们遇到了一个非常相似的问题:在maven构建期间,Junit测试成功执行,但是直接从Eclipse运行时却出现了“缺失代码”错误。解决方案不在pom.xml文件中(因为您的构建已经运行成功了,对吧?),而是在eclipse中配置Junit执行。在我们的情况下,当maven依赖项移到项目本身之上时,正确的实现(eclipselink 2.0.4)持久化类被加载了。因此,在eclipse中尝试检查“运行配置...”,选择相关的Junit配置,在“Classpath”选项卡上,更改“User Entries”部分库的顺序:将Maven依赖项移到最上面。

最好的问候, Joe Public


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接