Skip to content

Commit

Permalink
Add 'classpath(...)' test suite feature
Browse files Browse the repository at this point in the history
Resolves #47
  • Loading branch information
jjohannes committed Nov 18, 2024
1 parent 1cae623 commit 5ada29e
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Java Module Testing Gradle Plugin - Changelog

## Version 1.5
* [#47](https://github.com/gradlex-org/java-module-testing/issues/47) Add support for Classpath Test Suites

## Version 1.4
* [#2](https://github.com/gradlex-org/java-module-testing/issues/2) New approach to split Module Path and Classpath for whitebox testing
* [#40](https://github.com/gradlex-org/java-module-testing/issues/40) `useJUnitJupiter("")` without version does not fail for empty test directories
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,26 @@ src
| requires org.example.app; // 'main' module into which the tests are patched
| requires org.junit.jupiter.api;
| }
}
```

A whitebox _test source set_ does **not** have a `module-info.java`.
Instead, the _main_ and _test_ classes will be patched together and the test will run in the _main_ module which now includes the test classes as well.
Additional `requires` for the test are defined as shown above.
If the _sources under test_ are located in a different source set (not `main`), this can be configured via `sourcesUnderTest.set("source-set-name")`.

## Classpath Test Suites

An alternative variant of "whitebox" testing is to run testing on the classpath and ignore **all** module information.
This is what [Gradle does without this plugin](https://docs.gradle.org/current/userguide/java_testing.html#whitebox_unit_test_execution_on_the_classpath).
By default, this plugin replaces this with the [Whitebox Test Suite setup](#whitebox-test-suites), which should be the preferred testing approach.
If you still need to use the classpath-based setup for a Test Suite, you may configure it as follows:

```
javaModuleTesting.classpath(testing.suites["test"])
```

A reason to do testing like this is if you need to utilise testing libraries (e.g. for mocking) that do not work with the Module System at all.

# What does the plugin do?

The plugin rewires the inputs of test compilation (`:compileTestJava`) and test runtime (`:test`).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.gradlex.javamodule.testing;

import org.gradle.api.Action;
import org.gradle.api.Describable;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
Expand Down Expand Up @@ -98,6 +99,24 @@ public void whitebox(TestSuite jvmTestSuite) {
whitebox(jvmTestSuite, NO_OP_ACTION);
}

/**
* Turn the given JVM Test Suite into a Classpath Test Suite.
* For example:
* <p>
* javaModuleTesting.classpath(testing.suites["test"])
* <p>
* This restores the default behavior of Gradle to run tests on the Classpath if
* no 'module-info.java' is present in the source folder of the given test suite.
*
* @param jvmTestSuite the JVM Test Suite to configure
*/
@SuppressWarnings("unused")
public void classpath(TestSuite jvmTestSuite) {
if (jvmTestSuite instanceof JvmTestSuite) {
revertJvmTestSuiteForWhitebox((JvmTestSuite) jvmTestSuite);
}
}

/**
* Turn the given JVM Test Suite into a Whitebox Test Suite.
* If needed, configure additional 'requires' and open the
Expand Down Expand Up @@ -249,4 +268,25 @@ private void configureJvmTestSuiteForWhitebox(JvmTestSuite jvmTestSuite, Whitebo
}
});
}

/**
* Resets changes performed in 'configureJvmTestSuiteForWhitebox' to Gradle defaults.
*/
private void revertJvmTestSuiteForWhitebox(JvmTestSuite jvmTestSuite) {
TaskContainer tasks = project.getTasks();
SourceSet testSources = jvmTestSuite.getSources();

tasks.named(testSources.getCompileJavaTaskName(), JavaCompile.class, compileJava -> {
compileJava.setClasspath(testSources.getCompileClasspath());
compileJava.getOptions().getCompilerArgumentProviders().removeIf(p -> p instanceof WhiteboxTestCompileArgumentProvider);
compileJava.getActions().removeIf(a -> a instanceof Describable
&& JavaCompileSetModulePathAction.class.getName().equals(((Describable) a).getDisplayName()));
});

tasks.named(testSources.getName(), Test.class, test -> {
test.setClasspath(testSources.getRuntimeClasspath());
test.setTestClassesDirs(testSources.getOutput().getClassesDirs());
test.getJvmArgumentProviders().removeIf(p -> p instanceof WhiteboxTestRuntimeArgumentProvider);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.gradlex.javamodule.testing.internal.actions;

import org.gradle.api.Action;
import org.gradle.api.Describable;
import org.gradle.api.NonNullApi;
import org.gradle.api.Task;
import org.gradle.api.file.FileCollection;
Expand All @@ -28,11 +29,16 @@
import java.util.List;

@NonNullApi
public abstract class JavaCompileSetModulePathAction implements Action<Task> {
public abstract class JavaCompileSetModulePathAction implements Action<Task>, Describable {

@Inject
protected abstract JavaModuleDetector getJavaModuleDetector();

@Override
public String getDisplayName() {
return JavaCompileSetModulePathAction.class.getName();
}

@Override
public void execute(Task task) {
JavaCompile javaCompile = (JavaCompile) task;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.gradlex.javamodule.testing.test

import org.gradle.testkit.runner.TaskOutcome
import org.gradlex.javamodule.testing.test.fixture.GradleBuild
import spock.lang.Specification

class ClasspathSuiteTest extends Specification {

@Delegate
GradleBuild build = new GradleBuild()

def "can configure classpath test suite"() {
given:
appBuildFile << '''
javaModuleTesting.classpath(testing.suites["test"])
'''
appModuleInfoFile << '''
module org.example.app {
}
'''

when:
def result = runTests()

then:
result.output.contains('Main Module: null')
result.output.contains('Test Module: null')
result.task(':app:test').outcome == TaskOutcome.SUCCESS
}
}

0 comments on commit 5ada29e

Please sign in to comment.