The art of testing an EJB application – Part III (OpenEJB)
In the last two posts I’ve introduced my approach to testing (both unit and integration) and shown few ways to test bits and pieces of an enterprise application (some naive as well as more mature). This part is dedicated to tests with a fully featured EJB container, bootstrapped by the test suit. I’ll be using OpenEJB, because I’ve been using it for last few weeks in my latest project. I’ll show some basics and cover some of the more troublesome bits and pieces (which were not covered in the documentation or were not that intuitive). I must admit starting with OpenEJB wasn’t easy; in fact it was fairly frustrating experience at some point (which could have been noticed in my tweets). It’s a great a tool, but the learning curve is steep. I’m focusing on OpenEJB cause I’ve been using is the extensively. Nonetheless, it’s not the only way to test EJB application end-to-end. Alternatives are Sping (which handles @EJB annotations pretty neatly) or Arquillian (which is another approach to bootstrap the container). I’ll try to cover the latter in some future posts, but as for now – let’s start with OpenEJB.
There is one downside of OpenEJB – it bootstraps whole container and deploy all beans available in classpath. That’s not a desired behaviour in many cases. Arquillian seems to be more picky when it comes to beans for deployment. OpenEJB has some filtering capabilities as well – I’ll cover those later on in the series.
For something completely different – I thought I’d rather avoid embedding small code snippets, so I decided to host a complete working project at my GitHub space: https://github.com/kubamarchwicki/ejb-testing – feel free to fork.
Hello world! application
This is fairly easy – there are tons of dummy EJB examples. However, I don’t like when examples are too abstract so lets assume we are building a calculator application, with services responsible for calculations and some for printing the results, etc.
public interface CalculatingServiceLocal {
public int add(int... args);
public int subtract(int... args);
public int multiply(int... args);
}
public interface MethodControllerLocal {
public enum Operator {ADD, SUBTRACT, MULTIPLY};
public String calculate(Operator operation, String... args);
}
To test this application we need to add two things to project and write the actual test. First of all it’s required to include OpenEJB in our application, in test scope.
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-core</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>
The actual test looks like this:
public class MethodControllerTest {
MethodControllerLocal controller;
@BeforeClass
public void setup() throws NamingException {
InitialContext ctx = new InitialContext();
controller = (MethodControllerLocal) ctx
.lookup("MethodControllerLocal");
}
@Test
public void notSupportedOperation() {
String calculate = controller.calculate(Operator.MULTIPLY,
new String[] { "1", "2" });
Assert.assertEquals("501 Not Implemented", calculate);
}
//... more test methods
}
For this to successfully run, we need to instruct OpenEJB on how to perform the classpath discovery (see OpenEJB application discovery via classpath manual for details). My personal preference is to use openejb.deployments.classpath.include and openejb.deployments.classpath.exclude as I find it more reliable that putting an empty ejb-jar.xml file in the classpath. In most cases this works smoothly, with a little jinx: OpenEJB goes nuts when classpath entries contains spaces (so if you use Maven and your repository is located in “Documents and Settings” folder). Moving it to a space-free folder fixes the issue.
Beyond a dummy testing
In most cases, most tutorials finish here: a working sample, which has nothing to do with a real life application. In my case it’s more about a working baseline to iterate upon – the day-to-day testing problems are much more complicated and require significantly more effort. There is a long list of problems which you can stumble upon when trying to test real life projects and I intend to cover at least some of those issue. To list a few I’ve encountered:
-
including database in this equation (to test JPA named queries)
-
getting access to an entity manager, to check what actually got persisted
-
using ejb-jar.xml to instantiate vendor specific dependencies (which are not supported by the OpenEJB – like managed beans)
-
using ejb-jar.xml to inject different implementations (without changing the actual application code)
Adding database to this equation
This sounds like a fairly common use case; an enterprise application uses database underneath. I don’t want to test database, persisting etc – I’ll just start with adding it an application. Let’s assume we need to persist calculation results. To do this, we add a basic entity and persistence.xml file to our solution.
@Entity
@Table(name = "CALC_RESULTS_TB")
public class CalculationResult implements Serializable {
private static final long serialVersionUID = -3706396749558191166L;
@Id
@Column(name = "CALC_ID",
unique = true, nullable = false,
precision = 10, scale = 0)
@GeneratedValue(generator = "CALC_ID_SEQ")
@SequenceGenerator(name = "CALC_ID_SEQ", sequenceName = "CALC_ID_SEQ")
private long id;
@Column(name = "OPERATION")
private String operation;
@Column(name = "ELEMENTS")
private String elements;
@Column(name = "RESULT")
private int result;
//getters and setters
}
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="ejb-testing-pu" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<class>pl.marchwicki.ejb.entity.CalculationResult</class>
<properties>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.dialect"
value="org.hibernate.dialect.Oracle9Dialect"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.provider_class"
value="org.jboss.ejb3.entity.TreeCacheProviderHook" />
<property name="treecache.mbean.object_name"
value="jboss.cache:service=EJB3EntityTreeCache" />
<property name="hibernate.cache.region_prefix" value="/ejb-testing" />
<property name="hibernate.ejb.naming_strategy"
value="org.hibernate.cfg.DefaultComponentSafeNamingStrategy"/>
</properties>
</persistence-unit>
</persistence>
Now, let’s wrap it into an ear file and deploy onto JBoss (I’ve used JBoss 4.2.3 – I know it’s an old one, but I happened I had that one installed – so the choice was obvious). On JBoss it works like a charm:
16:06:55,827 INFO [EJBContainer] STARTED EJB: pl.marchwicki.ejb.view.DisplayingService ejbName: DisplayingService 16:06:55,842 INFO [EJBContainer] STARTED EJB: pl.marchwicki.ejb.controllers.MethodController ejbName: MethodController 16:06:55,858 INFO [EJB3Deployer] Deployed: [..] tmp4564005441996180815ear-0.0.1-SNAPSHOT.ear-contents/ejb-0.0.1-SNAPSHOT.jar 16:06:55,858 INFO [EARDeployer] Started J2EE application: file:/C:/_projects/_servers/jboss-4.2.3.GA/server/default/deploy/ear-0.0.1-SNAPSHOT.ear
Unfortunately, when tests are re-executed – everything fails. SNAFU – situation normal. First of all – JBoss uses Hibernate as an JPA implementation, OpenEJB does not (I assume OpenJPA – but I haven’t been using that one as well). Secondly, persistence.xml includes some more sophisticated caching configuration.OpenEJB manual is pretty helpful here: lookup configuring persistence unit in tests. However, more fundamental question should be answered here: what’s better for testing – two separate configurations (production and tests) or sharing same configuration only tuning (overriding it) when required. I prefer the latter – not once I had problems because I forgot updating test configuration with new entities, which cause tests fail. I’d rather satisfy test dependencies (in test scope) and override specific production properties (those related to cache, but also showing fomatted SQL in tests is pretty handy).
Additionally, (a clear coder notice) the setup() method is becoming a little boated with configuration. This can be easily moved to a jndi.properties file in the src/test/resources folder (not only the test suits become more readable, it makes the configuration reusable as well). So the configuration file for the test suit looks like this:
java.naming.factory.initial = org.apache.openejb.client.LocalInitialContextFactory persistence unit ejb-testing-pu.hibernate.cache.provider_class = org.hibernate.cache.EhCacheProvider ejb-testing-pu.hibernate.hbm2ddl.auto = create-drop ejb-testing-pu.hibernate.dialect = org.hibernate.dialect.HSQLDialect ejb-testing-pu.hibernate.show_sql = true ejb-testing-pu.hibernate.format_sql = true ejb-testing-pu.hibernate.use_sql_comments = true ejb-testing-pu.hibernate.jdbc.batch_size = 0 openejb.deployments.classpath.include = .*ejb-testing.* openejb.descriptors.output = true
Now OpenEJB works beautifully. Mission accomplished!
=============================================== Default test Tests run: 3, Failures: 0, Skips: 0 ===============================================
What’s comming next
I know, with this post I’ve only touched the surface of OpenEJB, but that’s just to begin with something bigger. In the next part I plan to tackle ejb-jar.xml – instantiation of vendor specific beans, not handled by the OpenEJB and injecting concrete implementation of a service (when more than one is available in a classpath).