EJBContainerRunner or test with EJBContainer easily


To test a EE application you have several solutions. One of them is to use EJBContainer.

EJBContainer container = EJBContainer.createEJBContainer(properties);
// do test
container.close();

These steps are often in a test lifecycle (@BeforeClass/@AfterClass for JUnit for instance).

With OpenEJB you also have:

container.getContext().bind("inject", this);

This allows you to get injections (@Inject, @Resource, @PersistenceContext…) in the test class.

That’s nice but you can make it easier!

Since some versions OpenEJB provides in openejb-junit module a runner to do more or less these steps.

Here is the dependency:

<dependency>
  <groupId>org.apache.openejb</groupId>
  <artifactId>openejb-junit</artifactId>
  <version>${openejb.version}</version>
  <scope>test</scope>
</dependency>

Then just write a standard JUnit test with EJBContainerRunner:

package org.superbiz;

import org.apache.openejb.junit.jee.EJBContainerRunner;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.inject.Inject;

import static org.junit.Assert.assertEquals;

@RunWith(EJBContainerRunner.class)
public class MyBeanTest {
    @Inject
    private MyBean bean;

    @Test
    public void sayHi() {
        assertEquals("Hello!", bean.hi());
    }
}

This is already great but you can go further configuring the container (properties):

// @PropertyFile if you want to reuse properties
@Properties({
    @Property(key = "myDataSource", value = "new://Resource?type=DataSource")
})
@RunWith(EJBContainerRunner.class)
public class MyBeanTest {
    @Inject
    private MyBean bean;

    @Resource(name = "myDataSource")
    private DataSource myDs;

    // ...tests
}

On test side you can control if the test is executed under a transaction or not:

@Test
@Transaction(rollback = true)
public void myTest() {
    // ...
}

Finally there are some test oriented injections to get the container Context (to do lookups), container properties or the container itself:

@RunWith(EJBContainerRunner.class)
public class MyBeanTest {
    @TestResource
    private Context ctx;

    @TestResource
    private Hashtable<String, ?> props;

    @TestResource
    private EJBContainer container;

    // ...
}

Want more content ? Keep up-to-date on my new blog

Or stay in touch on twitter @rmannibucau

 

