JBoss Community Archive (Read Only)

Arquillian Old

Integrating Arquillian into a Gradle build

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.

apply from: common

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

If you are using Gradle alongside Maven, you shouldn't set the buildDir to target since Gradle organizes compiled classes different than Maven does, possibly leading to conflicts (Though, the behavior of Gradle can also be customized).

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.

Strategy #1: Container-specific test tasks

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:

  • Weld EE Embedded 1.1

  • Remote JBoss AS 6

  • Embedded GlassFish 3

We'll need three components for each container:

  1. Dependency configuration (scope)

  2. Runtime dependencies

  3. Custom test task

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.

Since you are creating custom test tasks, you likely want to configure the default test task to either exclude Arquillian tests are to use a default container, perhaps Weld EE Embedded in this case.

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
}

Notice we've added an extra resources directory for remote JBoss AS 6 to include the required jndi.properties file. That's a special configuration for the remote JBoss AS containers, though won't be required after Arquillian 1.0.0.Alpha4.

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.

Strategy #2: Test profiles

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.

A Maven profile effectively overrides portions of the build configuration and is activated using a command option (or some other profile activation setting).

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

  • Weld EE Embedded 1.1

  • Remote JBoss AS 6

  • Embedded GlassFish 3

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:

  1. It crosses over into more than one build file

  2. You cannot run the tests in each container in a single build execution

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.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-10 12:18:57 UTC, last content change 2011-04-16 15:38:27 UTC.