├── .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 | 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 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /.idea/runConfigurations/ConsoleLauncherIntegration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /.idea/runConfigurations/IntegrationMain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 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 | ![](readme-junit5-idea-java9-project-tree-overview.png) 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 | ![](readme-junit5-idea-java9-consolelauncher-integration.png) 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 --------------------------------------------------------------------------------