Chapter 5. Testing with Gradle

The discussion of testing with Gradle takes two primary directions. The first is the simple testing of Java classes with existing test frameworks like JUnit and TestNG. The second is a full automation of the testing pipeline, including separating integration tests from unit test and the leveraging of more advanced testing frameworks like Spock and Geb.

JUnit

The simplest JUnit example is almost entirely supplied by the java Gradle plug-in. It adds the test task to the build graph and needs only the appropriate JUnit JAR to be added to the classpath to fully activate test execution. This is demonstrated in Example 5-1.

Example 5-1. Testing Java source with JUnit
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}

The report from the execution of the JUnit tests is quite handsome compared to its non-Gradle counterparts, as you can see in Figure 5-1. It offers a summary of the tests that were executed, succeeded, and failed.

JUnit Test Report
Figure 5-1. JUnit Test Report

When JUnit tests reach a certain level of proliferation within a project, there is a motivation to run them in parallel to get the results faster. However, there would be a great overhead to running every unit test in its own JVM. Gradle provides an intelligent compromise in that it offers a maxParallelForks that governs the maximum simultaneous JVMs that are spawned.

In the same area of testing, but with a different motivation, is the forkEvery setting. Tests, in their quest to touch everything and exercise as much as possible, can cause unnatural pressure on the JVM’s memory allocation. In short, it is what Java developers term a “leak”. It can merely be the loading of every class causing the problem. This isn’t really a leak since the problem stems from the fact that loaded class definitions are not garbage collected but instead are loaded into permgen space. The forkEvery setting causes a test-running JVM to close and be replaced by a brand new one after the specified number of tests have run under an instance.

Though these two settings have very different goals, they may often be seen used in combination when a project has a large battery of tests. The use of forkEvery and maxParallelForks is shown in Example 5-2.

Example 5-2. Testing Java source with multiple JUnit threads
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}

test {
    maxParallelForks = 5
    forkEvery = 50
}

In Example 5-2, we leverage the test closure that was provided by the java plug-in as a scoping syntax for assignment of new values to the forkEvery and maxParallelForks variables.

Example 5-3 executes a Gradle build that uses the two governing parallelism settings as set in Example 5-2.

Example 5-3. Displaying the Gradle worker JVMs with the jps utility
$ gradle test

:createTests
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
> Building > :test > 760 tests completed

When the Example 5-2 tests should spin up multiple JVMs for unit tests, we can validate this behavior by running the jps command as shown in Example 5-4.

Example 5-4. Displaying the Gradle worker JVMs with the jps utility
$ jps

90861 GradleWorkerMain
90862 GradleWorkerMain
90863 GradleWorkerMain
90865 GradleWorkerMain
90864 GradleWorkerMain
90731 GradleMain

We can see that five GradleWorkerMain instances are indeed busy, running tests as per our Example 5-2 specifications.

TestNG

A more recent testing framework, TestNG, has been gaining traction in the Java space as of late. It is also an easy testing framework to use with Gradle. Only the modest addition of the useTestNG() call to the test closure is needed in addition to the refactoring of the unit test class.

Example 5-5. A TestNG Unit Test
package org.gradle.example.simple;

import org.gradle.example.simple.Person;

import org.testng.annotations.*;
import static org.testng.AssertJUnit.*;

public class TestPerson {
  private Person p = null;

  @BeforeClass
  public void setUp() {
    p = new Person();
        p.setAge(20);
        p.setName("Fird Birfle");
        p.setSalary(195750.22);
  }

  @Test(groups = { "fast" })
  public void testPerson() {
      assertEquals(215325.242, p.calculateBonus(), 0.01);
      assertEquals("The Honorable Fird Birfle", p.becomeJudge());
      assertEquals(30, p.timeWarp());
  }

  @Test(groups = { "slow" })
  public void testPersonAgain() {
        Person p = new Person();
        p.setAge(30);
        p.setName("Bird Firfle");
        p.setSalary(32001.99);

        p.wasteTime();
  }
}
Example 5-6. Testing Java source with TestNG
apply plugin: 'java'

repositories {
    mavenCentral()
}

test {
    useTestNG()
}

dependencies {
    testCompile 'org.testng:testng:6.0.1'
}

Lastly, when tests start to bifurcate into both unit and integration tests, which among many differentiators, typically means short and long running respectively, a need to also have the tool respect those separations arises. Gradle handles this with aplomb with a simple specification of a pattern of tests to include in each grouping as shown in Example 5-7.

Example 5-7. Separating tests into unit and integration tests
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}

test {
   include '**/Test*.*'
}

task integrationTest(type: Test, dependsOn: "test") << {
   include '**/IntegrationTest*.*'
}

This same goal of separating unit and integration tests could also be accomplished with a different technique of separate sourceSet entries. The sourceSet and configuration distinction (one for unitTest and one for integrationTest) has JAR dependency precision, but also more verbosity. This is similar to the technique employed by the Gradle plug-in in that it has a distinct set of files (src/main/groovy) and dependencies (like the Groovy core library).

Spock

Spock, as the homepage states, is a “testing and specification framework for Java and Groovy applications.” In a sea of similar tools, it distinguishes itself from the others by a very expressive DSL that reads nearly like natural English writing.

Example 5-8. Spock testing specification
class HelloSpock extends spock.lang.Specification {
  def "length of Spock's and his friends' names"() {
    expect:
    name.size() == length

    where:
    name     | length
    "Spock"  | 5
    "Kirk"   | 4
    "Scotty" | 6
  }
}

A Spock specification, since run under the umbrella of JUnit, can be executed by the mere addition of Spock’s JAR to Gradle’s dependencies.

Example 5-9. Executing Spock test specifications with Gradle
apply plugin: "groovy"

repositories {
  mavenCentral()
}

dependencies {
  groovy("org.codehaus.groovy:groovy-all:1.7.5")
  testCompile "org.spockframework:spock-core:0.5-groovy-1.7"

  // dependencies used by examples in this project
  //  (not required for using Spock)
  testRuntime "com.h2database:h2:1.2.147"
}

The standard results for JUnit executions also apply to Spock, providing a consistent summary of both traditional and specification-style test execution results, as shown in Figure 5-2.

JUnit test report for Spock tests
Figure 5-2. JUnit test report for Spock tests

Geb and EasyB

Geb can be thought of as the furthest reaches of test automation on the JVM with Groovy-based control of web browsers for testing what is primarily thought of as a task for Selenium/WebDriver. There are a growing number of developers writing about and demonstrating this powerful combination of driving a browser from JUnit all the way to specification formats like easyB.

Gradle can easily drive a Geb and easyB combination with just a few additional dependencies and an Ant task defintion, as shown in Example 5-10.

Example 5-10. Geb Gradle build file
apply plugin: 'groovy'

repositories {
        mavenCentral()
}

dependencies {
//    groovy 'org.codehaus.groovy:groovy:1.7.10'
    testCompile ('org.easyb:easyb:0.9.8')
        testCompile 'org.codehaus.geb:geb-core:0.5.1'
        testCompile 'org.codehaus.geb:geb-easyb:0.5.1'
        testCompile 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.0a7'
}

test.doLast {
   ant.taskdef(name: "easyb", classname:"org.easyb.ant.BehaviorRunnerTask", classpath: 
        sourceSets.test.runtimeClasspath.asPath)

    ant.easyb( classpath: sourceSets.test.runtimeClasspath.asPath, 
        failureProperty:'easyb_failed' ) {
        report( location:"${project.testResultsDir}/story.html", format:"html" )
        behaviors( dir: "src/test/stories" ) {
            include( name:"**/*.story" )
        }
   }

   ant.fail( if:'easyb_failed', message: 'Failures in easyb stories')
}

The easyB specification file reads like plain English in its execution of web browser driving events and querying of the page responses.

Example 5-11. Geb easyB specification
using "geb"

scenario "scripting style", {

    when "we go to google", {
        go "http://google.com"
    }

    then "we are at google", {
        page.title.shouldBe "Google"
    }

    when "we search for chuck", {
        $("input", name: "q").value("chuck norris")
        $("input", value: "Google Search").click()
    }

    then "we are now at the results page", {
        page.title.shouldEndWith "Google Search"
    }

    and "we get straight up norris", {
        $("li.g", 0).find("a.l").text().shouldStartWith "Chuck Norris"
    }

}

And, at the end of the Geb via easyB execution, a very pleasant results page (Figure 5-3) describes the stories that were run, like a richer version of the JUnit test report.

easyB test report for Geb tests
Figure 5-3. easyB test report for Geb tests

Gradle’s Testing Focus

The review and examples of testing with Gradle shows that the traditional JUnit testing approach is slightly less ceremonious than with other build tools. However, Gradle truly shines in that the more advanced forms of separating integration tests, running Spock specifications, or driving the browser via Geb are handled with absolutely simplicity. As more complex software, larger code bases, and increased automation drive developers toward these more advanced testing tools, Gradle is poised to offer the most convenient use. This will continue to drive Gradle’s adoption as a software craftsperson’s build and automation tool.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset