How to write a junit test for a task

I’m writing some custom tasks and I’m trying to figure out how to execute these tasks within the context of a junit test. I can get a reference to the task but then how to I execute it (and any doFirst/doLast closures that have been applied to it)?

 public void testXjcGenerateSources() {
  def task = ProjectBuilder.builder().build().task("xjc", type: Xjc)
  assertTrue("Task should be of type Xjc", task instanceof Xjc)

Perhaps this helps: project.tasks.getByName(xjc).execute()

Also, have a look at the javadoc:org.gradle.GradleLauncher class that you can use to programatically launch a Gradle build.

This is safer than ‘task.execute()’ as it ensures all preconditions are met and executes the whole model.

I’ve looked at both the GradleLauncher and the Tooling API. I’m building a bunch of custom plugins using Gradle as my build engine. How do I tell GradleLauncher or the tooling API about the newly-compiled task/plugin classes and get them on the classpath so that GradleLauncher/tooling API can see them so that I can test them?

The GradleLauncher runs in process, so it’s going to use the classloader that loads ‘GradleLauncher’.

As for the tooling API, there’s no way to inject classes atm (there will be in the future in certain circumstances) so you’re left with using ‘buildscript {}’ to inject.

The classes that contain the custom plugins/tasks are not on the classloader that loads the GradleLauncher. The Gradle process is building the custom plugins/tasks and then I want to run a new gradle instance within my test case (with the newly-compiled plugins/tasks) and then test them.

Why can’t you put your plugins/classes on the classpath of the test?

The current instance of gradle that is running, by the time it gets to running the unit tests has already built its classpath correct? At test time I would need to take the plugin/task classes that were built in the compileJava task and then add those classes to the currently-executing gradle’s testRuntime classpath correct? I thought that modifying classpaths was a configuration-time thing and couldn’t be manipulated at execution time. Maybe I just don’t know/understand how to do it.

The basic flow would be this: 1) Fire up gradle to build the project (running gradle/gradlew build). At the end of the compile/assemble task there would be class files/jar file. 2) Get these newly-compiled classes/jar file onto the testRuntime classpath so that when the test task starts executing the classes that were previously compiled-built are now available on the classpath. 3) In the test cases, use the classes to run a build. For this I have a bunch of different build.gradle scripts which I want to point to to execute - each one with a different configuration/execution scenario based on different tests I’m trying to execute. I want my tests to actually run through a build execution - just testing out configuration parameters doesn’t cut it in my opinion. I want to execute a build and then inspect the results.

The “main” classes are always on the test classpath.

See this class from an open source Gradle plugin that uses GradleLauncher in the way I’m describing to run functional tests (which is what you’re describing).

Thanks I’ll take a look. I would be interested to see the calling code of this class as well, since most of the methods take a String… parameter. These methods are just passing along whatever the caller is passing to them. It also looks like this class is dependent on the Spock framework (which I am not familiar with - other than knowing that its a Groovy testing framework).

I see what you mean about the “main” classes being on the test classpath - testRuntime extends testCompile extends compile.

That class is using Spock, but there’s no reason it couldn’t be ported to something else. The Gradle specific code is agnostic.

There’s a test in that project: