无法在使用 Arquillian 和 WildFly 的 JPA 集成测试中注入 EntityManager

Posted

技术标签:

【中文标题】无法在使用 Arquillian 和 WildFly 的 JPA 集成测试中注入 EntityManager【英文标题】:Unable to Inject EntityManager in JPA Integration Testing With Arquillian and WildFly 【发布时间】:2015-09-02 15:32:12 【问题描述】:

我正在尝试使用以下堆栈进行集成测试:

App server: Embedded WildFly
CDI container: Weld
Database: In-memory H2
ORM: Hibernate/JPA
Platform: Java 8
OS: Mac OS X 10.10

我已经使用 Arquillian 设置了基本的集成测试(如 here 所做的那样),我能够注入依赖项,但注入 EntityManager 被证明是一个挑战。取消引用实体管理器字段始终会导致 NullPointerException

我看过很多文章(包括this 和this),但我仍然无法让这个看似简单的事情发挥作用。

请看下面我的pom.xml

  <dependencies>
    <dependency>
      <groupId>org.jboss.spec</groupId>
      <artifactId>jboss-javaee-7.0</artifactId>
      <version>1.0.0.Final</version>
      <type>pom</type>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.1</version>
      <scope>test</scope>
    </dependency>

    <!-- JUnit Container Implementation for the Arquillian Project -->
    <dependency>
      <groupId>org.jboss.arquillian.junit</groupId>
      <artifactId>arquillian-junit-container</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.arquillian.protocol</groupId>
      <artifactId>arquillian-protocol-servlet</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.arquillian.container</groupId>
      <artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
      <version>1.0.0.CR3</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.arquillian.extension</groupId>
      <artifactId>arquillian-persistence-dbunit</artifactId>
      <version>1.0.0.Alpha7</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.weld</groupId>
      <artifactId>weld-core</artifactId>
      <version>1.1.5.Final</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.6.4</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.jboss.arquillian</groupId>
        <artifactId>arquillian-bom</artifactId>
        <version>1.1.8.Final</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
    </dependencies>
  </dependencyManagement>

test-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="test" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>com.xyz.hellomaven.DummyEntity</class>

    <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
    <!--<jta-data-source>java:/DefaultDS</jta-data-source>-->
    <!--<jta-data-source>jdbc/arquillian</jta-data-source>-->

    <properties>
      <property name="hibernate.show_sql" value="true" />
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <!--<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />-->
    </properties>
  </persistence-unit>
</persistence>

测试用例

@RunWith(Arquillian.class)
public class GreeterTest 

  @Inject
  private Greeter instance; // Injection works!

  @PersistenceContext
  private EntityManager em; // Null pointer.

  public GreeterTest() 
  

  @Deployment
  public static WebArchive createDeployment() 
    return ShrinkWrap.create(WebArchive.class)
        .addClasses(Greeter.class, PhraseBuilder.class, DummyInterceptor.class)
        .addAsResource("logging.properties", "META-INF/logging.properties")
        .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
        .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
  

  @Test
  public void testCreateGreeting() 
    System.out.println("createGreeting");
    assertEquals("Hello, Steve!", instance.createGreeting("Steve"));
  

  @Test
  public void testPersistence() 
    DummyEntity de = new DummyEntity();
    de.setId(1l);
    de.setName("Petr Cech");
    de.setAge(10);
    em.persist(de);

    Query q = em.createQuery("SELECT d.age FROM DummyEntity d");
    assertEquals(10, q.getResultList().get(0));
  

完整的 Maven 项目可用on GitHub。

请问我做错了什么?

【问题讨论】:

我不了解 Arquillian,但容器管理的 entitymagner 仅在服务器上可用。我相信您将不得不使用应用程序托管实体管理器。 这有两个问题。 1. 行不通。如果您无法注入 EM,您将无法注入 EMF(如果我错了,请纠正我)。 2. 即使它确实有效,这也让我的“集成测试”变得不那么可靠,因为架构不再与生产环境相同(容器与 bean 管理的持久性/事务) Arquillian 是一个集成测试环境,并且是预计将生产支持服务器组件。这与仅在 JUnit/TestNG 中工作不同,后者将是类似 SE 的应用程序。 【参考方案1】:

