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
Romain, where is the documentation for these test classes? What versions of OpenEJB support them?
Hi
this is a trunk only feature ATM, doc is still TODO but if you want to contribute it it would be welcomed.
@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?
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.
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.
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.
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.
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.
Reblogged this on Dinesh Ram Kali..