EJBContainer and JUnit rules, no more need of any Runner! Thanks OpenEJB!


If you read this post you surely know some “common” ways to test EE applications with JUnit using a runner. It often looks like:

@RunWith(CdiTestRunner.class)
public class MyEETestWithDeltaSpike {
    @Inject
    private ACdiBean bean;

    @Test
    public void theTest() {
        // do test
    }
}

or

@RunWith(EJBContainerRunner.class)
public class MyEETestWithOneOpenEJB {
    @Inject
    private ACdiBean bean;

    @Test
    public void theTest() {
        // do test
    }
}

These tests have something in common: they use the classpath to deploy and don’t need something else (compared to Arquillian or ApplicationComposer).

That’s great but you bind your test lifecycle to the specific runner. It means you can’t compose runners and you can’t do anything before the container bootstrap like starting a FTP server with rule-them-all or just using JUnit Theories or Parameterized runners.

Quick reminder about JUnit rules

A JUnit rule allows to decorate a statement (execution of a part of the test). There are two kind of rules:

  • instance rules (@Rule): decorate a test method – @Before/@After like
  • class rules (@ClassRule): decorate the whole test class – @BeforeClass/@AfterClass like

Here a sample:

public class MyTest {
    @ClassRule
    public static final MyClassRule CLASS_RULE = new MyClassRule();

    @Rule
    public final MyTestRule testRule = new MyTestRule(this);

    // ...
}

EJBContainer rules

So how to do it with EJBContainer? Simply with openejb-junit module.

It provides several interesting rules which are mainly replacement for EJBContainerRunner I spoke about last week.

EJBContainerRule

EJBContainerRule allows to handle lifecycle of the container (start/stop). It can be a @ClassRule or @Rule depending if you want to start the container once by class or once by method.

If you pass to the container the test instance (means it is a @Rule only) the test instance we’ll get EE injections for free.

If you use it as a @ClassRule you need to get the injection by another way. The first one is to call inject() from the rule or to simply add as instance rule (@Rule) the InjectRule.

Here some samples:

  • @ClassRule with automatic injections
public class TestEJBContainerRule {
    @ClassRule
    public static final EJBContainerRule CONTAINER_RULE = new EJBContainerRule();

    @Rule
    public final InjectRule injectRule = new InjectRule(this, CONTAINER_RULE);

    // EE injections and tests
}

Side note: of course all EJBContainerRunner features are supported (@Transaction, @Properties…)

  • @ClassRule with manual injections
public class TestEJBContainerRule {
    @ClassRule
    public static final EJBContainerRule CONTAINER_RULE = new EJBContainerRule();

    @Test
    private void doTest() {
        CONTAINER_RULE.inject(this); // can be in @Before
        // do test
    }
}
  • @Rule
public class TestEJBContainerRuleSimpleRule {
    @Rule
    public final EJBContainerRule containerRule = new EJBContainerRule(this);

    // tests, note this is slower since you have one container by test
}

The EJBContainerRule provides one other interesting method “resource()” to lookup a container resource directly from its id.

Last note: container config will be merged with:

  • openejb-junit.properties (from classpath)
  • @PropertyFile
  • @Properties

To finish here is a test with JUnit Theories:

package org.superbiz;

import org.apache.openejb.junit.jee.EJBContainerRule;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

import javax.ejb.EJB;

import static org.junit.Assert.assertEquals;

@RunWith(Theories.class)
public class MultiplierTest {
    @DataPoints
    public static final int[] POINTS = { 1, 2, 3, 4, 5 };

    @ClassRule
    public static final EJBContainerRule rule = new EJBContainerRule();

    @Before
    public void bind() {
        rule.inject(this);
    }

    @EJB // a ejb multiply two integers
    private Multiplier multiplier;

    /**
     * Tests:
     * 1 * 1
     * 1 * 2
     * 1 * 3
     * 1 * 4
     * 1 * 5
     * 2 * 1
     * 2 * 2
     * 2 * 3
     * 2 * 4
     * 2 * 5
     * 3 * 1
     * 3 * 2
     * 3 * 3
     * 3 * 4
     * 3 * 5
     * 4 * 1
     * 4 * 2
     * 4 * 3
     * 4 * 4
     * 4 * 5
     * 5 * 1
     * 5 * 2
     * 5 * 3
     * 5 * 4
     * 5 * 5
     */
    @Theory
    public void run(final int i, final int j) {
        System.out.println(i + " * " + j);
        assertEquals(i * j, multiplier.multiply(i, j));
    }
}

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

Or stay in touch on twitter @rmannibucau

 

Advertisement

9 thoughts on “EJBContainer and JUnit rules, no more need of any Runner! Thanks OpenEJB!

  1. ymaraner

    Romain, where is the documentation for these test classes? What versions of OpenEJB support them?

    Reply
  2. Kinfofmemphis

    @RunWith(CdiTestRunner.class) Doesn’t seem to support @Allternatives even when they are enabled with the beans.xml. i.e. only the @Default bean is ever injected, which is rather annoying… Does @RunWith(EJBContainerRunner.class) support @Alternatives?

    Reply
    1. rmannibucau Post author

      EJBContainerRunner just starts an EJBContainer so yes. BTW CdiTestRunner does too (at least if the underlying CdiCtrl supports it which is the case with openejb and openwebbeans at least) so maybe something wrong on your side.

      Reply
  3. Kinfofmemphis

    Ah great thanks. With CdiTestRunner I’m using OpenWebBeans 1.2.1. Eclipse recognises that the correct @Alternative should be activated (Eclipse CDI tooling mentions the correct bean) but running CdiTestRunner through the IDE just does its thing and uses @Default ignoring the @Alternate in the beans.xml file. I have tried using the file in the main/resources and test/resources META-INF locations (maven) neither activates the @Alternative. I think I’ll need to take another look.

    Reply
    1. Kinfofmemphis

      Found the issue. Issue was that my @Alternative class was in the maven test/java not in main/java. This is rather silly no? One would expect a Test runner (CdiTESTRunner) to actually work with classes in test/java. My @Alternative class is just a test stub so doesn’t really belong in main/java! I miss Spring more and more everyday lol.

      Reply
      1. rmannibucau Post author

        Did you put a META-INF/beans.xml in src/test/resources? If not “what did you expect?” ;).

        PS: i shout after spring everyday since I work with it ;), it allows you to make too nasty and wrong things.

  4. Kinfofmemphis

    Ha! Point taken :). However, for your suggestion above to work, I had to put an empty beans.xml file in src/main/resources/META-INF AND the beans.xml which enables the alternative in src/test/resources/META-INF.

    I think its the precise control of whats going on from Spring that I miss i.e. @ContextConfiguration(“classpath:test-context.xml”). But then again… having 100MB+ war files because of Spring is no fun for deploy cycles either! lol.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s