How to add plugin's dependencies to custom task

plugins

(Sam Brannen) #1

The stand-alone JUnit5Plugin has a compile dependency on junit-console.

This works great when building the JUnit5Plugin, since this brings in the ConsoleRunner used by the plugin as well as other mandatory transitive dependencies.

However, the custom task created by the plugin also requires these same dependencies (as runtime dependencies only), but they are not associated with the task by default.

So, as a work-around, we are currently doing the following when applying the plugin.

// Ensure that the ConsoleRunner is on the classpath
project.dependencies.add("testRuntime", "org.junit:junit-console:5.+")

This works, but it’s ugly for the following reasons.

  1. We don’t want to hard-code any version information into the plugin’s source code.
  2. We would prefer to automatically add all of the plugin’s dependencies to the classpath of the custom task when it executes.
  3. We don’t actually want to add anything to the project’s dependencies if not absolutely necessary.

Sooooo, how can we achieve this in Gradle?

Any tips would be greatly appreciated!


(Schalk Cronjé) #2

Assuming Gradle 2.5+, you will need something in the plugin’s apply method along the lines of:

def conf = configurations.maybeCreate( 'my_junit_conf' ) 
conf.defaultDependencies { deps ->
    deps.add( project.dependencies.create(
        "org.junit:junit-console:5.+") 
    )
}

then in task code somewhere you can access it as

// If you want to work with dependency objects
project.configurations.my_junit_conf.dependencies

// If you want to get hold of the downloaded dependency files themselves
project.configurations.my_junit_conf.files

This approach allows the script author to override your default dependency.


(Stefan Oehme) #3

I’m assuming junit-console is something the user never depends on directly, but which needs to have the same version as the JUnit library that the user depends on? In that case you’ll want to lazily determine the JUnit version that the user selected (by analyzing the test runtime classpath) and then adding that specific version of junit-console to the classpath of the test task.

In my opiniion, the JUnit plugin should not depend on any specific version of JUnit. Otherwise, it would be bound to the release cycle of JUnit instead of being able to improve independently.

I had a look at the plugin and there are other things that could be improved. For instance, the usage of extensions for things that should be task properties instead. Extensions are used for things that are the same for a set of related tasks. But properties like includes/excludes/outputDir should be different for different test task and thus should not be on an extension. Also, the test task should be its own class, so users can create their own instances easily.

Finally, since the task is created in an afterEvaluate block, there is no convenient way for the user to configure it or depend on it. The task should be created early and configured later.


(Sam Brannen) #4

@Schalk_Cronje

Thanks for the tip regarding the custom configuration!

That solves issue #3 above; however, it only partially helps with #1 and unfortunately does not help with #2 at all.

Let me attempt to explain our goals better…

The junit-gradle plugin artifact can (over time) depend on one or more other artifacts and specific versions of those artifacts. Granted, at the moment it only depends directly on a single artifact (i.e., junit-console), but we still don’t want to hard-code any dependency information.

Ideally we would like to be able to query the dependencies of the junit-gradle plugin artifact (e.g., either from the standard dependency information in the POM or from whatever custom configuration the user set up in the build script), iterate over them, and add them to the classpath of the custom task created by the plugin.

And… we don’t want to have to care about how many direct dependencies the junit-gradle plugin has or about their versions.

So, my question is: is such an automatic/robust solution even possible using core Gradle features?


(Sam Brannen) #5

@st_oehme

Thanks: those are a lot of useful tips!

To be honest, no one in the JUnit Team claims to know anything about authoring custom Gradle tasks. So we admit it’s a big hack at the moment. That’s why we are eager for the Gradle team to take it over. :wink:

In any case, we are attempting to produce the simplest thing that is useful for JUnit 5.0 M1.

With regard to your questions, I’ll get back to you on those.

Cheers


(otrosien) #6

You should have a look at how ratpack does it. It’s not hidden from the user, but still just a one-liner when it comes to registering dependencies. https://ratpack.io/manual/current/gradle.html#ratpack_dependencies


(Sam Brannen) #7

@Schalk_Cronje

I ended up going with your suggestion as can be seen here.

That turned out to be the cleanest, simplest solution that meets our minimum requirements.

So we’ll stick with that for the time being… with the hope that the Gradle Team eventually takes it over and does it right. :wink:

Cheers,

Sam


(Sam Brannen) #8

@otrosien

That’s certainly an interesting technique, but it unfortunately does not address any of our current needs.

As I originally stated, we do not want to add anything to a user-defined classpath.

In any case, I’ll keep this technique in mind in case we run into a similar requirement.

thanks


(Sam Brannen) #9

@st_oehme

That’s correct.

Not exactly.

Beginning with JUnit 5, there is a strict separation between the Platform and the programming model. In other words, the JUnit plugin relies on a specific version of the platform modules (which junit-console is a member of), but the plugin is not tied to the version of the JUnit programming being used (e.g., JUnit 4, JUnit 5, or actually any other testing framework that runs on the platform (like Specsy, etc.)).

I hope my above statements clarify that this would not be appropriate (i.e., since there is no direct tie between the platform and the programming model in the user’s test runtime classpath).

We agree wholeheartedly, and that’s why we are very eager for the Gradle Team to take over the plugin!

We have absolutely no plans of supporting or maintaining this plugin. On the contrary, it only exists in order to provide development teams a mechanism for running JUnit 5 tests with Gradle… until the Gradle Team takes it over.

So, although you make some very valid points about how it can be improved, I do not foresee the JUnit Team investing much more time on this interim solution.

But… having said that… the JUnit Team of course welcomes pull requests for improving the JUnit plugin! :wink:

Cheers,

Sam