15 thoughts on “EJBContainerRunner or test with EJBContainer easily

  1. Gilberto

    How can I override the JPA provider since Tomee 7.0.0-M3 doesn’t use the 2.1 version yet? Openjpa always start up in the test even if I put p.put(“gacePU.provider”, “org.eclipse.persistence.jpa.PersistenceProvider”); .
    Thank you!

    Reply
    1. rmannibucau Post author

      You need to exclude openjpa and add your provider in the classpath or just set provider in your persistence.xml using EJBContainer.

      Reply
  2. Gilberto

    My persistence.xml does not have any information about jpa provider, just jta-data-source, which I’m overriding in the @BeforeClass. The eclipselink is in the classpath and I’ve excluded the openJPA provider. But I’m getting

    r.gov.to.bem.service.BancoServiceTest  Time elapsed: 3.243 sec  <<< ERROR!
    org.apache.openejb.OpenEjbContainer$AssembleApplicationException: org.apache.openejb.OpenEJBException: Creating application failed: /home/gilberto.andrade/tmp/gace: org.apache.openejb.OpenEJBRuntimeException: java.lang.ClassNotFoundException: org.apache.openjpa.persistence.PersistenceProviderImpl: java.lang.ClassNotFoundException: org.apache.openjpa.persistence.PersistenceProviderImpl
            at org.apache.openejb.OpenEjbContainer$Provider.createEJBContainer(OpenEjbContainer.java:346)
            at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:127)
            at br.gov.to.bem.service.BancoServiceTest.startTheContainer(BancoServiceTest.java:31)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:498)
    
    
    Reply
  3. Gilberto

    As I’ve said before, the property has not effect

    import br.gov.to.bem.model.Banco;
    import javax.ejb.EJB;
    import javax.ejb.embeddable.EJBContainer;
    import org.apache.openejb.junit.jee.EJBContainerRunner;
    import org.apache.openejb.junit.jee.config.Properties;
    import org.apache.openejb.junit.jee.config.Property;
    import org.junit.Assert;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    @Properties({
        @Property(key = "gacePU.provider", value = "org.eclipse.persistence.jpa.PersistenceProvider"),    
        @Property(key = "gaceDataSource", value = "new://Resource?type=DataSource"),
        @Property(key = "gaceDataSource.JdbcDriver", value = "org.h2.jdbcx.JdbcDataSource"),
        @Property(key = "gaceDataSource.JdbcUrl", value = "jdbc:h2:file:./data/gaceDB;create=true;INIT=CREATE SCHEMA IF NOT EXISTS GACE;MODE=PostgreSQL;DB_CLOSE_DELAY=-1")
    })
    @RunWith(EJBContainerRunner.class)
    public class BancoServiceTest {
    
       //private static EJBContainer ejbContainer;
        @EJB
        private BancoService candidatoService;
    
        @Test
        public void chamaSaveBancoRepository() {
            Banco bb = new Banco("001", "Banco do Brasil");
            candidatoService.save(bb);
            Assert.assertNotNull(bb);
        }
    }
    
    Reply
    1. rmannibucau Post author

      Not sure why you use gacePU.provider but when I mentionned javax.persistence.provider I really meant “javax.persistence.provider” property.

      Reply
  4. Gilberto

    It works using your suggestion. Fantastic, great work! Now, I don’t need to make local JPA uniting test. Is there any doc for openejb-junit?

    Reply
  5. Gilberto

    One more question: Shouldn’t the EJBContainerRunner class scan my project for any data-source definition? I’ve setup the project one in the web.xml file, but EJBContainerRunner doesn’t it.

    [14:01] gilberto.andrade@BEM-INF007:gace (ejb-test *+%) cat target/gace/WEB-INF/web.xml 
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
        <context-param>
            <param-name>javax.faces.PROJECT_STAGE</param-name>
            <param-value>Development</param-value>
        </context-param>
    ...
        <data-source>
            <name>gaceDataSource</name>
            <class-name>org.h2.jdbcx.JdbcDataSource</class-name>
            <url>jdbc:h2:file:./data/gaceDB;create=true;INIT=CREATE SCHEMA IF NOT EXISTS GACE;MODE=PostgreSQL;DB_CLOSE_DELAY=-1</url>
            <user>gace_user</user>
            <password>gace_user</password>
            <transactional>true</transactional>
            <isolation-level>TRANSACTION_READ_COMMITTED</isolation-level>
            <initial-pool-size>2</initial-pool-size>
            <max-pool-size>10</max-pool-size>
            <min-pool-size>5</min-pool-size>
            <max-statements>0</max-statements>
        </data-source>    
    </web-app>
    
    
    Reply
    1. rmannibucau Post author

      default EJBContainer is without any war location in MODULES (check EJBContainer standard properties) just a classpath deployer so it ignored WEB-INF. Note also there are different flavors like tomee embedded and tomee remote which can be more limited in term of MODULES (no classpath deployment IIRC) but would deploy a real war as any tomee deployment and not just a flat classpath scanning.

      Reply
  6. Gilberto

    Another issue. The container refuses to start when I setup a PostgreSQL DataSource class in the data-source’s class-name sub-element.

    ...
       <data-source>
            <name>gaceDataSource</name>
            <class-name>org.postgresql.xa.PGXADataSource</class-name>
            <url>jdbc:postgresql://srv-banco-new:5433/empreendedor</url>
            <user>gace_user</user>
    ...
    

    The execption:

    Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services – 2.6.2.v20151217-774c696): org.eclipse.persistence.exceptions.DatabaseException
    Internal Exception: java.sql.SQLException: Cannot create JDBC driver of class ‘org.postgresql.xa.PGXADataSource’ for connect URL ‘jdbc:postgresql://srv-banco-new:5433/empreendedor’
    Error Code: 0
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:316)
    at org.eclipse.persistence.sessions.JNDIConnector.connect(JNDIConnector.java:147)
    at org.eclipse.persistence.sessions.DatasourceLogin.connectToDatasource(DatasourceLogin.java:162)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.setOrDetectDatasource(DatabaseSessionImpl.java:207)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.loginAndDetectDatasource(DatabaseSessionImpl.java:760)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:265)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:731)
    … 43 more
    Caused by: java.sql.SQLException: Cannot create JDBC driver of class ‘org.postgresql.xa.PGXADataSource’ for connect URL ‘jdbc:postgresql://srv-banco-new:5433/empreendedor’
    at org.apache.commons.dbcp2.BasicDataSource.createConnectionFactory(BasicDataSource.java:2160)
    at org.apache.openejb.resource.jdbc.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:91)
    at org.apache.commons.dbcp2.BasicDataSource.createDataSource(BasicDataSource.java:2032)
    at org.apache.openejb.resource.jdbc.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:268)
    at org.apache.commons.dbcp2.BasicDataSource.getConnection(BasicDataSource.java:1533)
    at org.eclipse.persistence.sessions.JNDIConnector.connect(JNDIConnector.java:135)
    … 48 more
    Caused by: java.lang.ClassCastException: org.postgresql.xa.PGXADataSource cannot be cast to java.sql.Driver
    at org.apache.commons.dbcp2.BasicDataSource.createConnectionFactory(BasicDataSource.java:2149)
    … 53 more

    I’ve tested the app in Wildfly and Payara – both works. I didn’t find any discussion about it in the tomee user list.

    Reply
    1. rmannibucau Post author

      Not sure the config you use – the html formatting swallowed tags – can be a wrong XA configuration, the website and list has some explanations about it.

      Anyway these kind of questions belongs to the tomee list and you should move them to it.

      Reply

Leave a comment