只是不要使用焊接,sins 数据源超出了 CI 和 DI 可能涵盖的范围。也许你可以用 Mokito 嘲笑它,并留在 light Weld,

<dependency>
      <groupId>org.jboss.arquillian.container</groupId>
      <artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
      <version>1.0.0.CR3</version>
      <scope>test</scope>
</dependency>

但是,如果您想处理真正的数据库,请改用托管 jboss(ExampleDS 是一个演示 jboss h2 数据源)或托管 glassfish。

        <dependency>
            <groupId>org.jboss.as</groupId>
            <artifactId>jboss-as-arquillian-container-managed</artifactId>
            <version>7.1.1.Final</version>
            <scope>test</scope>
        </dependency>

参考。 https://github.com/arquillian/arquillian-examples/blob/master/arquillian-persistence-tutorial/pom.xml

【讨论】:

【参考方案2】:

正如@Soloviev Dmitry 所说,您使用 CDI 容器进行集成测试,它只启用 CDI。

我看到了两个选项:

第一个是使用在你的maven项目中配置的wildfly嵌入式容器,所以在maven阶段运行你的集成测试,wildfly将被下载,你的测试包将被部署到它。因此,ExampleDS 可以正常工作,因为 Wildfly 开箱即用。

详情请见this post

第二个将包括不使用 Arquillian 进行集成测试。因此,如果您的集成测试仅涵盖托管 bean(不是会话 bean、Wildfly 特定资源......),您可以在测试执行之前实例化一个 CDI 容器(例如,在使用 Junit 的 @Before 或 @BeforeClass 注释方法中)然后使用 EntityManagerFactory 类实例化您的 EntityManager,引用用于此集成测试的持久性单元。使用此方法,您还可以创建 CDI 生产者来为您的集成测试、模拟注入其他资源,具体取决于您的测试范围。

maven 依赖

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se</artifactId>
    <version>2.1.2.Final</version>
    <scope>test</scope>
</dependency>

测试类

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.junit.*;

public class ExampleIT 

    private EntityManager em;

    protected static Weld weld;
    protected static WeldContainer container;

    @BeforeClass
    public static void init() 
        weld = new Weld();
        container = weld.initialize();
    

    @AfterClass
    public static void close() 
        weld.shutdown();
    

    @Before
    private void before()
    em = Persistence.createEntityManagerFactory("MyPersistenceUnit").createEntityManager();
    

    @Test
    public void testToto()
        // Do something with entity manager ...
    


我通常为集成测试选择第二种解决方案,因为它比 Arquillian 测试更容易设置,并且执行速度更快。

【讨论】:

【参考方案3】:

使用应用程序管理的实体管理器。

@PersistenceUnit
EntityManagerFactory emf; 

并使用

创建 entityManager
EntityManager em = emf.createEntityManager();

容器管理的 entitymanager 由容器自身创建和注入。如果你不是在服务器环境下,那么你需要使用 application managed persistence context。

【讨论】:

您好,感谢您花时间帮助我。我只是尝试了这种方法(只是为了确定),不出所料,它没有用。这有两个问题。 1. 行不通。如果您无法注入 EM,您将无法注入 EMF(如果我错了,请纠正我)。 2. 即使它确实有效,它也让我的“集成测试”变得不那么可靠,因为架构不再与生产环境相同(容器与 bean 管理的持久性/事务) Arquillian 是一个集成测试环境,并且是预计将生产支持服务器组件。【参考方案4】:

我猜您的上下文文件中缺少事务管理器 + 实体管理器工厂。在 test-persistence.xml 中配置两者,然后将实体管理器工厂设置为事务管理器的属性。

【讨论】:

嗨,Marcus,感谢您抽出时间帮我解答。您能否提供示例 sn-ps 来实施您的解决方案?提前致谢。

以上是关于无法在使用 Arquillian 和 WildFly 的 JPA 集成测试中注入 EntityManager的主要内容,如果未能解决你的问题,请参考以下文章

无法创建类 org.jboss.arquillian.test.impl.EventTestRunnerAdaptor 的新实例

使用 Arquillian 和 Wildfly 进行集成测试

是否可以使用arquillian资源进行参数化测试?

Arquillian:嵌入野蝇?

在 Equinox 中运行 Arquillian

使用 Arquillian 时如何引用放置在 WEB-INF 文件夹中的文件?