lundi 7 juillet 2008

Testing JBoss Seam Persistence Components Outside a Container

Some weeks ago I had to experiment with the Seam Framework. After a lot of errors and research I was able to run unit tests on the Home Components outside a container and I will share my solution here.

Technologies used : Eclipse, Maven, Ant, Seam (EJB3, Hibernate, TestNG), SimpleJNDI and PostgreSQL.

Here is the configuration avec Maven dependencies :
"pom.xml"
<dependencies>
<dependency>
<groupId>javax.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>1.2_02</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.0.1B</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.3.2.GA</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>3.0.0.ga</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
<version>2.0.2.SP1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.3-603.jdbc3</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.8</version>
<classifier>jdk15</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>simple-jndi</groupId>
<artifactId>simple-jndi</artifactId>
<version>0.11.3</version>
<scope>test</scope>
</dependency>
</dependencies>

The dependencies from JBoss need the JBoss repository :

"pom.xml"
<repositories>
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

Next, the configuration of Seam, in the "META-INF" folder in the classpath :
"components.xml"
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:persistence="http://jboss.com/products/seam/persistence"
xmlns:transaction="http://jboss.com/products/seam/transaction"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd
http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.0.xsd
http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.0.xsd">

<core:init debug="true" jndi-pattern="#{ejbName}/local" />

<persistence:entity-manager-factory name="entityManagerFactory" />

<persistence:managed-persistence-context name="entityManager"
entity-manager-factory="#{entityManagerFactory}" />

<transaction:entity-transaction entity-manager="#{entityManager}" />

</components>

"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_1_0.xsd"
version="1.0">

<persistence-unit name="entityManagerFactory"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>test.model.Person</class>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.connection.driver_class"
value="org.postgresql.Driver" />
<property name="hibernate.connection.username"
value="test-seam-user-login" />
<property name="hibernate.connection.password"
value="test-seam-user-password" />
<property name="hibernate.connection.url"
value="jdbc:postgresql://127.0.0.1:5432/test-seam" />
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>

</persistence>

In my case, I put the Seam configuration files in the "test/resources" folder of the Maven project structure, so I have to put an empty "seam.properties" file in the "main/resources" folder.

You also have to configure a JNDI context with the SimpleJNDI API. You have to put the following configuration file in the classpath :
"jndi.properties"
java.naming.factory.initial = org.osjava.sj.memory.MemoryContextFactory

Then, the code. I have a simple Person entity and an associated PersonHome component which only extend the "org.jboss.seam.framework.EntityHome" class. Then, here is the test class :

package test.home;

import javax.el.ELException;

import org.apache.commons.lang.StringUtils;
import org.jboss.seam.mock.SeamTest;
import org.testng.annotations.Test;

import test.model.Person;

@Test

public class PersonHomeTest extends SeamTest {

public PersonHomeTest() {
super();
}

@Test(dependsOnGroups = {
"persistOK"
})
public void testGetNull() throws Exception {
new ComponentTest() {

@Override
protected void testComponents() {
try {
super.setValue("#{personHome.id}", -1);
super.invokeMethod("#{personHome.getInstance}");

assert false;
} catch (final ELException ex) {
assert ex.getMessage().equals(
"org.jboss.seam.framework.EntityNotFoundException"
+ ": entity not found: test.model.Person#-1");
}
}

}.run();
}

@Test(groups = {
"persistOK"
})
public void testPersistOK() throws Exception {
new ComponentTest() {

@Override

protected void testComponents() {
final Person person =
(Person) super.invokeMethod("#{personHome.getInstance}");
person.setAge(20);
person.setGivenName("Arthur");
person.setSurname("Superman");
assert super.invokeMethod("#{personHome.persist}").equals(
"persisted");
}

}.run();
}

@Test(groups = {
"persistOK"
}, dependsOnMethods = {

"testPersistOK"
})
public void testGetOK() throws Exception {
new ComponentTest() {

@Override
protected void testComponents() {
super.setValue("#{personHome.id}", 1);
final Person person =
(Person) super.invokeMethod("#{personHome.getInstance}");
assert person.getAge() == 20;
assert person.getGivenName().equals("Arthur");
assert person.getSurname().equals("Superman");
}

}.run();

}

@Test(dependsOnGroups = {
"persistOK"
})
public void testPersistNoSurname() throws Exception {
new ComponentTest() {

@Override
protected void testComponents() {
try {
final Person person =
(Person) super
.invokeMethod("#{personHome.getInstance}");
person.setAge(15);

person.setGivenName("Max");
assert super.invokeMethod("#{personHome.persist}").equals(
"persisted");
assert false;
} catch (final ELException ex) {
assert ex
.getMessage()
.equals(
"javax.persistence.PersistenceException"
+ ": org.hibernate.PropertyValueException"
+ ": not-null property references a null or transient value"
+ ": test.model.Person.surname");

}
}

}.run();
}

@Test(dependsOnGroups = {
"persistOK"
})
public void testPersistSurnameTooLong() throws Exception {
new ComponentTest() {

@Override
protected void testComponents() {
try {

final Person person =
(Person) super
.invokeMethod("#{personHome.getInstance}");
person.setAge(15);
person.setGivenName("Max");
person.setSurname(StringUtils.repeat("a", 129));
assert super.invokeMethod("#{personHome.persist}").equals(
"persisted");
assert false;
} catch (final ELException ex) {
assert ex.getMessage().equals(
"javax.persistence.PersistenceException"
+ ": org.hibernate.exception.DataException"
+ ": Could not execute JDBC batch update");
}

}

}.run();
}
}


Here a screenshot of the project structure under Eclipse :



It was a bit hard to configure this and I hope it can help some of you to save time. I have an archive of this small test, I can send it to you if you want, only ask me. ;-)

3 commentaires:

Anonyme a dit…

nous voici revenus du week end à st georges passé avec alexis manon ithry chloé et papi
tu nous as peut etre téléphoné
le mariage de m pascaline etait très bien nous avons fait la connaissance de nathalie très sympathique leweek end prochain nous partons abeaulieu mais papa ne sera en vacance qu'a partir du 10 08 et ns partirons ensuite en autriche
nous espérons que tout va toujours bien pour toi, visiblement l'élaboration de ta société avance et tu as du boulot ... tâche de ne pas tout écrire en anglais maintenant que tu es un buissness man péruvien
mille bisous de toute la famille en attente de frâiches nouvelles
maman et chloé

Anonyme a dit…

Je me trompe ou ce n'est pas de l'espagnol ni du français cette partie du blog ? :-D

Anonyme a dit…

would you mind sending me the sample project, please?

Thanks in advance...

loginexception [at] gmail . com