JBoss.orgCommunity Documentation

Chapter 8. Build system integration

8.1. Arquillian's active build ingredient
8.2. Integrating Arquillian into a Gradle build
8.2.1. apply from: common
8.2.2. Strategy #1: Container-specific test tasks
8.2.3. Strategy #2: Test profiles
8.3. Integrating Arquillian into an Ant (+Ivy) build

Just because the Arquillian project uses Maven doesn't mean you have to use it to run your Arquillian tests. Arquillian is designed to have seamless integration with JUnit and TestNG without any necessary test framework configuration. That means you can use any build system that has a JUnit or TestNG task to execute your Arquillian test cases. Since most of this guide focuses on using Arquillian in a Maven build, this chapter is going to be about alternative build systems, namely Gradle and Ant.

The secret ingredient required to activate the Arquillian test runner is getting the correct libraries on the classpath. (Often easier said than done). The libraries consist of the Arquillian container integration and the container runtime (for an embedded container) or deployment client (for a remote container).

In general, the steps to incorporate Arquillian into a build, regardless of what build tool you are using, can be summarized as:

If you are only running the Arquillian tests on a single container, this setup is exceptionally straightforward. The challenge comes when you want to run the tests on multiple containers. It's really just a matter of putting the correct libraries on the test classpath, though.

For some build systems, isolating multiple classpath definitions is more tricky than others. For instance, in Maven, you only get one test classpath per run (without using really advanced plugin configuration). You can toggle between different test classpath pairings through the use of profiles. Each profile contains the libraries for a single target container (a combination of the libraries itemized in steps 2 and 3 above). You'll see this strategy used in the Arquillian examples.

Other build tools, such as Gradle, can easily define new test tasks that each have their own, unique classpath. This makes it not only possible to separate out the target containers, but also run the tests against each one in the same build execution. We'll see an example of that later in this chapter. Gradle can also emulate the Maven profile strategy through the use of build fragment imports. We'll also show an example of that approach for contrast.

Gradle is a build tool that allows you to create declarative, maintainable, concise and highly-performing builds. More importantly, in this context, Gradle gives you all the freedom you need instead of imposing a rigid build lifecycle on you. You'll get a glimpse of just how flexible Gradle can be by learning how to integrate Arquillian into a Gradle build.

We'll be contrasting two strategies for running Arquillian tests from Gradle:

  1. Container-specific test tasks

  2. Test "profiles" via build fragment imports

The first strategy is the recommended one since it gives you the benefit of being able to run your tests on multiple containers in the same build. However, the second approach is less esoteric and will be more familiar to Maven users. Of course, Gradle is so flexible that there are likely other solutions for this problem. We invite you to give us feedback if you find a better way (or another way worth documenting).

Let's get the common build stuff out of the way, then dive into the two strategies listed above.

The simplest Gradle build for a Java project is a sparse one line.

apply plugin: JavaPlugin

Put this line into a file named build.gradle at the root of the project, which is the standard location of the Gradle build file. (Perhaps after seeing this configuration you'll understand the reference in the section title).

Next we'll add the Maven Central and JBoss Community repository definitions, so that we can pull down dependent libraries. The latter repository hosts the Arquillian artifacts.

apply plugin: JavaPlugin

repositories {
   mavenCentral()
   mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}

If your SCM (e.g., SVN, Git) is already ignoring the target directory, you may want to move the Gradle build output underneath this folder, rather than allowing Gradle to use it's default build directory, build. Let's add that configuration to the common build logic as well:

apply plugin: JavaPlugin

buildDir = 'target/gradle-build'

repositories {
   mavenCentral()
   mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}

We also recommend that you centralize version numbers at the top of your build to make upgrading your dependency easy. This list will grow as you add other containers, but we'll seed the list for the examples below:

apply plugin: JavaPlugin

buildDir = 'target/gradle-build'

libraryVersions = [
   junit: '4.8.1', arquillian: '1.0.0.Alpha4', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
   slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]

...

We also need to add the unit test library (JUnit or TestNG) and the corresponding Arquillian integration:

dependencies {
   testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
   testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian
}

In this example, we'll assume the project is compiling against APIs that are provided by the target container runtime, so we need to add a dependency configuration (aka scope) to include libraries on the compile classpath but excluded from the runtime classpath. In the future, Gradle will include support for such a scope. Until then, we'll define one ourselves in the configurations closure.

configurations {
   compileOnly
}

We also need to add the dependencies associated with that configuration to the compile classpaths using the sourceSets closure:

sourceSets {
   main {
      compileClasspath = configurations.compile + configurations.compileOnly
   }
   test {
      compileClasspath = compileClasspath + configurations.compileOnly
   }
}

Here's the Gradle build all together now:

apply plugin: JavaPlugin

buildDir = 'target/gradle-build'

libraryVersions = [
   junit: '4.8.1', arquillian: '1.0.0.Alpha3', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
   slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]

repositories {
   mavenCentral()
   mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}

configurations {
   compileOnly
}

sourceSets {
   main {
      compileClasspath = configurations.compile + configurations.compileOnly
   }
   test {
      compileClasspath = compileClasspath + configurations.compileOnly
   }
}

Now that the foundation of a build is in place (or you've added these elements to your existing Gradle build), we are ready to configuring the container-specific test tasks. In the first approach, we'll create a unique dependency configuration and task for each container.

Each project in Gradle is made up of one or more tasks. A task represents some atomic piece of work which a build performs. Examples include compiling classes, executing tests, creating a JAR, publishing an artifact to a repository. We are interested in the executing tests task. But it's not necessarily just a single test task. Gradle allows you to define any number of test tasks, each having its own classpath configuration. We'll use this to configure test executions for each container.

Let's assume that we want to run the tests against the following three Arquillian-supported containers:

We'll need three components for each container:

We'll start with the Weld EE Embedded container. Starting from the Gradle build defined in the previous section, we first define a configuration for the test runtime dependencies.

configurations {
   compileOnly
   weldEmbeddedTestRuntime { extendsFrom testRuntime }
}

Next we add the dependencies for compiling against the Java EE API and running Arquillian tests in the Weld EE Embedded container:

dependencies {
   compileOnly group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi

   testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
   testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian

   // temporarily downgrade the weld-ee-embedded-1.1 container
   weldEmbeddedTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-weld-ee-embedded-1.1', version: '1.0.0.Alpha3'
   weldEmbeddedTestRuntime group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
   weldEmbeddedTestRuntime group: 'org.jboss.weld', name: 'weld-core', version: libraryVersions.weld
   weldEmbeddedTestRuntime group: 'org.slf4j', name: 'slf4j-log4j12', version: libraryVersions.slf4j
   weldEmbeddedTestRuntime group: 'log4j', name: 'log4j', version: libraryVersions.log4j
}

Finally, we define the test task:

task weldEmbeddedTest(type: Test) {
   testClassesDir = sourceSets.test.classesDir
   classpath = sourceSets.test.classes + sourceSets.main.classes + configurations.weldEmbeddedTestRuntime
}

This task will execute in the lifecycle setup by the Java plugin in place of the normal test task. You run it as follows:

gradle weldEmbeddedTest

Or, more simply:

gradle wET

Now we just repeat this setup for the other containers.

Here's the full build file with the tasks for our three target containers:

apply plugin: JavaPlugin

buildDir = 'target/gradle-build'

libraryVersions = [
   junit: '4.8.1', arquillian: '1.0.0.Alpha4', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
   slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]

repositories {
   mavenCentral()
   mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
   mavenRepo urls: 'http://repository.jboss.org/nexus/content/repositories/deprecated'
}

configurations {
   compileOnly
   weldEmbeddedTestRuntime { extendsFrom testRuntime }
   jbossasRemoteTestRuntime { extendsFrom testRuntime, compileOnly }
   glassfishEmbeddedTestRuntime { extendsFrom testRuntime }
}

dependencies {
   compileOnly group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi

   testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
   testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian

   // temporarily downgrade the weld-ee-embedded-1.1 container
   weldEmbeddedTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-weld-ee-embedded-1.1', version: '1.0.0.Alpha3'
   weldEmbeddedTestRuntime group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
   weldEmbeddedTestRuntime group: 'org.jboss.weld', name: 'weld-core', version: libraryVersions.weld
   weldEmbeddedTestRuntime group: 'org.slf4j', name: 'slf4j-log4j12', version: libraryVersions.slf4j
   weldEmbeddedTestRuntime group: 'log4j', name: 'log4j', version: libraryVersions.log4j

   jbossasRemoteTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-jbossas-remote-6', version: libraryVersions.arquillian
   jbossasRemoteTestRuntime group: 'org.jboss.jbossas', name: 'jboss-as-server', classifier: 'client', version: libraryVersions.jbossas, transitive: false
   jbossasRemoteTestRuntime group: 'org.jboss.jbossas', name: 'jboss-as-profileservice', classifier: 'client', version: libraryVersions.jbossas

   glassfishEmbeddedTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-glassfish-embedded-3', version: libraryVersions.arquillian
   glassfishEmbeddedTestRuntime group: 'org.glassfish.extras', name: 'glassfish-embedded-all', version: libraryVersions.glassfish
}

sourceSets {
   main {
      compileClasspath = configurations.compile + configurations.compileOnly
   }
   test {
      compileClasspath = compileClasspath + configurations.compileOnly
   }
}

task weldEmbeddedTest(type: Test) {
   testClassesDir = sourceSets.test.classesDir
   classpath = sourceSets.test.classes + sourceSets.main.classes + configurations.weldEmbeddedTestRuntime
}

task jbossasRemoteTest(type: Test) {
   testClassesDir = sourceSets.test.classesDir
   classpath = sourceSets.test.classes + sourceSets.main.classes + files('src/test/resources-jbossas') + configurations.jbossasRemoteTestRuntime
}

task glassfishEmbeddedTest(type: Test) {
   testClassesDir = sourceSets.test.classesDir
   classpath = sourceSets.test.classes + sourceSets.main.classes + configurations.glassfishEmbeddedTestRuntime
}

It's now possible to run the Arquillian tests against each of the three containers in sequence using this Gradle command (make sure a JBoss AS is started in the background):

gradle weldEmbeddedTest jbossasRemoteTest glassfishEmbeddedTest

Pretty cool, huh?

Now let's look at another way to solve this problem.

Another way to approach integrating Arquillian into a Gradle build is to emulate the behavior of Maven profiles. In this case, we won't be adding any extra tasks, rather overriding the Java plugin configuration and provided tasks.

Once again, let's assume that we want to run the tests against the following three Arquillian-supported containers:

All we need to do is customize the test runtime classpath for each container. First, let's setup the common compile-time dependencies in the main build file:

apply plugin: JavaPlugin

buildDir = 'target/gradle-build'

libraryVersions = [
   junit: '4.8.1', arquillian: '1.0.0.Alpha3', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
   slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]

repositories {
   mavenCentral()
   mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}

configurations {
   compileOnly
}

dependencies {
   group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
}

sourceSets {
   main {
      compileClasspath = configurations.compile + configurations.compileOnly
   }
   test {
      compileClasspath = compileClasspath + configurations.compileOnly
   }
}

We then need to create a partial Gradle build file for each container that contains the container-specific dependencies and configuration. Let's start with Weld EE Embedded.

Create a file named weld-ee-embedded-profile.gradle and populate it with the following contents:

dependencies {
   // temporarily downgrade the weld-ee-embedded-1.1 container
   testRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-weld-ee-embedded-1.1', version: '1.0.0.Alpha3'
   testRuntime group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
   testRuntime group: 'org.jboss.weld', name: 'weld-core', version: libraryVersions.weld
   testRuntime group: 'org.slf4j', name: 'slf4j-log4j12', version: libraryVersions.slf4j
   testRuntime group: 'log4j', name: 'log4j', version: libraryVersions.log4j
}

Here's the partial build file for Remote JBoss AS, named jbossas-remote-profile.gradle:

dependencies {
   testRuntime group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi
   testRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-jbossas-remote-6', version: libraryVersions.arquillian
   testRuntime group: 'org.jboss.jbossas', name: 'jboss-as-server', classifier: 'client', version: libraryVersions.jbossas, transitive: false
   testRuntime group: 'org.jboss.jbossas', name: 'jboss-as-profileservice', classifier: 'client', version: libraryVersions.jbossas
}

test {
   classpath = sourceSets.test.classes + sourceSets.main.classes + files('src/test/resources-jbossas') + configurations.testRuntime
}
And finally the one for Embedded GlassFish, named glassfish-embedded-profile.gradle:
dependencies {
   testRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-glassfish-embedded-3', version: libraryVersions.arquillian
   testRuntime group: 'org.glassfish.extras', name: 'glassfish-embedded-all', version: libraryVersions.glassfish
}

Now we need to import the appropriate partial Gradle build into the main build. The file will be selected based on the value of the project property named profile.

apply plugin: JavaPlugin

buildDir = 'target/gradle-build'

libraryVersions = [
   junit: '4.8.1', arquillian: '1.0.0.Alpha4', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
   slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]

apply from: profile + '-profile.gradle'

repositories {
   mavenCentral()
   mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}

configurations {
   compileOnly
}

dependencies {
   compileOnly group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi

   testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
   testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian
}

sourceSets {
   main {
      compileClasspath = configurations.compile + configurations.compileOnly
   }
   test {
      compileClasspath = compileClasspath + configurations.compileOnly
   }
}

Tests are run in the Weld EE Embedded runtime using this command:

gradle test -Pprofile=weld-ee-embedded

That's pretty much the same experience you get when you use Maven (and a whole heck of a lot simpler).

While the configuration is much simpler using the profiles strategy, there are two things to keep in mind:

If you have a better idea of how to integrate an Arquillian test suite into a Gradle build, we'd love to hear it on the Arquillian discussion forums.

See the CDI subproject of the Arquillian showcase for an Ant+Ivy build example until this section is written.