无状态 ejb + jpa 的 junit 测试

junit test for stateless ejb + jpa

我想为我的无状态 ejb + jpa 演示代码编写一个 junit 测试。我觉得其实不是junit测试,是集成测试。

我有一个无状态 ejb,其中注入了 EntityManager 并使用了 PostgreSQL 数据库服务器。我使用 CDI(Spring 未在我的项目中使用)和带有 persistent.xml 文件的 EclipseLink。我的应用程序将在 GlassFish 服务器上执行。

我想编写一个测试来检查完整的逻辑:在我的示例无状态 ejb 上调用一个方法并将数据保存到内存数据库中。我想用我的测试启动内存数据库,并在我的测试 class 执行时停止它。

ejb class:

@Stateless
public class PropertyServiceImpl implements PropertyService {

    @PersistenceContext(name = "anything-jndi-em")
    private EntityManager em;

    public String getStringValue(final String key) {
        Property property = em.createNamedQuery("Property.findByKey", Property.class)
                .setParameter("key", key)
                .getSingleResult();

        return property.getValue();
    }
}

实体 class:

@Entity
@Table(name = "APPLICATION_SETTING")
@NamedQueries({
        @NamedQuery(name = "Property.findByKey", query = "select a from Property a where a.key = :key and a.status = 1")
})
public class Property
{
    @Id
    @SequenceGenerator(name = "APPLICATION_SETTING_SEQ", sequenceName = "APPLICATION_SETTING_SEQ", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "APPLICATION_SETTING_SEQ")
    @Column(name = "ID", unique = true, nullable = false)
    private Long id;

    @Column(name = "KEY", length = 200, nullable = false)
    private String key;
    ...
}

如果我是正确的,我需要执行以下步骤:

  1. 使用正确的 jdbc 连接参数创建一个新的 persistent.xml 文件,该文件将连接到内存数据库并将其放在 /test/Resources/META-INF 文件夹下
  2. 为内存数据库(例如:hsqldb)和嵌入式 ejb 容器添加一些 pom 依赖项
  3. 创建一个简单的 PropertyServiceImplTest.java class
  4. 以某种方式配置 /test/Resources/META-INF/persistent.xml 文件将由我的测试使用 class
  5. 初始化嵌入式ejb容器并启动内存数据库
  6. 执行我的juni测试方法:

@Test public void testGetStringValue() { PropertyService service = new PropertyServiceImpl(); assertNotNull(service.getStringValue("abc")); }

你能帮我写一个适合这个场景的测试吗javaclass?

在您的实际测试用例中启动 javax.ejb.embeddable.EJBContainer。之后使用它的 javax.naming.Context 来查找你的无状态 bean。您可以像习惯一样使用您的 bean 并断言其行为。请记住,与完整的 ejb 容器相比,可嵌入容器 impl 只需支持功能的一个子集(ejb lite)。 Here 你找到了一个非常简洁的例子。

代码片段:

JBContainer ejbContainer = EJBContainer.createEJBContainer();
Context ctx = ejbContainer.getContext();
PropertyService  service = (PropertyService) ctx.lookup("java:global/classes/PropertyServiceImpl");
assertNotNull(service.getStringValue("abc"));

我强烈推荐Arquillian。我在 Wildfly 8、9 和 10 上将它与 gradle 一起使用。一旦设置正确(可能有点费力),它就会产生奇迹。

您只需像这样注释您的 JUnit 测试:

@RunWith(Arquillian.class)
public class MRSInjectionServiceTests extends ...

然后提供部署存档,我通过 JBoss ShrinkWrap:

@Deployment
public static WebArchive createDeployment() {
    return ShrinkWrap.create(ZipImporter.class, "MRSInjectionServiceTests.war").importFrom(new File(ROOT_WAR_DEPLOYMENT_LOCATION)).as(WebArchive.class);
}

然后您可以 运行 来自 maven 或 gradle 的这些集成测试。 Arquillian 将 运行 您的应用程序服务器容器(Wildfly、GLassfish 等)基于您的配置,并将 运行 JUnit 测试作为对整个系统的系统测试 运行ning.

很不错。值得付出努力。

我想与您分享我的解决方案。根据你的帮助,我查看了 Arquillian 文档,最后我能够创建一个合适的集成测试。 我的测试使用带有 HSQL 内存数据库的 GlassFish 嵌入式 EE 容器。

测试javaclass:

@RunWith(Arquillian.class)
public class PropertyServiceImplTest {

    @EJB
    private PropertyService propertyService;

    @Deployment
    public static JavaArchive createTestArchive() {
        return ShrinkWrap.create(JavaArchive.class, "test.jar").
                .addPackage(Property.class.getPackage())
                .addPackage(PropertyService.class.getPackage())
                //.addPackage(PropertyServiceImpl.class.getPackage())
                addAsResource("META-INF/persistence.xml");
    }

    @Test
    public void testGetStringValue() {
        assertNotNull(propertyService);

        Property property = new Property();
        property.setKey("aaa");
        property.setValue("5");
        propertyService.setStringValue(property);

        assertEquals(propertyService.getStringValue("aaa"), "5");
    }
}

persistence.xml

  • 目标数据库的有效值:eclipselink doc

    <persistence-unit name="example" transaction-type="JTA">
        <jta-data-source>jdbc/my-ds</jta-data-source>
        <class>a.b.domain.Property</class>
        <properties>
            <property name="eclipselink.target-database" value="HSQL"/>
            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
            <property name="eclipselink.logging.level" value="ALL"/>
        </properties>
    </persistence-unit>
    

arquillian.xml

如何消除疯狂的"Potential problem found"警告信息?必须设置 DatatypeFactory 属性。

WARNING: Potential problem found: The configured data type factory
         'class org.dbunit.dataset.datatype.DefaultDataTypeFactory' might cause problems with the current database
           'HSQL Database Engine' (e.g. some datatypes may not be supported properly). In rare cases you might see this
           message because the list of supported database products is incomplete (list=[derby]). If so please request a
           java-class update via the forums.If you are using your own IDataTypeFactory extending DefaultDataTypeFactory,
           ensure that you override getValidDbProducts() to specify the supported database products.
  • DBUnit 特定设置:jboss documentation
  • 数据类型工厂:dbunit documentation

    <engine>
        <property name="deploymentExportPath">target/arquillian</property>
    </engine>
    
    <container default="true" qualifier="glassfish">
        <configuration>
            <property name="resourcesXml">myproject/src/test/resources/glassfish-resources.xml</property>
        </configuration>
    </container>
    
    <extension qualifier="persistence">
        <property name="defaultDataSource">jdbc/my-ds</property>
    </extension>
    
    <extension qualifier="persistence-dbunit">
        <property name="datatypeFactory">org.dbunit.ext.hsqldb.HsqldbDataTypeFactory</property>
    </extension>
    

glassfish-resources.xml

<resources>
    <jdbc-connection-pool name="jdbc/my-pool"
                          res-type="javax.sql.DataSource"
                          datasource-classname="org.hsqldb.jdbc.JDBCDataSource">
        <property name="PortNumber" value="9001" />
        <property name="serverName" value="localhost" />
        <property name="URL" value="jdbc:hsqldb:mem:arquillian" />
        <property name="user" value="sa" />
        <property name="password" value="" />
    </jdbc-connection-pool>

    <jdbc-resource jndi-name="jdbc/my-ds" pool-name="jdbc/my-pool" />
</resources>

项目结构:

  • src/test/java/a.b.PropertyServiceImplTest.java
  • src/test/resources/META-INF/persistence.xml
  • src/test/resources/arquillian.xml
  • src/test/resources/glassfish-resources.xml

用了 arquillian 3-4 天后,我很失望。

我阅读了有关此测试框架的文档,并使用它创建了一个不错的环境。创建了漂亮的 xml 文件,其中包含用于生产和测试环境的占位符,并且完成了 pom 文件。我的简单 ejb 和实体一切正常 java 类.

今天我开始处理我真正的 java 8 应用程序。一段时间后发生了一些事情,因为我的测试失败了。当我从我的代码中删除 lambda 表达式时,我的测试再次正常运行。

所以当我使用 lambda 表达式时,注入的 ejb 总是空的: @EJB 私有配置服务配置服务;

GlassFish Managed 3.1 Container 的最新版本是 1.0.0.CR4,发布日期:2013-04-29。它很旧:(

A​​rquillian GlassFish 模块完全没用:(

我也遇到过这个问题,但你可以使用 Payara 的嵌入式服务器解决它。

   <dependency>
        <groupId>fish.payara.extras</groupId>
        <artifactId>payara-embedded-all</artifactId>
        <version>4.1.1.162</version>
        <scope>test</scope>
    </dependency>