Document Version 1.0
Copyright © 2012-2013 beijing.beijing.012@gmail.com
Keywords:
JEE integration test, JEE unit test, arquillian sample, JBoss 5, JBoss 7, EJB test, arquillian dependencies, arquillian enbedded container, managed container, remote container, arquilllian TestNG
Table of Contents
1. Create a simple Maven project in Eclipse
2. Create project's pom.xml file
2.1 Replace the JUnit dedendency
2.2 Replaced the "arquillian-junit-container dedendency
3. Implement source code and test code
4. persistence.xml
5. java:/DefaultDS
6. arquillian.xml
7. Configuring TestNG "testng.xml"
8. Run test
1. Create a simple Maven project in Eclipse
2. Create project's pom.xml file
2.1 Replace the JUnit dedendency
2.2 Replaced the "arquillian-junit-container dedendency
3. Implement source code and test code
4. persistence.xml
5. java:/DefaultDS
6. arquillian.xml
7. Configuring TestNG "testng.xml"
8. Run test
- the various posibilities TestNG provides to configure and group tests.
- the facilities of TestNG to test thread and concurrencies
Having shown Arquillian test with JUnit in the past, in this post we will see :
- how to switch from JUnit to TestNG with Arquillian
- JPA / Entity Bean Testing with Arquillian.
1. Create a simple Maven project in Eclipse
Follow the section 1 of "EJB3 Integration Test with Arquillian part1-JBoss 7 managed container" to create a new Eclipse project "testarq_jb5_managed_testng". Be sure in step2, to name the ArtifactId as "testarq_jb5_managed_testng".
2. Create project's pom.xml file
Follow the section 1 of "EJB3 Integration Test with Arquillian part1-JBoss 7 managed container" to create a new Eclipse project "testarq_jb5_managed_testng". Be sure in step2, to name the ArtifactId as "testarq_jb5_managed_testng".
2. Create project's pom.xml file
The "pom.xml" for TestNG is just slightly different from that of Turtorial part - 3:
2.1 Replace the JUnit dedendency:
with TestNG dependency:
2.2 Replaced the "arquillian-junit-container dedendency:
with "arquillian-testng-container":
3. Implement source code and test code
As code sample we will take a typical use case in JEE application:
A business layer/class uses a DAO (data access object) to access business data. Here business class and DAO class are both stateless session bean.
To implement and test the above use case, we need to create following classes in the "testarq_jb5_managed_testng" project:
2 for bussiness classes:
GrussGottEjbLocal.java
GrussGottEjb.java
2 for DAO classes:
UserDaoLocal.java
UserDao.java
The User Entity class:
and the test class:
User.java
And the test class:
TestGrussGott.java
Now the "testarq_jb5_managed_testng" project in Eclipse will looks like:
UserDaoLocal.java
@Local
public interface UserDaoLocal {
public void persist(User halloEntity);
public User findById(Long id);
public User findByName(String name);
}
UserDao.java
GrussGottEjbLocal.java
package test.ejb;
import javax.ejb.EJB;
import javax.ejb.Stateless;
@Stateless
public class GrussGottEjb implements GrussGottEjbLocal {
@EJB
UserDaoLocal userDao;
@Override
public void newUser(User user) {
userDao.persist(user);
}
@Override
public User findUserByName(String name) {
return userDao.findByName(name);
}
}
TestGrussGott.java
@EJB(mappedName = "test/GrussGottEjb/local")
// injecting em does not work for jb5 + arquillian, just use DAOs and
@Test
User result = grusEjb.findUserByName("hehe");
System.out.println("#### found db entry text= " + result.getName()
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
<persistence-unit name="testPU">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
5. java:/DefaultDS
The "persistence.xml" also contains data source configuration:
As data source will use the embedded hsqldb. The hsqldb is included in JBoss 5.1.0.
We just assume:
6. arquillian.xml
arquillian.xml should also be put in the folder "src/test/resources" :
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="jboss5" default="true">
<configuration>
<property name="jbossHome">/home/he/programms/jboss-5.1.0.GA</property>
<property name="profileName">default</property>
<property name="javaVmArguments">-Xmx512m -XX:MaxPermSize=256m</property>
<!-- unfountunally, change system properties, i.e. the -D properties does not work: here the loglevel change doen't work. Nor does it work with adding the -D param in above javaVmArguments. As workaround, just change the log appender threadhold in JBoss directly.
-->
<property name="jboss.server.log.threshold">INFO</property>
</configuration>
</container>
</arquillian>
7. Configuring TestNG "testng.xml"
We still need to configuration TestNG. TestNG provides the conveniece to group tests, and decide which tests are to be executed with changing the test classes.
We will create a "testng.xml" file in ""src/test/resources":
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite 1">
<test name="testNG test1">
<classes>
<class name="test.ejb.TestGrussGott" />
</classes>
</test>
</suite>
> mvn clean test -Parquillian-jbossas-managed_5_1
When you have followed all the steps till now, you would have seen test result like this in the console:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running test.ejb.TestGrussGott
Configuring TestNG with: org.apache.maven.surefire.testng.conf.TestNGMapConfigurator@1cb25f1
30.12.2012 07:43:47 org.jboss.arquillian.container.impl.MapObject populate
WARNUNG: Configuration contain properties not supported by the backing object org.jboss.arquillian.container.jbossas.managed_5_1.JBossASConfiguration
Unused property entries: {jboss.server.log.threshold=INFO}
Supported property names: [shutdownTimeoutInSeconds, javaVmArguments, javaHome, partition, jbossHome, bindAddress, httpPort, startupTimeoutInSeconds, useRmiPortForAliveCheck, portBindingSet, profileName, rmiPort]
Starting server "default", with command (start timeout is 120 seconds ):
/usr/java/jdk1.6.0_32/bin/java -cp /home/he/programms/jboss-5.1.0.GA/bin/run.jar:/usr/java/jdk1.6.0_32/lib/tools.jar -Xmx512m -XX:MaxPermSize=256m -Djbosstest.udp.ip_ttl=0 -Djava.endorsed.dirs=/home/he/programms/jboss-5.1.0.GA/lib/endorsed -Djboss.server.log.threshold=DEBUG -Djava.endorsed.dirs=/home/he/programms/jboss-5.1.0.GA/lib/endorsed/ -Dxb.builder.useUnorderedSequence=true org.jboss.Main -c default -b localhost -g 13bea8d5080
Server started.
log4j:WARN No appenders could be found for logger (org.jboss.security.SecurityAssociation).
log4j:WARN Please initialize the log4j system properly.
hgrussEjb.jar:
/META-INF/
/META-INF/persistence.xml
/test/
/test/ejb/
/test/ejb/GrussGottEjbLocal.class
/test/ejb/UserDao.class
/test/ejb/GrussGottEjb.class
/test/ejb/TestGrussGott.class
/test/ejb/UserDaoLocal.class
/test/ejb/User.class
Shutting down server: default
shutdownTimeout will be=45
Server stopped.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 162.169 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3:16.269s
[INFO] Finished at: Sun Dec 30 07:46:25 CET 2012
[INFO] Final Memory: 28M/167M
[INFO] ------------------------------------------------------------------------
2.1 Replace the JUnit dedendency:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
with TestNG dependency:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.1.1</version>
<scope>test</scope>
</dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.1.1</version>
<scope>test</scope>
</dependency>
2.2 Replaced the "arquillian-junit-container dedendency:
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<version>${version.arquillian_core}</version>
<scope>test</scop>
</dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<version>${version.arquillian_core}</version>
<scope>test</scop>
</dependency>
with "arquillian-testng-container":
<dependency>
<groupId>org.jboss.arquillian.testng</groupId>
<artifactId>arquillian-testng-container</artifactId>
<version>${version.arquillian_core}</version>
</dependency>
<groupId>org.jboss.arquillian.testng</groupId>
<artifactId>arquillian-testng-container</artifactId>
<version>${version.arquillian_core}</version>
</dependency>
3. Implement source code and test code
As code sample we will take a typical use case in JEE application:
A business layer/class uses a DAO (data access object) to access business data. Here business class and DAO class are both stateless session bean.
To implement and test the above use case, we need to create following classes in the "testarq_jb5_managed_testng" project:
2 for bussiness classes:
GrussGottEjbLocal.java
GrussGottEjb.java
2 for DAO classes:
UserDaoLocal.java
UserDao.java
The User Entity class:
and the test class:
User.java
And the test class:
TestGrussGott.java
Now the "testarq_jb5_managed_testng" project in Eclipse will looks like:
User.java
package test.ejb;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, unique = true)
private String name;
// private Long version;
@Version
private long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, unique = true)
private String name;
// private Long version;
@Version
private long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
UserDaoLocal.java
package test.ejb;
import javax.ejb.Local;@Local
public interface UserDaoLocal {
public void persist(User halloEntity);
public User findById(Long id);
public User findByName(String name);
}
UserDao.java
package test.ejb;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Stateless
public class UserDao implements UserDaoLocal {
@PersistenceContext
EntityManager em;
@Override
public void persist(User halloEntity) {
em.persist(halloEntity);
}
@Override
public User findById(Long id) {
return em.find(User.class, id);
}
@Override
public User findByName(String name) {
String qfind = "select x from User x where x.name= :name";
Query findByName= em.createQuery(qfind);
findByName.setParameter("name", name);
return (User)findByNamet.getSingleResult();
}
}
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Stateless
public class UserDao implements UserDaoLocal {
@PersistenceContext
EntityManager em;
@Override
public void persist(User halloEntity) {
em.persist(halloEntity);
}
@Override
public User findById(Long id) {
return em.find(User.class, id);
}
@Override
public User findByName(String name) {
String qfind = "select x from User x where x.name= :name";
Query findByName= em.createQuery(qfind);
findByName.setParameter("name", name);
return (User)findByNamet.getSingleResult();
}
}
GrussGottEjbLocal.java
package test.ejb;
import javax.ejb.Local;
@Local
public interface GrussGottEjbLocal {
public void newUser(User use);
public User findUserByName(String name);
}
import javax.ejb.Local;
@Local
public interface GrussGottEjbLocal {
public void newUser(User use);
public User findUserByName(String name);
}
GrussGottEjb.java
package test.ejb;
import javax.ejb.EJB;
import javax.ejb.Stateless;
@Stateless
public class GrussGottEjb implements GrussGottEjbLocal {
@EJB
UserDaoLocal userDao;
@Override
public void newUser(User user) {
userDao.persist(user);
}
@Override
public User findUserByName(String name) {
return userDao.findByName(name);
}
}
TestGrussGott.java
package test.ejb;
import javax.ejb.EJB;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
*
* @author he
*
*/
// @RunWith(Arquillian.class)
public class TestGrussGott extends Arquillian {
@Deployment
public static JavaArchive createDeployment() {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "hgrussEjb.jar");
jar.addPackage(User.class.getPackage());
jar.addAsResource("test-persistence.xml", "META-INF/persistence.xml");
// jar.addClass(GrussGottEjbLocal.class).addClass(GrussGottEjb.class);
// jar.addClass(UserDao.class).addClass(UserDaoLocal.class);
// jar.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
System.out.println(jar.toString(true));
return jar;
}
@EJB(mappedName = "test/GrussGottEjb/local")
GrussGottEjbLocal grusEjb;
// injecting em does not work for jb5 + arquillian, just use DAOs and
// business methods for inserting, deleting of entities
// @PersistenceContext
// EntityManager em;
@Test
public void sayHalloTxt() throws Exception {
User hehe = new User();
hehe.setName("hehe");
grusEjb.newUser(hehe);
User result = grusEjb.findUserByName("hehe");
Assert.assertEquals("hehe", result.getName());
System.out.println("#### found db entry text= " + result.getName()
+ " id=" + result.getId());
}
}
Explaination to "TestGrussGott.java"
- Different from Arquillian JUnit test, "@RunWith(Arquillian.class)", is not supported by TestNG. For TestNG , test class should subclass "org.jboss.arquillian.testng.Arquillian".
- Instead of adding class one by one to test archive, we add all classes in a package using " jar.addPackage(package_name)"
- For JPA / Hibernate, test archive needs a "persistence.xml" file. This file should be added to test archive's "META-INF" folder. The line " jar.addAsResource("test-persistence.xml", "META-INF/persistence.xml");" says:
"add test-persistence.xml to test archive in META-INF folder, and rename the file to persistence.xml".
- Use of EntityManager in test class
Whe you are familiar with persistence test with Springframework, you might remember, that you could inject an EntityManager in Spring test class, and use EntityManager to prepare test data. But unfortunately, for currently, Arquillian 1.0.3.Final + arquillian-jbossas-managed-5.1, does not support such a feature.
4. persistence.xml
As mentioned in section 3, Arquillian needs a "persistence.xml" for JPA / Hibernate to work. Such a "persistence.xml" is created only for test purpose, and is normally different from the one used in production environment.
We will create a "test-persistence.xml" file in "src/test/resources" folder. Arquillian takes care of renameing it to "persistence.xml" and packing this it in the deplyoment archive.
! You may wunder why we don't just name the original file as "persistence.xml"?
The answer is, we just want to avoid this file been taken mistakenly to be used for production environment.
The content of test-persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
<persistence-unit name="testPU">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
The "persistence.xml" also contains data source configuration:
<jta-data-source>java:/DefaultDS</jta-data-source>
As data source will use the embedded hsqldb. The hsqldb is included in JBoss 5.1.0.
We just assume:
- you have a "hsqldb-ds.xml" in "jbossHome/server/default/deploy",
- the data source configuration works
- the data source is bund to jndi "java:/defaultDS"
6. arquillian.xml
arquillian.xml should also be put in the folder "src/test/resources" :
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="jboss5" default="true">
<configuration>
<property name="jbossHome">/home/he/programms/jboss-5.1.0.GA</property>
<property name="profileName">default</property>
<property name="javaVmArguments">-Xmx512m -XX:MaxPermSize=256m</property>
<!-- unfountunally, change system properties, i.e. the -D properties does not work: here the loglevel change doen't work. Nor does it work with adding the -D param in above javaVmArguments. As workaround, just change the log appender threadhold in JBoss directly.
-->
<property name="jboss.server.log.threshold">INFO</property>
</configuration>
</container>
</arquillian>
7. Configuring TestNG "testng.xml"
We still need to configuration TestNG. TestNG provides the conveniece to group tests, and decide which tests are to be executed with changing the test classes.
We will create a "testng.xml" file in ""src/test/resources":
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite 1">
<test name="testNG test1">
<classes>
<class name="test.ejb.TestGrussGott" />
</classes>
</test>
</suite>
8. Run test
Now everything is set, we are ready to run the test.
Run Maven tests using following command
> mvn clean test -Parquillian-jbossas-managed_5_1
When you have followed all the steps till now, you would have seen test result like this in the console:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running test.ejb.TestGrussGott
Configuring TestNG with: org.apache.maven.surefire.testng.conf.TestNGMapConfigurator@1cb25f1
30.12.2012 07:43:47 org.jboss.arquillian.container.impl.MapObject populate
WARNUNG: Configuration contain properties not supported by the backing object org.jboss.arquillian.container.jbossas.managed_5_1.JBossASConfiguration
Unused property entries: {jboss.server.log.threshold=INFO}
Supported property names: [shutdownTimeoutInSeconds, javaVmArguments, javaHome, partition, jbossHome, bindAddress, httpPort, startupTimeoutInSeconds, useRmiPortForAliveCheck, portBindingSet, profileName, rmiPort]
Starting server "default", with command (start timeout is 120 seconds ):
/usr/java/jdk1.6.0_32/bin/java -cp /home/he/programms/jboss-5.1.0.GA/bin/run.jar:/usr/java/jdk1.6.0_32/lib/tools.jar -Xmx512m -XX:MaxPermSize=256m -Djbosstest.udp.ip_ttl=0 -Djava.endorsed.dirs=/home/he/programms/jboss-5.1.0.GA/lib/endorsed -Djboss.server.log.threshold=DEBUG -Djava.endorsed.dirs=/home/he/programms/jboss-5.1.0.GA/lib/endorsed/ -Dxb.builder.useUnorderedSequence=true org.jboss.Main -c default -b localhost -g 13bea8d5080
Server started.
log4j:WARN No appenders could be found for logger (org.jboss.security.SecurityAssociation).
log4j:WARN Please initialize the log4j system properly.
hgrussEjb.jar:
/META-INF/
/META-INF/persistence.xml
/test/
/test/ejb/
/test/ejb/GrussGottEjbLocal.class
/test/ejb/UserDao.class
/test/ejb/GrussGottEjb.class
/test/ejb/TestGrussGott.class
/test/ejb/UserDaoLocal.class
/test/ejb/User.class
Shutting down server: default
shutdownTimeout will be=45
Server stopped.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 162.169 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3:16.269s
[INFO] Finished at: Sun Dec 30 07:46:25 CET 2012
[INFO] Final Memory: 28M/167M
[INFO] ------------------------------------------------------------------------