├── .gitignore
├── .idea
├── compiler.xml
├── kotlinc.xml
├── libraries
│ └── org_junit_platform_junit_platform_console_standalone_1_0_0.xml
├── misc.xml
├── modules.xml
├── runConfigurations
│ ├── ApplicationMain.xml
│ ├── ConsoleLauncherIntegration.xml
│ └── IntegrationMain.xml
└── vcs.xml
├── .travis.yml
├── README.md
├── application.api-tests
├── application.api-tests.iml
└── test
│ ├── META-INF
│ └── services
│ │ └── foo.bar.api.ApplicationPlugin
│ └── foo
│ └── bar
│ ├── api
│ ├── ApplicationPluginTests.java
│ └── ApplicationVersionTests.java
│ └── internal
│ └── ReverseTests.java
├── application.api
├── application.api.iml
└── src
│ ├── foo
│ └── bar
│ │ ├── api
│ │ ├── ApplicationMain.java
│ │ ├── ApplicationPlugin.java
│ │ └── ApplicationVersion.java
│ │ └── internal
│ │ └── Reverse.java
│ └── module-info.java
├── application.iml
├── integration
├── integration.iml
└── src
│ ├── integration
│ ├── ConsoleLauncherIntegration.java
│ ├── IntegrationTests.java
│ ├── LegacyTest.java
│ └── Uppercase.java
│ └── module-info.java
├── readme-junit5-idea-java9-consolelauncher-integration.png
└── readme-junit5-idea-java9-project-tree-overview.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDEA
2 | .idea/workspace.xml
3 | target/
4 |
5 | # Gradle
6 | .gradle
7 | build/
8 |
9 | # Eclipse
10 | bin/
11 | .classpath
12 | .project
13 | .settings/
14 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/libraries/org_junit_platform_junit_platform_console_standalone_1_0_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/ApplicationMain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/ConsoleLauncherIntegration.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/IntegrationMain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: java
4 | jdk: oraclejdk9
5 |
6 | script:
7 | - java --version && echo "Build script not available, yet"
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # application-junit5-jdk9-demo
2 |
3 | The `application-junit5-jdk9-demo` project demonstrates how to run tests based on
4 | JUnit Platform standalone distribution using IDEA in the a modular environment.
5 |
6 | This sample project does not aim to demonstrate how to use the JUnit Jupiter APIs.
7 | For detailed information on the JUnit Jupiter programming and extension models,
8 | please consult the [User Guide](http://junit.org/junit5/docs/current/user-guide/).
9 |
10 |
11 | ## overview
12 |
13 | 
14 |
15 | This project hosts 2 (two) Java modules **with** module descriptors.
16 |
17 | ### module `application.api`
18 |
19 | ```java
20 | module application.api {
21 |
22 | exports foo.bar.api;
23 |
24 | uses foo.bar.api.ApplicationPlugin;
25 |
26 | provides foo.bar.api.ApplicationPlugin
27 | with foo.bar.internal.Reverse;
28 |
29 | }
30 | ```
31 |
32 | ### module `integration`
33 |
34 | ```java
35 | module integration {
36 |
37 | requires application.api;
38 | requires junit.platform.console.standalone;
39 |
40 | opens integration
41 | to junit.platform.console.standalone;
42 |
43 | provides foo.bar.api.ApplicationPlugin
44 | with integration.Uppercase;
45 |
46 | }
47 | ```
48 |
49 | ### `application.api-tests`
50 |
51 | There's also a simple IDEA module **without** a module descriptor. It contains
52 | the (unit) tests in an old-fashioned but useful manner:
53 |
54 | - no `module-info.java`
55 | - just an IDEA-style `test` source folder with a *test scope* dependency to
56 | `application.api`
57 | - adds `junit-platform-console-standalone-1.0.0-M4.jar` to the module path, which
58 | is automatically transformed into the `junit.platform.console.standalone` module
59 | - tests are defined in same packages as their tested classes: don't fear the
60 | split, package!
61 | - duplicates the service provision information from `application.api` by using
62 | an entry in `META-INF/services` folder
63 |
64 | ## shared run configurations
65 |
66 | https://github.com/sormuras/application-junit5-jdk9-demo/tree/master/.idea/runConfigurations
67 |
68 | ### run ApplicationMain.xml
69 | ```
70 | jdk-9/bin/java
71 |
72 | -p [...]/target/idea/production/application.api
73 |
74 | -m application.api/foo.bar.api.ApplicationMain
75 |
76 | ApplicationVersion 9.123
77 | ApplicationPlugin: class foo.bar.internal.Reverse
78 | '123' -> [Reverse] -> '321'
79 | 'abc' -> [Reverse] -> 'cba'
80 | ```
81 |
82 | ### run IntegrationMain.xml
83 | ```
84 | jdk-9/bin/java
85 |
86 | -p [...]/target/idea/production/integration
87 | [...]/target/idea/production/application.api
88 | [~m2]/junit-platform-console-standalone-1.0.0-M4.jar
89 |
90 | -m application.api/foo.bar.api.ApplicationMain
91 |
92 | ApplicationVersion 9.123
93 | ApplicationPlugin: class foo.bar.internal.Reverse
94 | '123' -> [Reverse] -> '321'
95 | 'abc' -> [Reverse] -> 'cba'
96 | ApplicationPlugin: class integration.Uppercase
97 | '123' -> [Uppercase] -> '123'
98 | 'abc' -> [Uppercase] -> 'ABC'
99 | ```
100 |
101 | ### run ConsoleLauncherIntegration.xml
102 | ```
103 | jdk-9/bin/java
104 |
105 | -p [...]/target/idea/production/integration
106 | [...]/target/idea/production/application.api
107 | [~m2]/junit-platform-console-standalone-1.0.0-M4.jar
108 |
109 | -m integration/integration.ConsoleLauncherIntegration
110 |
111 | --scan-classpath
112 | --classpath target/idea/production/application.api
113 | --classpath target/idea/production/integration
114 |
115 | ```
116 | 
117 |
118 | ## known issues
119 |
120 | - When launching *Run 'All Test'* in `integration` service loading does not work.
121 | - The single IDEA module `application.api-tests` can be configured as a part of
122 | `application.api` excluding `module-info.java` from compilation. I guess.
123 | - Instead of using `--scan-classpath --classpath ... --classpath ...` command
124 | line options, the JUnit Platform ConsoleLauncher should support something like:
125 | `--scan-modulepath`
126 | - Forwarding `integration.ConsoleLauncherIntegration` wrapper should not be
127 | necessary.
--------------------------------------------------------------------------------
/application.api-tests/application.api-tests.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/application.api-tests/test/META-INF/services/foo.bar.api.ApplicationPlugin:
--------------------------------------------------------------------------------
1 | foo.bar.internal.Reverse
--------------------------------------------------------------------------------
/application.api-tests/test/foo/bar/api/ApplicationPluginTests.java:
--------------------------------------------------------------------------------
1 | package foo.bar.api;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | class ApplicationPluginTests {
7 |
8 | @Test
9 | void loadedPluginListIsNotEmpty() {
10 | Assertions.assertFalse(ApplicationPlugin.load().isEmpty(), "nothing's loaded");
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/application.api-tests/test/foo/bar/api/ApplicationVersionTests.java:
--------------------------------------------------------------------------------
1 | package foo.bar.api;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | class ApplicationVersionTests {
7 |
8 | @Test
9 | void majorIsNine() {
10 | Assertions.assertEquals("9", ApplicationVersion.MAJOR);
11 | }
12 |
13 | @Test
14 | void version() {
15 | Assertions.assertEquals("9.123", ApplicationVersion.VERSION);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/application.api-tests/test/foo/bar/internal/ReverseTests.java:
--------------------------------------------------------------------------------
1 | package foo.bar.internal;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | class ReverseTests {
7 |
8 | @Test
9 | void reverse() {
10 | Assertions.assertEquals("123", new Reverse().apply("321"));
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/application.api/application.api.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/application.api/src/foo/bar/api/ApplicationMain.java:
--------------------------------------------------------------------------------
1 | package foo.bar.api;
2 |
3 | import java.util.Arrays;
4 |
5 | public class ApplicationMain {
6 |
7 | public static void main(String[] args) throws Exception {
8 | System.out.println("ApplicationVersion " + ApplicationVersion.VERSION);
9 | for (ApplicationPlugin plugin : ApplicationPlugin.load()) {
10 | System.out.println("ApplicationPlugin: " + plugin.getClass());
11 | apply(plugin, "123");
12 | apply(plugin, "abc");
13 | Arrays.stream(args).forEach(arg -> apply(plugin, arg));
14 | }
15 | }
16 |
17 | static void apply(ApplicationPlugin plugin, String input) {
18 | String name = plugin.getClass().getSimpleName();
19 | String output = plugin.apply(input);
20 | System.out.printf("'%s' -> [%s] -> '%s'%n", input, name, output);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/application.api/src/foo/bar/api/ApplicationPlugin.java:
--------------------------------------------------------------------------------
1 | package foo.bar.api;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.ServiceLoader;
6 | import java.util.function.UnaryOperator;
7 |
8 | public interface ApplicationPlugin extends UnaryOperator {
9 |
10 | static List load() {
11 | List plugins = new ArrayList<>();
12 | for (ApplicationPlugin plugin : ServiceLoader.load(ApplicationPlugin.class)) {
13 | plugins.add(plugin);
14 | }
15 | return plugins;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/application.api/src/foo/bar/api/ApplicationVersion.java:
--------------------------------------------------------------------------------
1 | package foo.bar.api;
2 |
3 | public class ApplicationVersion {
4 |
5 | static final String MAJOR = "9";
6 |
7 | private static final String MINOR = "123";
8 |
9 | public static final String VERSION = MAJOR + "." + MINOR;
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/application.api/src/foo/bar/internal/Reverse.java:
--------------------------------------------------------------------------------
1 | package foo.bar.internal;
2 |
3 | import foo.bar.api.ApplicationPlugin;
4 |
5 | public class Reverse implements ApplicationPlugin {
6 |
7 | @Override
8 | public String apply(String s) {
9 | return new StringBuilder(s).reverse().toString();
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/application.api/src/module-info.java:
--------------------------------------------------------------------------------
1 | module application.api {
2 | exports foo.bar.api;
3 |
4 | uses foo.bar.api.ApplicationPlugin;
5 |
6 | provides foo.bar.api.ApplicationPlugin
7 | with foo.bar.internal.Reverse;
8 | }
9 |
--------------------------------------------------------------------------------
/application.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/integration/integration.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/integration/src/integration/ConsoleLauncherIntegration.java:
--------------------------------------------------------------------------------
1 | package integration;
2 |
3 | import org.junit.platform.console.ConsoleLauncher;
4 |
5 | public class ConsoleLauncherIntegration {
6 | public static void main(String[] args) {
7 | ConsoleLauncher.main(args);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/integration/src/integration/IntegrationTests.java:
--------------------------------------------------------------------------------
1 | package integration;
2 |
3 | import foo.bar.api.ApplicationPlugin;
4 | import foo.bar.api.ApplicationVersion;
5 | import java.util.List;
6 | import java.util.stream.Collectors;
7 | import org.junit.jupiter.api.Assertions;
8 | import org.junit.jupiter.api.Test;
9 |
10 | class IntegrationTests {
11 |
12 | @Test
13 | void versionIsAccessible() {
14 | Assertions.assertEquals("9.123", ApplicationVersion.VERSION);
15 | }
16 |
17 | @Test
18 | void loadedPluginListIsNotEmpty() {
19 | Assertions.assertFalse(ApplicationPlugin.load().isEmpty(), "nothing's loaded");
20 | }
21 |
22 | @Test
23 | void loadedPluginListToStringMatches() {
24 | List actual = ApplicationPlugin.load().stream().map(Object::toString).collect(Collectors.toList());
25 | actual.sort(String::compareTo);
26 | Assertions.assertLinesMatch(List.of("^foo.bar.internal.Reverse.*", "^integration.Uppercase.*"), actual);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/integration/src/integration/LegacyTest.java:
--------------------------------------------------------------------------------
1 | package integration;
2 |
3 | import foo.bar.api.ApplicationMain;
4 |
5 | public class LegacyTest {
6 |
7 | @org.junit.Test
8 | public void testApplicationMain() {
9 | org.junit.Assert.assertEquals("ApplicationMain", ApplicationMain.class.getSimpleName());
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/integration/src/integration/Uppercase.java:
--------------------------------------------------------------------------------
1 | package integration;
2 |
3 | import foo.bar.api.ApplicationPlugin;
4 |
5 | public class Uppercase implements ApplicationPlugin {
6 |
7 | @Override
8 | public String apply(String input) {
9 | return input.toUpperCase();
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/integration/src/module-info.java:
--------------------------------------------------------------------------------
1 | module integration {
2 |
3 | requires application.api;
4 | requires junit.platform.console.standalone;
5 |
6 | opens integration
7 | to junit.platform.console.standalone;
8 |
9 | provides foo.bar.api.ApplicationPlugin
10 | with integration.Uppercase;
11 | }
12 |
--------------------------------------------------------------------------------
/readme-junit5-idea-java9-consolelauncher-integration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sormuras/application-junit5-jdk9-demo/339d1122b7cf64280e9e921e1592a1650bbf8eda/readme-junit5-idea-java9-consolelauncher-integration.png
--------------------------------------------------------------------------------
/readme-junit5-idea-java9-project-tree-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sormuras/application-junit5-jdk9-demo/339d1122b7cf64280e9e921e1592a1650bbf8eda/readme-junit5-idea-java9-project-tree-overview.png
--------------------------------------------------------------------------------