diff --git a/documentation/modules/ROOT/pages/extensions/exception-handling.adoc b/documentation/modules/ROOT/pages/extensions/exception-handling.adoc index b4eb6d0eb213..563c7efbbd72 100644 --- a/documentation/modules/ROOT/pages/extensions/exception-handling.adoc +++ b/documentation/modules/ROOT/pages/extensions/exception-handling.adoc @@ -10,27 +10,59 @@ and for those thrown during one of test lifecycle methods (`@BeforeAll`, `@Befor The following example shows an extension which will swallow all instances of `IOException` but rethrow any other type of exception. -[source,java,indent=0] .An exception handling extension that filters IOExceptions in test execution +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/exception/IgnoreIOExceptionExtension.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/exception/IgnoreIOExceptionExtension.kt[tags=user_guide] +---- +-- +==== Another example shows how to record the state of an application under test exactly at -the point of unexpected exception being thrown during setup and cleanup. Note that unlike +the point where an unexpected exception is thrown during setup and cleanup. Note that unlike relying on lifecycle callbacks, which may or may not be executed depending on the test status, this solution guarantees execution immediately after failing `@BeforeAll`, `@BeforeEach`, `@AfterEach` or `@AfterAll`. -[source,java,indent=0] .An exception handling extension that records application state on error +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/exception/RecordStateOnErrorExtension.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/exception/RecordStateOnErrorExtension.kt[tags=user_guide] +---- +-- +==== Multiple execution exception handlers may be invoked for the same lifecycle method in order of declaration. If one of the handlers swallows the handled exception, subsequent -ones will not be executed, and no failure will be propagated to JUnit engine, as if the +ones will not be executed, and no failure will be propagated to the JUnit engine, as if the exception was never thrown. Handlers may also choose to rethrow the exception or throw a different one, potentially wrapping the original. @@ -39,8 +71,24 @@ exceptions thrown during `@BeforeAll` or `@AfterAll` need to be registered on a while handlers for `BeforeEach` and `AfterEach` may be also registered for individual test methods. -[source,java,indent=0] .Registering multiple exception handling extensions +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/exception/MultipleHandlersTestCase.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/exception/MultipleHandlersTestCase.kt[tags=user_guide] +---- +-- +==== diff --git a/documentation/modules/ROOT/pages/extensions/intercepting-invocations.adoc b/documentation/modules/ROOT/pages/extensions/intercepting-invocations.adoc index b3090668d4d3..dd7d183ecaf2 100644 --- a/documentation/modules/ROOT/pages/extensions/intercepting-invocations.adoc +++ b/documentation/modules/ROOT/pages/extensions/intercepting-invocations.adoc @@ -6,11 +6,27 @@ test code. The following example shows an extension that executes all test methods in Swing's Event Dispatch Thread. -[source,java,indent=0] .An extension that executes tests in a user-defined thread +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/interceptor/SwingEdtInterceptor.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/interceptor/SwingEdtInterceptor.kt[tags=user_guide] +---- +-- +==== [NOTE] .Accessing the test-scoped `ExtensionContext` diff --git a/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-class-templates.adoc b/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-class-templates.adoc index fa1b7936b10a..0ceafaff6093 100644 --- a/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-class-templates.adoc +++ b/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-class-templates.adoc @@ -9,11 +9,27 @@ will only be used for the next invocation of the `{ClassTemplate}`. The following example shows how to write a class template as well as how to register and implement a `{ClassTemplateInvocationContextProvider}`. +.A class template with an accompanying extension +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] -.A class template with accompanying extension ---- include::example$java/example/ClassTemplateDemo.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ClassTemplateDemo.kt[tags=user_guide] +---- +-- +==== In this example, the class template will be invoked twice, meaning all test methods in the class template will be executed twice. The display names of the invocations will be @@ -33,9 +49,9 @@ output when using the `ConsoleLauncher` is as follows. The `{ClassTemplateInvocationContextProvider}` extension API is primarily intended for implementing different kinds of tests that rely on repetitive invocation of _all_ test -methods in a test class albeit in different contexts — for example, with different +methods in a test class, albeit in different contexts — for example, with different parameters, by preparing the test class instance differently, or multiple times without modifying the context. -Please refer to the implementations of +Please refer to the implementation of xref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Classes] which uses this extension point to provide its functionality. diff --git a/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-test-templates.adoc b/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-test-templates.adoc index 2d55a58a5ae3..5dec4b7ffafa 100644 --- a/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-test-templates.adoc +++ b/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-test-templates.adoc @@ -9,11 +9,27 @@ for the next invocation of the `{TestTemplate}` method. The following example shows how to write a test template as well as how to register and implement a `{TestTemplateInvocationContextProvider}`. +.A test template with an accompanying extension +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] -.A test template with accompanying extension ---- include::example$java/example/TestTemplateDemo.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/TestTemplateDemo.kt[tags=user_guide] +---- +-- +==== In this example, the test template will be invoked twice. The display names of the invocations will be `apple` and `banana` as specified by the invocation context. Each @@ -28,7 +44,7 @@ parameter. The output when using the `ConsoleLauncher` is as follows. The `{TestTemplateInvocationContextProvider}` extension API is primarily intended for implementing different kinds of tests that rely on repetitive invocation of a test-like -method albeit in different contexts — for example, with different parameters, by preparing +method, albeit in different contexts — for example, with different parameters, by preparing the test class instance differently, or multiple times without modifying the context. Please refer to the implementations of xref:writing-tests/repeated-tests.adoc[] or xref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Tests] which use this extension point diff --git a/documentation/modules/ROOT/pages/extensions/relative-execution-order-of-user-code-and-extensions.adoc b/documentation/modules/ROOT/pages/extensions/relative-execution-order-of-user-code-and-extensions.adoc index 6c88a1ed6d29..c4566e00bdc3 100644 --- a/documentation/modules/ROOT/pages/extensions/relative-execution-order-of-user-code-and-extensions.adoc +++ b/documentation/modules/ROOT/pages/extensions/relative-execution-order-of-user-code-and-extensions.adoc @@ -10,7 +10,7 @@ NOTE: See also: xref:writing-tests/test-execution-order.adoc[] The following diagram illustrates the relative order of user-supplied code and extension code. User-supplied test and lifecycle methods are shown in orange, with callback code -implemented by extensions shown in blue. The grey box denotes the execution of a single +implemented by extensions shown in blue. The gray box denotes the execution of a single test method and will be repeated for every test method in the test class. [[diagram]] @@ -65,7 +65,7 @@ extension code executed after all tests of the container are executed In the simplest case only the actual test method will be executed (step 9); all other steps are optional depending on the presence of user code or extension support for the -corresponding lifecycle callback. For further details on the various lifecycle callbacks +corresponding lifecycle callback. For further details on the various lifecycle callbacks, please consult the respective Javadoc for each annotation and extension. All invocations of user code methods in the above table can additionally be intercepted @@ -119,32 +119,98 @@ for user-supplied _lifecycle methods_ (see xref:writing-tests/definitions.adoc[] The following examples demonstrate this behavior. Please note that the examples do not actually do anything realistic. Instead, they mimic common scenarios for testing interactions with the database. All methods imported statically from the `Logger` class -log contextual information in order to help us better understand the execution order of +log contextual information to help better understand the execution order of user-supplied callback methods and callback methods in extensions. +In Kotlin examples, the corresponding methods are top-level functions in the same package +and therefore don't require imports. -[source,java,indent=0] .Extension1 +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/callbacks/Extension1.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/callbacks/Extension1.kt[tags=user_guide] +---- +-- +==== -[source,java,indent=0] .Extension2 +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/callbacks/Extension2.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/callbacks/Extension2.kt[tags=user_guide] +---- +-- +==== -[source,java,indent=0] .AbstractDatabaseTests +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/callbacks/AbstractDatabaseTests.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/callbacks/AbstractDatabaseTests.kt[tags=user_guide] +---- +-- +==== -[source,java,indent=0] .DatabaseTestsDemo +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/callbacks/DatabaseTestsDemo.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/callbacks/DatabaseTestsDemo.kt[tags=user_guide] +---- +-- +==== When the `DatabaseTestsDemo` test class is executed, the following is logged. @@ -160,7 +226,7 @@ When the `DatabaseTestsDemo` test class is executed, the following is logged. @AfterEach AbstractDatabaseTests.disconnectFromDatabase() Extension2.afterEach() Extension1.afterEach() -@BeforeAll DatabaseTestsDemo.afterAll() +@AfterAll DatabaseTestsDemo.afterAll() @AfterAll AbstractDatabaseTests.destroyDatabase() ---- @@ -200,11 +266,27 @@ are executed. * The database connection is closed _before_ deleting the test data, which results in a failure to connect to the database. -[source,java,indent=0] .BrokenLifecycleMethodConfigDemo +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/callbacks/BrokenLifecycleMethodConfigDemo.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/callbacks/BrokenLifecycleMethodConfigDemo.kt[tags=user_guide] +---- +-- +==== When the `BrokenLifecycleMethodConfigDemo` test class is executed, the following is logged. diff --git a/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java b/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java index 6e518d23bab6..797e2a6d73a2 100644 --- a/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java +++ b/documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java @@ -12,6 +12,7 @@ // tag::user_guide[] +import static example.callbacks.Logger.afterAllMethod; import static example.callbacks.Logger.afterEachMethod; import static example.callbacks.Logger.beforeAllMethod; import static example.callbacks.Logger.beforeEachMethod; @@ -54,7 +55,7 @@ void deleteTestDataFromDatabase() { @AfterAll static void afterAll() { - beforeAllMethod(DatabaseTestsDemo.class.getSimpleName() + ".afterAll()"); + afterAllMethod(DatabaseTestsDemo.class.getSimpleName() + ".afterAll()"); } } diff --git a/documentation/src/test/kotlin/example/kotlin/ClassTemplateDemo.kt b/documentation/src/test/kotlin/example/kotlin/ClassTemplateDemo.kt new file mode 100644 index 000000000000..80c693713606 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/ClassTemplateDemo.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2015-2026 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.kotlin + +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.ClassTemplate +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertNotNull +import org.junit.jupiter.api.extension.ClassTemplateInvocationContext +import org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extension +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.TestInstancePostProcessor +import java.util.stream.Stream + +// tag::user_guide[] +@ClassTemplate +@ExtendWith(ClassTemplateDemo.MyClassTemplateInvocationContextProvider::class) +class ClassTemplateDemo { + private var fruit: String? = null + + @Test + fun notNull() { + assertNotNull(fruit) + } + + @Test + fun wellKnown() { + assertTrue(fruit in WELL_KNOWN_FRUITS) + } + + class MyClassTemplateInvocationContextProvider : ClassTemplateInvocationContextProvider { + override fun supportsClassTemplate(context: ExtensionContext) = true + + override fun provideClassTemplateInvocationContexts(context: ExtensionContext) = + Stream.of(invocationContext("apple"), invocationContext("banana")) + + private fun invocationContext(parameter: String) = + object : ClassTemplateInvocationContext { + override fun getDisplayName(invocationIndex: Int) = parameter + + override fun getAdditionalExtensions(): List = + listOf( + TestInstancePostProcessor { testInstance, _ -> + (testInstance as ClassTemplateDemo).fruit = parameter + } + ) + } + } + + companion object { + val WELL_KNOWN_FRUITS = listOf("apple", "banana", "lemon") + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/TestTemplateDemo.kt b/documentation/src/test/kotlin/example/kotlin/TestTemplateDemo.kt new file mode 100644 index 000000000000..40d2eb20d0dd --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/TestTemplateDemo.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2015-2026 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.kotlin + +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.TestTemplate +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extension +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.ParameterContext +import org.junit.jupiter.api.extension.ParameterResolver +import org.junit.jupiter.api.extension.TestTemplateInvocationContext +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider +import java.util.stream.Stream + +class TestTemplateDemo { + // tag::user_guide[] + val fruits = listOf("apple", "banana", "lemon") + + @TestTemplate + @ExtendWith(MyTestTemplateInvocationContextProvider::class) + fun testTemplate(fruit: String) { + assertTrue(fruit in fruits) + } + + class MyTestTemplateInvocationContextProvider : TestTemplateInvocationContextProvider { + override fun supportsTestTemplate(context: ExtensionContext) = true + + override fun provideTestTemplateInvocationContexts(context: ExtensionContext) = + Stream.of(invocationContext("apple"), invocationContext("banana")) + + private fun invocationContext(parameter: String) = + object : TestTemplateInvocationContext { + override fun getDisplayName(invocationIndex: Int) = parameter + + override fun getAdditionalExtensions(): List = + listOf( + object : ParameterResolver { + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ) = parameterContext.parameter.type == String::class.java + + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Any = parameter + } + ) + } + } + // end::user_guide[] +} diff --git a/documentation/src/test/kotlin/example/kotlin/callbacks/AbstractDatabaseTests.kt b/documentation/src/test/kotlin/example/kotlin/callbacks/AbstractDatabaseTests.kt new file mode 100644 index 000000000000..7635490e0231 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/callbacks/AbstractDatabaseTests.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2015-2026 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.kotlin.callbacks + +// tag::user_guide[] + +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach + +/** + * Abstract base class for tests that use the database. + */ +abstract class AbstractDatabaseTests { + @BeforeEach + fun connectToDatabase() { + beforeEachMethod("${AbstractDatabaseTests::class.simpleName}.connectToDatabase()") + } + + @AfterEach + fun disconnectFromDatabase() { + afterEachMethod("${AbstractDatabaseTests::class.simpleName}.disconnectFromDatabase()") + } + + companion object { + @JvmStatic + @BeforeAll + fun createDatabase() { + beforeAllMethod("${AbstractDatabaseTests::class.simpleName}.createDatabase()") + } + + @JvmStatic + @AfterAll + fun destroyDatabase() { + afterAllMethod("${AbstractDatabaseTests::class.simpleName}.destroyDatabase()") + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/callbacks/BrokenLifecycleMethodConfigDemo.kt b/documentation/src/test/kotlin/example/kotlin/callbacks/BrokenLifecycleMethodConfigDemo.kt new file mode 100644 index 000000000000..5a4b35be11e4 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/callbacks/BrokenLifecycleMethodConfigDemo.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2015-2026 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.kotlin.callbacks + +// tag::user_guide[] + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +/** + * Example of "broken" lifecycle method configuration. + * + * Test data is inserted before the database connection has been opened. + * + * Database connection is closed before deleting test data. + */ +@ExtendWith(Extension1::class, Extension2::class) +class BrokenLifecycleMethodConfigDemo { + @BeforeEach + fun connectToDatabase() { + beforeEachMethod("${javaClass.simpleName}.connectToDatabase()") + } + + @BeforeEach + fun insertTestDataIntoDatabase() { + beforeEachMethod("${javaClass.simpleName}.insertTestDataIntoDatabase()") + } + + @Test + fun testDatabaseFunctionality() { + testMethod("${javaClass.simpleName}.testDatabaseFunctionality()") + } + + @AfterEach + fun deleteTestDataFromDatabase() { + afterEachMethod("${javaClass.simpleName}.deleteTestDataFromDatabase()") + } + + @AfterEach + fun disconnectFromDatabase() { + afterEachMethod("${javaClass.simpleName}.disconnectFromDatabase()") + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/callbacks/DatabaseTestsDemo.kt b/documentation/src/test/kotlin/example/kotlin/callbacks/DatabaseTestsDemo.kt new file mode 100644 index 000000000000..4e4592dc4b87 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/callbacks/DatabaseTestsDemo.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2015-2026 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.kotlin.callbacks + +// tag::user_guide[] + +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +/** + * Extension of [AbstractDatabaseTests] that inserts test data + * into the database (after the database connection has been opened) + * and deletes test data (before the database connection is closed). + */ +@ExtendWith(Extension1::class, Extension2::class) +class DatabaseTestsDemo : AbstractDatabaseTests() { + @BeforeEach + fun insertTestDataIntoDatabase() { + beforeEachMethod("${javaClass.simpleName}.insertTestDataIntoDatabase()") + } + + @Test + fun testDatabaseFunctionality() { + testMethod("${javaClass.simpleName}.testDatabaseFunctionality()") + } + + @AfterEach + fun deleteTestDataFromDatabase() { + afterEachMethod("${javaClass.simpleName}.deleteTestDataFromDatabase()") + } + + companion object { + @JvmStatic + @BeforeAll + fun beforeAll() { + beforeAllMethod("${DatabaseTestsDemo::class.simpleName}.beforeAll()") + } + + @JvmStatic + @AfterAll + fun afterAll() { + afterAllMethod("${DatabaseTestsDemo::class.simpleName}.afterAll()") + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/callbacks/Extension1.kt b/documentation/src/test/kotlin/example/kotlin/callbacks/Extension1.kt new file mode 100644 index 000000000000..5fbb590bff47 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/callbacks/Extension1.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2015-2026 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.kotlin.callbacks + +// tag::user_guide[] + +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +class Extension1 : + BeforeEachCallback, + AfterEachCallback { + override fun beforeEach(context: ExtensionContext) { + beforeEachCallback(this) + } + + override fun afterEach(context: ExtensionContext) { + afterEachCallback(this) + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/callbacks/Extension2.kt b/documentation/src/test/kotlin/example/kotlin/callbacks/Extension2.kt new file mode 100644 index 000000000000..1f8bccbc8d67 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/callbacks/Extension2.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2015-2026 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.kotlin.callbacks + +// tag::user_guide[] + +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +class Extension2 : + BeforeEachCallback, + AfterEachCallback { + override fun beforeEach(context: ExtensionContext) { + beforeEachCallback(this) + } + + override fun afterEach(context: ExtensionContext) { + afterEachCallback(this) + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/callbacks/Logger.kt b/documentation/src/test/kotlin/example/kotlin/callbacks/Logger.kt new file mode 100644 index 000000000000..799ba8a195cd --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/callbacks/Logger.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2015-2026 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.kotlin.callbacks + +import org.junit.jupiter.api.extension.Extension +import java.util.logging.Logger + +private val logger = Logger.getLogger("example.kotlin.callbacks.Logger") + +fun beforeAllMethod(text: String) = log { "@BeforeAll $text" } + +fun beforeEachCallback(extension: Extension) = log { " ${extension.javaClass.simpleName}.beforeEach()" } + +fun beforeEachMethod(text: String) = log { " @BeforeEach $text" } + +fun testMethod(text: String) = log { " @Test $text" } + +fun afterEachMethod(text: String) = log { " @AfterEach $text" } + +fun afterEachCallback(extension: Extension) = log { " ${extension.javaClass.simpleName}.afterEach()" } + +fun afterAllMethod(text: String) = log { "@AfterAll $text" } + +private fun log(supplier: () -> String) { + logger.info(supplier) +} diff --git a/documentation/src/test/kotlin/example/kotlin/exception/IgnoreIOExceptionExtension.kt b/documentation/src/test/kotlin/example/kotlin/exception/IgnoreIOExceptionExtension.kt new file mode 100644 index 000000000000..e5680c1babb9 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/exception/IgnoreIOExceptionExtension.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2015-2026 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.kotlin.exception + +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler +import java.io.IOException + +// tag::user_guide[] +class IgnoreIOExceptionExtension : TestExecutionExceptionHandler { + override fun handleTestExecutionException( + context: ExtensionContext, + throwable: Throwable + ) { + when (throwable) { + is IOException -> return + else -> throw throwable + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/exception/MultipleHandlersTestCase.kt b/documentation/src/test/kotlin/example/kotlin/exception/MultipleHandlersTestCase.kt new file mode 100644 index 000000000000..33ec4caa0246 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/exception/MultipleHandlersTestCase.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2015-2026 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.kotlin.exception + +import example.kotlin.exception.MultipleHandlersTestCase.ThirdExecutedHandler +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler + +// tag::user_guide[] +// Register handlers for @Test, @BeforeEach, @AfterEach as well as @BeforeAll and @AfterAll +@ExtendWith(ThirdExecutedHandler::class) +class MultipleHandlersTestCase { + // Register handlers for @Test, @BeforeEach, @AfterEach only + @ExtendWith(SecondExecutedHandler::class) + @ExtendWith(FirstExecutedHandler::class) + @Test + fun testMethod() { + } + + // end::user_guide[] + + class FirstExecutedHandler : TestExecutionExceptionHandler { + override fun handleTestExecutionException( + context: ExtensionContext, + ex: Throwable + ): Unit = throw ex + } + + class SecondExecutedHandler : LifecycleMethodExecutionExceptionHandler { + override fun handleBeforeEachMethodExecutionException( + context: ExtensionContext, + ex: Throwable + ): Unit = throw ex + } + + class ThirdExecutedHandler : LifecycleMethodExecutionExceptionHandler { + override fun handleBeforeAllMethodExecutionException( + context: ExtensionContext, + ex: Throwable + ): Unit = throw ex + } + // tag::user_guide[] +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/exception/RecordStateOnErrorExtension.kt b/documentation/src/test/kotlin/example/kotlin/exception/RecordStateOnErrorExtension.kt new file mode 100644 index 000000000000..4b24a7a7731d --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/exception/RecordStateOnErrorExtension.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2015-2026 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.kotlin.exception + +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler + +// tag::user_guide[] +class RecordStateOnErrorExtension : LifecycleMethodExecutionExceptionHandler { + override fun handleBeforeAllMethodExecutionException( + context: ExtensionContext, + ex: Throwable + ) { + memoryDumpForFurtherInvestigation("Failure recorded during class setup") + throw ex + } + + override fun handleBeforeEachMethodExecutionException( + context: ExtensionContext, + ex: Throwable + ) { + memoryDumpForFurtherInvestigation("Failure recorded during test setup") + throw ex + } + + override fun handleAfterEachMethodExecutionException( + context: ExtensionContext, + ex: Throwable + ) { + memoryDumpForFurtherInvestigation("Failure recorded during test cleanup") + throw ex + } + + override fun handleAfterAllMethodExecutionException( + context: ExtensionContext, + ex: Throwable + ) { + memoryDumpForFurtherInvestigation("Failure recorded during class cleanup") + throw ex + } + // end::user_guide[] + + private fun memoryDumpForFurtherInvestigation(error: String) { + } + // tag::user_guide[] +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/interceptor/SwingEdtInterceptor.kt b/documentation/src/test/kotlin/example/kotlin/interceptor/SwingEdtInterceptor.kt new file mode 100644 index 000000000000..065aaacd3d5f --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/interceptor/SwingEdtInterceptor.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2015-2026 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.kotlin.interceptor + +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.InvocationInterceptor +import org.junit.jupiter.api.extension.InvocationInterceptor.Invocation +import org.junit.jupiter.api.extension.ReflectiveInvocationContext +import java.lang.reflect.Method +import javax.swing.SwingUtilities + +// tag::user_guide[] +class SwingEdtInterceptor : InvocationInterceptor { + override fun interceptTestMethod( + invocation: Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + var throwable: Throwable? = null + SwingUtilities.invokeAndWait { + try { + invocation.proceed() + } catch (t: Throwable) { + throwable = t + } + } + throwable?.let { throw it } + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/registration/DocumentationDemo.kt b/documentation/src/test/kotlin/example/kotlin/registration/DocumentationDemo.kt index c955d7984106..5304d20c11bf 100644 --- a/documentation/src/test/kotlin/example/kotlin/registration/DocumentationDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/registration/DocumentationDemo.kt @@ -26,11 +26,9 @@ class DocumentationDemo { // use this.docs ... } - companion object { - fun lookUpDocsDir(): Path? { - // return path to docs dir - return null - } + private fun lookUpDocsDir(): Path? { + // return path to docs dir + return null } } // end::user_guide[]