From b6dff0d53e6fdc9036d4a35b84fa76ef16e6b0fd Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sun, 12 Sep 2021 18:08:27 +0200 Subject: [PATCH] Add LauncherSessionListener example to user guide --- .../asciidoc/user-guide/launcher-api.adoc | 46 +++++++++- .../session/GlobalSetupTeardownListener.java | 83 +++++++++++++++++++ .../test/java/example/session/HttpTests.java | 35 ++++++++ ....platform.launcher.LauncherSessionListener | 1 + 4 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 documentation/src/test/java/example/session/GlobalSetupTeardownListener.java create mode 100644 documentation/src/test/java/example/session/HttpTests.java create mode 100644 documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener diff --git a/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc b/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc index 63a22848b6d0..def262aec5a4 100644 --- a/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc +++ b/documentation/src/docs/asciidoc/user-guide/launcher-api.adoc @@ -130,10 +130,48 @@ programmatically via the `{LauncherConfig}` that is passed to the `{LauncherFact they can be discovered at runtime via Java's `{ServiceLoader}` mechanism and automatically registered with `LauncherSession` (unless automatic registration is disabled.) -For example, an `example.CustomLauncherSessionListener` class implementing -`LauncherSessionListener` and declared within the -`/META-INF/services/org.junit.platform.launcher.LauncherSessionListener` file is loaded -and registered automatically. +A `{LauncherSessionListener}` is well suited for implementing once-per-JVM setup/teardown +behavior since it's called before the first and after the last test in a launcher session, +respectively. The scope of a launcher session depends on the used IDE or build tool but +usually corresponds to the lifecycle of the test JVM. A custom listener that starts an +HTTP server before executing the first test and stops it after the last test has been +executed, could look like this: + +[source,java] +.src/test/java/example/session/GlobalSetupTeardownListener.java +---- +package example.session; + +include::{testDir}/example/session/GlobalSetupTeardownListener.java[tags=user_guide] +---- +<1> Start the HTTP server +<2> Export its dynamic port as a system property for consumption by tests +<3> Stop the HTTP server + +This sample uses the HTTP server implementation from the jdk.httpserver module that comes +with the JDK but would work similarly with any other server or resource. In order for the +listener to be picked up by JUnit Platform, you need to register it as a service by adding +a resource file with the following name and contents to your test runtime classpath (e.g. +by adding the file to `src/test/resources`): + +[source] +.src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener +---- +include::{testResourcesDir}/META-INF/services/org.junit.platform.launcher.LauncherSessionListener[] +---- + +You can now use the resource from your test: + +[source,java] +.src/test/java/example/session/HttpTests.java +---- +package example.session; + +include::{testDir}/example/session/HttpTests.java[tags=user_guide] +---- +<1> Read the port of the server from the system property set by the listener +<2> Send a request to the server +<3> Check the status code of the response [[launcher-api-launcher-discovery-listeners-custom]] ==== Registering a LauncherDiscoveryListener diff --git a/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java new file mode 100644 index 000000000000..9039f683b2a9 --- /dev/null +++ b/documentation/src/test/java/example/session/GlobalSetupTeardownListener.java @@ -0,0 +1,83 @@ +/* + * Copyright 2015-2021 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.session; + +//tag::user_guide[] +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.InetSocketAddress; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import com.sun.net.httpserver.HttpServer; + +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.LauncherSessionListener; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestPlan; + +public class GlobalSetupTeardownListener implements LauncherSessionListener { + + private Fixture fixture; + + @Override + public void launcherSessionOpened(LauncherSession session) { + // Avoid setup for test discovery by delaying it until tests are about to be executed + session.getLauncher().registerTestExecutionListeners(new TestExecutionListener() { + @Override + public void testPlanExecutionStarted(TestPlan testPlan) { + if (fixture == null) { + fixture = new Fixture(); + fixture.setUp(); + } + } + }); + } + + @Override + public void launcherSessionClosed(LauncherSession session) { + if (fixture != null) { + fixture.tearDown(); + fixture = null; + } + } + + static class Fixture { + + private HttpServer server; + private ExecutorService executorService; + + void setUp() { + try { + server = HttpServer.create(new InetSocketAddress(0), 0); + } + catch (IOException e) { + throw new UncheckedIOException("Failed to start HTTP server", e); + } + server.createContext("/test", exchange -> { + exchange.sendResponseHeaders(204, -1); + exchange.close(); + }); + executorService = Executors.newCachedThreadPool(); + server.setExecutor(executorService); + server.start(); // <1> + int port = server.getAddress().getPort(); + System.setProperty("http.server.port", String.valueOf(port)); // <2> + } + + void tearDown() { + server.stop(0); // <3> + executorService.shutdownNow(); + } + } + +} +//end::user_guide[] diff --git a/documentation/src/test/java/example/session/HttpTests.java b/documentation/src/test/java/example/session/HttpTests.java new file mode 100644 index 000000000000..de6f806510d8 --- /dev/null +++ b/documentation/src/test/java/example/session/HttpTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015-2021 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package example.session; + +//tag::user_guide[] +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.HttpURLConnection; +import java.net.URL; + +import org.junit.jupiter.api.Test; + +class HttpTests { + + @Test + void respondsWith204() throws Exception { + String port = System.getProperty("http.server.port"); // <1> + URL url = new URL("http://localhost:" + port + "/test"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + int responseCode = connection.getResponseCode(); // <2> + + assertEquals(204, responseCode); // <3> + } +} +//end::user_guide[] diff --git a/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener new file mode 100644 index 000000000000..f6d297627eeb --- /dev/null +++ b/documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener @@ -0,0 +1 @@ +example.session.GlobalSetupTeardownListener