Arquillian makes it easy to test your Java EE application on a real server. However, if you deploy an application, that uses a database to perform its tasks, how do you make sure a database with test entries is available on the target server?
If you set up a “real” database on the server, the tests become hard to understand, because the data that’s used in assertions is not visible directly in the test. You have to know the content of the database to make sense of the tests. In addition, you’ll have to maintain the database and keep the data in a consistent state.
Instead, I would like my tests to create the test data themselves, so that I can see everything I need to know to understand the tests in one place and I don’t have to maintain a database system.
Using EntityManager
My first attempt was to inject an Entity Manager
into my Arquillian test and setup the test data like this:
@RunWith(Arquillian.class)
public class IntegrationTest
{
@PersistenceContext
private EntityManager em;
@Resource
private UserTransaction userTransaction;
private User user;
@Deployment
public static WebArchive createDeployment()
{
return ShrinkWrap
.create(WebArchive.class)
.addClass(User.class)
...
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsResource("arquillian/persistence.xml", "META-INF/persistence.xml");
}
@Before
public void setup() throws Exception
{
user = new User("myuser", "mypassword");
userTransaction.begin();
em.persist(user);
userTransaction.commit();
}
@Test
public void existingUserShouldBeFound()
{
assertThat(
em.find(User.class, "myuser"),
is(user));
}
}
The file arquillian/persistence.xml
looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="myPU">
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>
It simply uses the default datasource (of JBoss EAP in my case) of the application server. So I don’t even have to setup a new datasource for my tests. Hibernate drops and recreates the tables during each deployment, so the tests start with a fresh database.
This test works great, as long as you run it inside the container. The EntityManager
only gets injected into the test, if you deploy it to the application server. However, if you want to test your application from the outside, em
will be null.
@Test
@RunAsClient
public void existingUserShouldBeFound()
{
// some assertions against the API
}
If you add @RunAsClient
to the test method or set the deployment to @Deployment(testable = false)
, the test is run outside the container (e.g. if you want to test a REST API). Dependency injection doesn’t work in this case and the test fails:
java.lang.NullPointerException
at it.macke.arquillian.api.IntegrationTest.setup(IntegrationTest.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
...
Using an initialization observer
If you use Java EE 7 (which I do), there’s an easy way to run code at the start of your application: observers. If you add an observer to your project, that gets called as soon as the application has initialized, you can execute all the needed setup code before the first test is run.
Here’s an example of my observer, that creates the test data in the database after the deployment to the remote server:
@ApplicationScoped
public class TestDataCreator
{
@PersistenceContext
private EntityManager em;
@Transactional
void createTestUser(
@Observes @Initialized(ApplicationScoped.class) final Object event)
{
User user = new User("myuser", "mypassword");
em.persist(user);
}
}
Of course, you need to include the observer in your deployment:
@Deployment
public static WebArchive createDeployment()
{
return ShrinkWrap
.create(WebArchive.class)
.addClass(TestDataCreator.class)
...
}
If you add some logging to TestDataCreator
, you can see that it creates the test data after the deployment to the remote server:
19:28:27,589 INFO [org.hibernate.tool.hbm2ddl.SchemaExport] (ServerService Thread Pool -- 140) HHH000227: Running hbm2ddl schema export
19:28:27,592 INFO [org.hibernate.tool.hbm2ddl.SchemaExport] (ServerService Thread Pool -- 140) HHH000230: Schema export complete
...
19:28:27,916 INFO [it.macke.arquillian.setup.TestDataCreator] (ServerService Thread Pool -- 143) Adding test user: User{username=myuser, password=mypassword}
...
19:28:28,051 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 143) WFLYUT0021: Registered web context: /2e36a3c3-be16-4e00-86c3-598ce822d286
19:28:28,101 INFO [org.jboss.as.server] (management-handler-thread - 29) WFLYSRV0010: Deployed "2e36a3c3-be16-4e00-86c3-598ce822d286.war" (runtime-name : "2e36a3c3-be16-4e00-86c3-598ce822d286.war")
Now, my client test runs successfully, because the needed test data gets created as soon as the application is deployed.
Sorry, I am new to arquillian, but I didn’t got if you used a embedded server now or a remote-server with a modified database.
In fact, my (small) experience is, that this makes a big difference, since I didn’t got it right now to use CDI correctly for a remote Server (I use Glassfish 4.1), while with an embedded server everything works really fine.
Can you tell me your file structure, where your arquillian folder lays in your project and maybe tell a little more details about how to use an external persistence.xml and what’s needed to define and what’s optional but useful from your experience?
Further: did you ever got following exception and solved it?
Best regards
Hi Steven,
first of all: I don't know the exception you posted. Sorry.
In fact, I'm using a remote server for deployment. This server is configured like our production system and I can test my application "in the real world". It's hard to setup an embedded server that behaves just like your real one. In addition to that, not every application server can be embedded (e.g. due to missing drivers).
I simply added an additional
persistence.xml
to my resources folder and have it included by the ShrinkWrap deployment. It's not important where you put the file, as long as you configure it in the deployment (you can set the target path of the file there, e.g.META-INF/persistence.xml
).Best regards, Stefan
Your hint help me to solve a similar situation , was trying to use javax.persistence.sql-load-script-source but was not working,
Is there are a way I can seed the database even before the deployment happens?
Hi Hasnat, I don’t think so – if you want to seed the database programmatically with the EntityManager. You could execute a SQL script before deployment, of course. But that SQL would perhaps be database specific.