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>