Saturday, December 29, 2012

Tutorial EJB3 Integration Test with Arquillian part4 - JBoss 5 managed container and TestNG

Document  Version 1.0
I prefer TestNG to JUnit mainly for 2 reasons:
  • 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

The "pom.xml" for TestNG is just slightly different from that of Turtorial  part - 3:

2.1 Replace the JUnit dedendency:


<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>       
           
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>
          

with "arquillian-testng-container": 


<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;
}
}


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();
}
}

GrussGottEjbLocal.java

package test.ejb;
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>


5. java:/DefaultDS

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] ------------------------------------------------------------------------







1 comment: