Introduction

This short workshop will guide you through creating a Gradle build file for an existing Java application, including building a distributable package and running integration tests.

Setting up

This workshop is based on Eclipse and uses the Buildship plugin which provides support for developing Gradle projects. When writing this workshop, I used Eclipse Mars 2 (4.5.2).

Installing buildship

Note: Newer versions of Eclipse already have Buildship installed, but the version in the Eclipse Marketplace may be newer and have additional bugfixes.

Cloning the workshop

Importing the project

Unfortunately, eclipse won’t give you the option of importing a repository as a gradle project after cloning it, so we have to do it manually.

On the Gradle Tasks view, open gradle-workshop > build and double-click build. In the console view, you should now see the build failing.

Note: if you can’t see the Gradle Tasks view, you can open it by going to Window > Show View > Other… > Gradle > Gradle Tasks

Adding dependencies

Our build does not run because our application requires some dependencies which aren’t declared in the build.gradle file.

We’re going to fetch our dependencies from maven central. If you go to the page of the artifact you want to include, it will helpfully give you a line to add to your gradle file to add the artifact as a dependency of your main code.

Go back to the Java perspective, open build.gradle and add the following code:

repositories {
	mavenCentral()
}

dependencies {
	compile 'org.parboiled:parboiled-java:1.1.7'

	testCompile 'junit:junit:4.12'
}

Note that we use compile for dependencies of our application and testCompile for dependencies of our unit tests. Gradle calls these groupings of dependencies configurations. A list of configurations added by the Java plugin and how they’re used can be found in the user guide.

At this point, even though we’ve added the dependencies to the build.gradle file, Eclipse is still showing compile error markers on our code. To fix this, right-click on the gradle-workshop project > Gradle > Refresh Gradle project. After a few moments while Gradle processes the project and downloads the dependencies, Eclipse should show our project as being error-free.

Now go to the Gradle Tasks view again, open gradle-workshop > build and double-click build. This time you should see the build complete successfully.

But where’s our output? Here again, the user guide will tell us that java artifacts are built in build/libs, but by default this folder is hidden in the project explorer in Eclipse.

Click the down arrow in the Project Explorer view and select Customize view… (or Filters… in the Package Explorer view), uncheck Gradle build folder and click OK.

Now select the gradle-workshop project and press F5 and the jar should show up under build/libs.

Packaging an application

We will now build a user-friendly package for our application so that a user can download a single archive, unpack it and run it like a native application.

Fortunately, Gradle’s Application plugin does most of the work for us here including creating .bat and shell scripts to launch java with the correct classpath to run our app and creating an archive which includes all our library dependencies.

The Application plugin page in the user guide tells us that we must apply the plugin and set the mainClassName property.

In build.gradle, replace the top section of the file with this:

apply plugin: 'java'
apply plugin: 'application'

mainClassName = 'uk.co.azquelt.simplecalc.Main'

From the Gradle tasks view, build the project again.

Select the project and press F5 and you should now see build/distributions/gradle-workshop.zip which contains a zipped up directory with a script to run the application.

Although gradle-workshop is a descriptive name for the project, it’s not a great name for the built application so let’s change that. The application plugin page notes that it adds some properties which we can find on the Project API. This tells us that there is an applicationName property which we can set. Similarly, on the Java Plugin page we find the archivesBaseName property which will control the name of our compiled jar.

Set both of these properties in the gradle.build file:

applicationName = 'simple-calculator'
archivesBaseName = applicationName

Build the project again, and we see that everything is now named simple-calculator instead of gradle-workshop

Running integration tests

Our project has some integration tests under src/integrationTest/java. You might not have noticed them because they’re not mentioned in the build.gradle file and don’t show up as a Java source folder. Let’s fix that and make sure they get run when we do our build.

Adding a source set

First, we need to tell gradle about the source code. The Java plugin has support for additional source sets and will automatically add new tasks and configurations (remember these from earlier?) for each new source set that you add. These are explained further down in the tasks and dependency management sections of the user guide.

Add this section near the top of the build.gradle file. It needs to come before the dependencies block.

sourceSets {
	integrationTest
}

We also need to add the same JUnit dependency to the integrationTestCompile configuration:

dependencies {
	compile 'org.parboiled:parboiled-java:1.1.7'

	testCompile 'junit:junit:4.12'

	integrationTestCompile 'junit:junit:4.12'
}

Now right-click on the project > Gradle > Refresh Gradle project and you should see the new integrationTest source folder appear.

IntegrationTest source folder

Running the tests

However, if we run the build task again, we don’t see the integration tests mentioned at all! This is because although we have some new tasks, they’re not in the task graph so they can’t be executed by the build task.

Let’s have a look at the initial task graph created by the Java plugin (in green) taken from the user guide, along with the new tasks that have been added by the Java plugin for our source set (in orange).

Task graph for Java plugin next to tasks for integration tests

We can clearly see in this diagram that nothing depends on the new tasks so they will never be run unless we request them directly. We need to add them to the existing task graph created by the Java plugin but the next question is where should they go?

Well, the only time we need to build the integration tests is when we’re going to run the integration tests so they should be a dependency of the task that runs the tests.

But wait, we don’t have a task that runs the tests yet! We need to create this new task and then we can link all of our tasks into the task graph like this:

Integration test tasks integrated into task graph

Here’s the snippet you need to add to your build.gradle file to create the integrationTest task:

task integrationTest(type: Test) {
	dependsOn integrationTestClasses
	dependsOn installDist

	testClassesDir = sourceSets.integrationTest.output.classesDir
	classpath = sourceSets.integrationTest.runtimeClasspath
}

Let’s break down what’s going on here.

Using the task keyword, we’re creating a new task named “integrationTest” and we’re using the Test task type because we want to run JUnit tests.

We then need to configure the test task using the methods and properties listed in the Test task type DSL documentation. Here the essential settings are testClassesDir to tell the task where our tests are and classpath to define the classpath needed to run the tests.

We could define both of these manually but it’s easier to pull this information from the source set which we created earlier because Gradle knows where the class files are and has computed the classpath from the integrationTestCompile dependencies.

We look up the source set by name from the sourceSets property and can then call any of the methods on the SourceSet interface to get the information we want.

We also set up the task dependencies needed for our integrationTest task to make sure that everything it needs to run is available. The things we need are:

Lastly, to get our task graph looking the way we want it, we also need to have the check task depend on our new integrationTest task, which we can do by adding this line.

check.dependsOn integrationTest

Here we’re configuring a task which has already been created by the plugin. If we wanted to add more configuration options, we could also use the form

check {
    dependsOn integrationTest
    /* ... more options ... */
}

It’s important to note that when we’re creating a new task, we use the task keyword, but when we’re configuring an existing task we don’t.

Now run the build task again, and you should see it build and run the all the tests.

Note: if your integration tests don’t pass, first check that the name of your application (which you specified earlier) matches the application name in the RunApplication.java file.

Further information

This is the end of the guided workshop, we’ve successfully created a Gradle build file to build our application into a distributable package and run the associated unit and integration tests.

Here are some possible projects if you’d like to explore Gradle further:

Here are some resources that you may find useful: