Another Arquillian EJB test, this time based on TestNG

In my last post I presented Simple EJB Arquillian test based on JUnit running on managed JBoss AS 7. And in this post I show a similar test only this time it is based on the TestNG testing framework.

I have more experience with TestNG than with JUnit and all my tests are based on it. That is why I have decided to give it a try despite the fact that my preliminary tries failed. My goal was to create one Java EE 6 bean, a simple persistence entity and one TestNG test.

A testing project described in this post is again based on the Maven and targeted on JBoss AS 7. It is only supposed to show the possibilities the Arquillian offers not to act as a real world application.

Step by step

  1. Maven project
  2. Setup pom.xml to support the testing
  3. Create the Entity class
  4. Setup persistence.xml
  5. Create the EJB class
  6. Create the test class
  7. Setup arquillian.xml

Maven project

I recommend creating a special project for the tests, because there are a lot of dependencies needed for testing, but superfluous in the rest of your application. (Yes, surprisingly the same reason as for JUnit)

Setup pom.xml to support the testing

Here is my version of pom.xml. The most important parts are arquillian-bom dependency management and arq-jbossas-managed profile. Dependency in dependency management org.jboss.arquillian:arquillian-bom:1.1.0.Final contains all the essential libraries needed for the launching the test. The arq-jbossas-managed profile is responsible for executing the tests. There is only a change of the testing library and its container.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>cz.effy.server</groupId>
 <artifactId>testng.test</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>test</name>
 <dependencies>
  <dependency>
   <groupId>org.testng</groupId>
   <artifactId>testng</artifactId>
   <version>5.14.6</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.jboss.spec.javax.ejb</groupId>
   <artifactId>jboss-ejb-api_3.1_spec</artifactId>
   <version>1.0.1.Final</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax.enterprise</groupId>
   <artifactId>cdi-api</artifactId>
   <version>1.0-SP4</version>
   <scope>provided</scope>
   <exclusions>
    <exclusion>
     <artifactId>jboss-interceptor-api</artifactId>
     <groupId>org.jboss.interceptor</groupId>
    </exclusion>
    <exclusion>
     <artifactId>jsr250-api</artifactId>
     <groupId>javax.annotation</groupId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.jboss.arquillian.testng</groupId>
   <artifactId>arquillian-testng-container</artifactId>
  </dependency>
  <dependency>
   <groupId>org.jboss.arquillian.protocol</groupId>
   <artifactId>arquillian-protocol-servlet</artifactId>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.hibernate.javax.persistence</groupId>
   <artifactId>hibernate-jpa-2.0-api</artifactId>
   <scope>provided</scope>
   <version>1.0.1.Final</version>
  </dependency>
 </dependencies>
 <dependencyManagement>
  <dependencies>
   <dependency>
    <groupId>org.jboss.arquillian</groupId>
    <artifactId>arquillian-bom</artifactId>
    <version>1.1.0.Final</version>
    <scope>import</scope>
    <type>pom</type>
   </dependency>
  </dependencies>
 </dependencyManagement>
 <build>
  <plugins>
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
     <encoding>UTF-8</encoding>
     <source>1.7</source>
     <target>1.7</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
 <profiles>
  <profile>
   <id>arq-jbossas-managed</id>
   <activation>
    <activeByDefault>true</activeByDefault>
   </activation>
   <dependencies>
    <dependency>
     <groupId>org.jboss.as</groupId>
     <artifactId>jboss-as-arquillian-container-managed</artifactId>
     <version>7.1.1.Final</version>
     <scope>test</scope>
    </dependency>
   </dependencies>
  </profile>
 </profiles>
</project>

Create the Entity class

Just a simple persistence class for a demo purpose.
package cz.effy.server.testng.test;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name = "user")
public class User {

 @Id
 @Column(name = "uid")
 private String uid;

 @Column(name = "email")
 private String email;

 @Column(name = "name")
 private String name;

 public String getUid() {
  return uid;
 }

 public void setUid(String uid) {
  this.uid = uid;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

Setup persistence.xml

When working with the JBoss AS 7, you can easily use its example data source (h2 in memory). Automatic create-drop database mode is my favorite option for repeatable testing.
<?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="primary">
      <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
      <properties>
         <property name="hibernate.hbm2ddl.auto" value="create-drop" />
         <property name="hibernate.show_sql" value="true" />
      </properties>
   </persistence-unit>
</persistence>

Create the EJB class

This EBJ class offers only create and get operations. The only interesting part is the injection of the EntityManager.
package cz.effy.server.testng.test;

import javax.ejb.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Singleton
public class UserBean {

 @PersistenceContext(unitName = "primary")
 EntityManager em;

 public void createUser(User user) {
  em.persist(user);
 }

 public User findUserById(String id) {
  return em.find(User.class, id);
 }

}

Create the test class

Now this is the most important part. Arquillian's TestNG test differs more from its POJOs cousin than JUnit does. You need to extend org.jboss.arquillian.testng.Arquillian class. This presents a certain limitation and needs to be counted with.
The testing archive is very similar to the one presented in the previous post, the only change is including persistence.xml file and the entity. Including beans.xml depends on your preferences (.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml") works exactly the same way).
The test creates a User and than verifies the operation by retrieving him from a database and comparing the name property of the original one to the one retrieved.
package cz.effy.server.testng.test;

import javax.inject.Inject;

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;

public class ProjectTest extends Arquillian {

 @Inject
 private UserBean userBean;

 @Deployment
 public static JavaArchive createNotTestableDeployment() {
  final JavaArchive jar = ShrinkWrap
    .create(JavaArchive.class, "test.jar")
    .addClasses(UserBean.class).addClasses(User.class)
    .addAsResource("META-INF/beans.xml")
    // enable CDI
    .addAsResource("META-INF/persistence.xml");
    // persistence
  return jar;
 }

 @Test
 public void persistenceTest() {
  User usr = new User();
  usr.setEmail("fdsfs");
  usr.setName("test");
  usr.setUid("uid");
  userBean.createUser(usr);

  User newUser = userBean.findUserById("uid");
  Assert.assertNotNull(newUser);
  Assert.assertEquals(usr.getName(), newUser.getName());
 }
}

Setup arquillian.xml

This file contains a configuration for the arquillian container. And as was previously indicated, this one deals with managed JBoss AS 7. You need to replace the jbossHome property with a value for your environment. Windows path looks like this C:\servers\jboss-as-7.1.1.Final.
Only this time, using Servlet 3.0 as the protocol is not really an arbitrary choice, you have to use it. Using the default jmx protocol results in java.lang.ClassNotFoundException of the test class (ProjectTest here) at org.testng.internal.XmlMethodSelector.checkMethod (XmlMethodSelector.java:248) (at least on my configuration).
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/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">
   <!-- Force the use of the Servlet 3.0 protocol with all containers, as it is the most mature --> 
   <defaultProtocol type="Servlet 3.0" />
   <!-- Example configuration for a remote JBoss AS 7 instance -->
   <container qualifier="jboss" default="true">
      <!-- If you want to use the JBOSS_HOME environment variable, just delete the jbossHome property -->
      <configuration>
         <property name="jbossHome">/home/petr/servers/jboss-as-7.1.1.Final</property>
      </configuration>
   </container>
</arquillian>

Executing the test

The test execution works the same way as the JUnit's. You launch it the same way you launch any other TestNG tests. Just keep in mind that the JBoss AS 7 will be launched in standalone configuration, thus everything located in the deployments folder will be deployed, possibly slowing down the tests.

Possible injection problems

The reason my preliminary tries failed was, that I did not use the arquillian-bom import and although the project compiled just fine, the CDI did not work so the test ended with a NullPointerException at the line using the @Inject(ed) bean. I do believe that you can make it work by declaring all the necessary imports yourself, but I don't think that it is worth the effort.

Conclusion

The configuration for TestNG based testing is very similar to the one for JUnit. The execution time for this test is also a matter of 10 to 15 seconds. The longer execution time is mostly caused by a persistence preparation.
I really enjoyed using the combination of TestNG, Arquillian and JBoss AS 7 and I am looking forward to a real project application.

Comments

  1. This comment has been removed by the author.

    ReplyDelete
  2. Do you have stress with @BeforeClass @AfterClass @BeforeTest and @AfterTest using TestNG?

    ReplyDelete
    Replies
    1. Hello Luis, I do use these annotations from TestNG on a daily basis without encountering any issues. But I have to admit that I do not use them in combination with Arquillian. All my following projects were based on Spring Framework, so my experience with Arquillian did not go beyond research for this post.

      Delete

Post a Comment

Popular posts from this blog

Automatic jsp recompile on Jboss AS 7

Password hashing in Java

Running scripts written in Clojure as an executable using Inlein