Arquillian has a lot of extensions which can dramtically cut needed code. An interesting one is the persistence one which simplifies a lot the DBUnit usage.
The sad part is this extension is designed for not embedded containers….but with this tip you’ll be able to use it (so with OpenEJB, OpenWebBeans, Weld…).
The test
We suppose we have a standard JPA application (using JTA mode since we’ll use OpenEJB adapter).
Here is the entity:
@Entity @NamedQuery(name = "findAll", query = "select p from Person p") public class Person { @Id private long id; private String name; public void setId(final long id) { this.id = id; } public long getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
We’ll use an EJB to get an EntityManager:
@Singleton @Lock(LockType.READ) public class PersonBean { @PersistenceContext private EntityManager em; public Collection<Person> people() { return em.createNamedQuery("findAll", Person.class).getResultList(); } public Person findById(final long id) { return em.find(Person.class, id); } public long newPerson(final long id, final String dummy) { final Person p = new Person(); p.setName(dummy); p.setId(id); em.persist(p); em.flush(); return p.getId(); } }
And here is the persistence.xml:
<?xml version="1.0" encoding="UTF-8"?> <persistence 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_2_0.xsd" version="2.0"> <persistence-unit name="test" transaction-type="JTA"> <jta-data-source>testDatabase</jta-data-source> <class>org.superbiz.Person</class> <properties> <property name="openejb.jpa.init-entitymanager" value="true" /> <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)" /> </properties> </persistence-unit> </persistence>
We see we need a testDatabase datasource, we’ll just define it in arquillian.xml:
<?xml version="1.0" encoding="UTF-8"?> <arquillian xmlns="http://jboss.com/arquillian" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> <container default="true" qualifier="openejb-embedded-4"> <configuration> <property name="properties"> testDatabase = new://Resource?type=DataSource </property> </configuration> </container> </arquillian>
Now we just need a test:
@RunWith(Arquillian.class) public class PersonTest { @Deployment public static Archive<?> createDeploymentPackage() { return ShrinkWrap.create(WebArchive.class, "test.war") .addPackage(Person.class.getPackage()) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") .addAsWebInfResource("META-INF/persistence.xml", "persistence.xml"); } @Inject private PersonBean bean; @Test @Transactional(TransactionMode.COMMIT) @UsingDataSet("datasets/person.yml") @ShouldMatchDataSet("datasets/expected-person.yml") public void personBeanTest() throws Exception { final Person p = bean.findById(1); assertNotNull(p); p.setName("foo"); bean.newPerson(2, "dummy"); } }
This test is pretty trivial and mainly done for this post: it populates the database with a person from a YAML DBUnit file, we update this entity, then we persist another person and check we have both people in the database.
Here is the init YAML file:
PERSON: - id: 1 name: bar
And here is the result/expected YAML file:
PERSON: - id: 1 name: foo - id: 2 name: dummy
Normally that’s all. But in embedded case you need some more configuration to get it working.
The tip
Normally you configure completely the persistence extension in the arquillian.xml file. In our case we’ll simulate what does arquillian persistence extension to work and it will replace the arquillian.xml file (but you’ll quickly see you have almost all parameters you could set in arquillian.xml but somewhere else).
First we need to activate server side persistence extension. To do so just create a file META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension containing:
org.jboss.arquillian.persistence.core.container.RemotePersistenceExtension
Then we need to initialize the configuration for all parts of the extension (even if we use all defaults we need to create files since that’s what does arquillian persistence extension and it expects it to be done).
First create a arquillian.extension.persistence.properties file containing the persistence configuration. For instance:
defaultDataSeedStrategy = CLEAN_INSERT defaultDataSource = openejb:Resource/testDatabase dumpData = true
Then do the same for script part of the persistence extension using the file arquillian.extension.persistence.script.properties:
# can contain all script properties sqlStatementDelimiter = NEW_LINE
Then for dbunit in arquillian.extension.persistence.dbunit.properties:
# can contain all dbunit properties caseSensitiveTableNames = true datatypeWarning = true defaultDataSetFormat = YAML datatypeFactory = org.dbunit.ext.hsqldb.HsqldbDataTypeFactory
And finally for JPA cache part of the extension in arquillian.extension.persistence.jpacacheeviction.properties (i used an empty one since i don’t use this part in the test).
Here we are, we simulate the server execution of the extension forcing it to be executed and we manually initialized the configuration used by the server side of the persistence extension. This way arquillian persistence extension will find all the data it needs to be executed.
Hi romain . Please do you gave the source of this example with arquillan and persistence ?
Hi
should be https://github.com/rmannibucau/persistence-with-openejb-and-arquillian
Hi Romain,
This work around uses specific version of openjpa that too it is very old. I am trying to use it along with latest TomEE embedded 8.0.0. When I replace maven coordinates then I get error stating “No Naming Context available.” Please suggest solution.
Thanks
Sudhakar
Hi Sudhakar, this is likely due to the jndi name of the datasource or the default jndi.properties, you can try to replace openejb: by java:openejb/ or set the openejb local initial context factory as global context factory on the jvm.
In any case contacting the user list of tomee can be wise.