0

I'm using TomEE with Intellij to test my EJB/JPA beans. I saw on this answer that I should use a embedded container to test. I discovered Arquillian on this other answer(from the same question) but as stated on comments, it's difficult to set it up and not user friendly, things beginners like me search for.

Unfortunately I'm not using glassfish-embedded-all dependency as answered, but tomee-embedded. I saw on this official tutorial it should use JTA as well as answered above. But why?

Doing as last link, I was getting this error:

No EJBContainer provider available: no provider names had been found.

Then using a piece of code from @BeforeClass method from this answer. My test is shown below:

    Properties properties = new Properties();
    properties.setProperty(EJBContainer.PROVIDER, "tomee-embedded");
    EJBContainer container = EJBContainer.createEJBContainer(properties);
    AccountDao dao = (AccountDao) container.getContext().lookup("java:global/Test/AccountDao");

Where Test is my application name and AccountDao is my Stateless Bean that I want tested. But now I'm getting this error:

Caused by: org.hsqldb.HsqlException: user lacks privilege or object not found: PG_CLASS

Although I'm not using HSQLDB, I'm having this error. How can I correctly add some postgresql properties to correctly instantiate my Hibernate entityManager? Here's my persistence.xml:

<persistence-unit name="unitName">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <class>entity.PersistentEntity.Account</class>
    <properties>
        <property name="tomee.jpa.factory.lazy" value="true"/>
        <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/click10"/>
        <property name="javax.persistence.jdbc.user" value="postgres"/>
        <property name="javax.persistence.jdbc.password" value="postgres"/>
        <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL82Dialect"/>
        <property name="hibernate.show_sql" value="true"/>
        <property name="hibernate.format_sql" value="true"/>
        <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
</persistence-unit>
rado
  • 5,720
  • 5
  • 29
  • 51

1 Answers1

1

I have had success with using the TomEE embedded container for unit tests. As with any external JUnit resource it can be managed using a @Rule so I have two classes, the Rule class and a wrapper for the Embedded TomEE.

The wrapper class for the TomEE Container class. Configures an embedded derby datasource, and a user so we can test Basic authentication.

/**
 * class for starting an Embedded TomEE server which will scan the classpath and start the application.
 * The configuration configures an InMemory derby database, and tells JPA to create tables based on the Entity annotations
 *
 */
public class EmbeddedTomEE {

    public static final String USERNAME = "aUser";
    public static final String PASSWORD = "aPassword";
    private Container container;

    public void start() {
        Configuration configuration = new Configuration();
        Properties properties = new Properties();
        properties.setProperty("jdbc/UdDB", "new://Resource?type=DataSource");
        properties.setProperty("jdbc/UdDB.jdbcDriver", "org.apache.derby.jdbc.EmbeddedDriver");
        properties.setProperty("jdbc/UdDB.jdbcUrl", "jdbc:derby:memory:udb;create=true");
        properties.setProperty("jdbc/UdDB.username", "SA");
        properties.setProperty("jdbc/UdDB.password", "");
        properties.setProperty("jdbc/UdDB.jtaManaged", "true");
        properties.setProperty("persistence_unit.javax.persistence.schema-generation.database.action", "create");
        properties.setProperty("persistence_unit.javax.persistence.sql-load-script-source", "META-INF/testdata.sql");
        properties.setProperty("rest-persistence_unit.eclipselink.logging.level", "FINE");  //use 'FINE' for JPA logging

        configuration.setProperties(properties);
        // use a random port so we can have TomEE running parallel with tests
        configuration.randomHttpPort();
        configuration.setWebXml("src/main/webapp/WEB-INF/web.xml");

        HashMap<String, String> users = new HashMap<>();
        users.put(USERNAME, PASSWORD);
        configuration.setUsers(users);
        HashMap<String, String> roles = new HashMap<>();
        roles.put("aUser", "user");
        configuration.setRoles(roles);
        container = new Container(configuration).deployClasspathAsWebApp();
    }

    public int getPort() {
        return container.getConfiguration().getHttpPort();
    }

    public void stop() {
        container.close();
    }
}

The JUnit Rule which takes care of starting the embedded TomEE before each test is executed. We also have some logic to avoid the cost of starting and stopping the container with every test. The class also creates a JAX-RS webClient that can be used to make calls to the applications REST services.

/**
 * JUnit rule for running an EmbeddedTomEE in memory. The rule has static state, this is to avoid starting and stopping the embedded container
 * with every test. Every time no test are running we start a timer, which is canceled if another test is started. This way the rule works well for a
 * single test run inside an IDE, and running multiple tests from Maven.
 *
 */
public class EmbeddedTomEERule extends ExternalResource {

    private static EmbeddedTomEE tomEE;
    private static final AtomicInteger count = new AtomicInteger();
    private static Timer timer;

    @Override
    protected void before() throws Throwable {
        startIfNeeded();
        if (timer != null) {
            timer.cancel();
        }
        count.incrementAndGet();
    }

    @Synchronized
    private void startIfNeeded() {
        if (tomEE == null) {
            tomEE = new EmbeddedTomEE();
            tomEE.start();
            Runtime.getRuntime().removeShutdownHook(new Thread(() -> tomEE.stop()));
        }
    }

    @Override
    protected void after() {
        int runningTests = count.decrementAndGet();
        if (runningTests == 0) {
            // stop after some time if no new test are started
            timer = new Timer();
            timer.schedule(new StopEmbeddedContainer(), 10000);
        }
    }

    public int getPort() {
        return tomEE.getPort();
    }

    /**
     * creates a new WebClient that can request data from the specified path
     */
    public WebClient getWebClient(String path, MediaType mediatype) {
        WebClient client = WebClient.create("http://localhost:" + tomEE.getPort() + "/", Collections.singletonList(new JohnzonProvider()),
                EmbeddedTomEE.USERNAME, EmbeddedTomEE.PASSWORD, null)

                .path(path).accept(mediatype);
        return client;
    }

    private static class StopEmbeddedContainer extends TimerTask {
        @Override
        public void run() {
            tomEE.stop();
        }
    }
}

Here is an example of how a Test would look

public class ExampleTest {

    @Rule
    public EmbeddedTomEERule rule = new EmbeddedTomEERule();

    @Test
    public void doTest() {

        WebClient client = rule.getWebClient("some-endpoint", MediaType.APPLICATION_JSON_TYPE);
        Output dto = client.get(Input.class);
    }
}

This type of test allows you to test your application at the HTTP layer, and it allows you to place breakpoints in both test and the server code. Technically it may be a stretch to call this a Unit test, but I prefer this type of test when testing more that one component. Since you need a fully functional TomEE you will need to provide a number of external dependencies, in my case it looked like this:

<dependency>
    <groupId>org.apache.derby</groupId>
    <artifactId>derby</artifactId>
    <version>${derby.db.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomee</groupId>
    <artifactId>openejb-core</artifactId>
    <version>${openejb-core.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomee</groupId>
    <artifactId>openejb-cxf-rs</artifactId>
    <version>${openejb-core.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomee</groupId>
    <artifactId>openejb-server</artifactId>
    <version>${openejb-core.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomee</groupId>
    <artifactId>openejb-rest</artifactId>
    <version>${openejb-core.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomee</groupId>
    <artifactId>tomee-embedded</artifactId>
    <version>${openejb-core.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>el-impl</artifactId>
    <version>2.2</version>
    <scope>test</scope>
</dependency>
Klaus Groenbaek
  • 4,820
  • 2
  • 15
  • 30