diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bf4d92714ec6..e8dd8512ae8c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -122,26 +122,15 @@ jobs: run: | sudo apt-get update sudo apt-get install graphviz - - name: Build Documentation - uses: ./.github/actions/run-gradle + - name: Install Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - arguments: | - prepareDocsForUploadToGhPages \ - -Dscan.tag.Documentation - - name: Configure Git - shell: bash - run: | - git config --global user.name "JUnit Team" - git config --global user.email "team@junit.org" - - name: Upload Documentation - if: github.event_name == 'push' && github.repository == 'junit-team/junit-framework' && github.ref == 'refs/heads/main' + node-version-file: documentation/.tool-versions + - name: Build Documentation uses: ./.github/actions/run-gradle with: encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | - gitPublishPush \ + antora \ + -Pantora.downloadNode=false \ -Dscan.tag.Documentation - env: - GIT_USERNAME: git - GIT_PASSWORD: ${{ secrets.JUNIT_BUILDS_GITHUB_TOKEN_DOCS_REPO }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000000..a646ea1727c0 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,5 @@ +rules: + cache-poisoning: + ignore: + # reports issues for setup-node which isn't used while releasing + - main.yml diff --git a/.gitignore b/.gitignore index 2ff2b321ef5c..2f03fe1f2adc 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ checksums* # snapshot-tests *.snapshot_actual *.snapshot_raw + +# Antora +/documentation/node_modules/ diff --git a/documentation/.tool-versions b/documentation/.tool-versions new file mode 100644 index 000000000000..9d709112585c --- /dev/null +++ b/documentation/.tool-versions @@ -0,0 +1 @@ +nodejs 24.11.1 diff --git a/documentation/README.md b/documentation/README.md index acb46649d9d5..c0254882da92 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,10 +1,10 @@ # JUnit User Guide -This subproject contains the AsciiDoc sources for the JUnit User Guide. +This subproject contains the Antora/AsciiDoc sources for the JUnit User Guide. ## Structure -- `src/docs/asciidoc`: AsciiDoc files +- `modules/ROOT/pages`: AsciiDoc files - `src/test/java`: Java test source code that can be included in the AsciiDoc files - `src/test/kotlin`: Kotlin test source code that can be included in the AsciiDoc files - `src/test/resources`: Classpath resources that can be included in the AsciiDoc files or @@ -12,23 +12,14 @@ This subproject contains the AsciiDoc sources for the JUnit User Guide. ## Usage -### Generate AsciiDoc +### Generate Antora site This following Gradle command generates the HTML version of the User Guide as -`build/docs/asciidoc/user-guide/index.html`. +`build/antora/build/site`. ``` -./gradlew asciidoctor +./gradlew antora ``` On Linux operating systems, the `graphviz` package providing `/usr/bin/dot` must be -installed in order to generate the User Guide. - -### Generate AsciiDocPdf - -This following Gradle command generates the PDF version of the User Guide to -`build/docs/asciidocPdf/user-guide/index.pdf`. - -``` -./gradlew asciidoctorPdf -``` +installed in order to generate the PlantUML images used in the User Guide. diff --git a/documentation/antora-playbook.yml b/documentation/antora-playbook.yml new file mode 100644 index 000000000000..f371bf92c6f2 --- /dev/null +++ b/documentation/antora-playbook.yml @@ -0,0 +1,26 @@ +antora: + extensions: + - '@antora/collector-extension' + - '@antora/lunr-extension' + - require: '@springio/antora-extensions/root-component-extension' + root_component_name: junit + - require: '@springio/antora-extensions/root-attachments-extension' +site: + title: JUnit User Guide + url: https://docs.junit.org +content: + sources: + - url: @GIT_REPO_ROOT@ + branches: @GIT_BRANCH_NAME@ + start_path: documentation + worktrees: true +asciidoc: + attributes: + attribute-missing: warn +runtime: + log: + failure_level: warn +ui: + bundle: + url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip?job=bundle-stable + snapshot: true diff --git a/documentation/antora.yml b/documentation/antora.yml new file mode 100644 index 000000000000..9afb0753ee7f --- /dev/null +++ b/documentation/antora.yml @@ -0,0 +1,295 @@ +name: junit +version: null +title: JUnit +nav: + - modules/ROOT/nav.adoc +ext: + collector: + run: + command: gradlew --quiet :documentation:generateAntoraResources + local: true + scan: + - dir: ./build/generated-antora-resources + clean: true + - dir: ./build/docs/fixedJavadoc + clean: true + into: modules/ROOT/attachments/api + - dir: ./src/test + into: modules/ROOT/examples + - dir: ./build/generated/asciidoc + clean: true + into: modules/ROOT/partials + - dir: ./build/plantuml + clean: true + into: modules/ROOT/images +asciidoc: + attributes: + javadoc-root: "xref:attachment$api/" + # Snapshot Repository + snapshot-repo: 'https://central.sonatype.com/service/rest/repository/browse/maven-snapshots' + # Base Links + junit-team: 'https://github.com/junit-team' + junit-framework-repo: '{junit-team}/junit-framework' + current-branch: '{junit-framework-repo}/tree/{release-branch}' + # Platform Commons + junit-platform-support-package: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/package-summary.html[org.junit.platform.commons.support]' + AnnotationSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/AnnotationSupport.html[AnnotationSupport]' + ClassSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ClassSupport.html[ClassSupport]' + ConversionSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/conversion/ConversionSupport.html[ConversionSupport]' + ModifierSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ModifierSupport.html[ModifierSupport]' + ReflectionSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ReflectionSupport.html[ReflectionSupport]' + Testable: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/annotation/Testable.html[@Testable]' + # Platform Console Launcher + junit-platform-console: '{javadoc-root}/org.junit.platform.console/org/junit/platform/console/package-summary.html[junit-platform-console]' + ConsoleLauncher: '{javadoc-root}/org.junit.platform.console/org/junit/platform/console/ConsoleLauncher.html[ConsoleLauncher]' + # Platform Engine + junit-platform-engine: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/package-summary.html[junit-platform-engine]' + junit-platform-engine-support-discovery: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/discovery/package-summary.html[org.junit.platform.engine.support.discovery]' + CancellationToken: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/CancellationToken.html[CancellationToken]' + ClasspathResourceSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathResourceSelector.html[ClasspathResourceSelector]' + ClasspathRootSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathRootSelector.html[ClasspathRootSelector]' + ClassSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClassSelector.html[ClassSelector]' + DiscoveryIssue: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/DiscoveryIssue.html[DiscoveryIssue]' + DiscoveryIssueReporter: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/discovery/DiscoveryIssueReporter.html[DiscoveryIssueReporter]' + DirectorySelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DirectorySelector.html[DirectorySelector]' + DiscoverySelectors: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html[DiscoverySelectors]' + DiscoverySelectors_selectClasspathResource: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathResource(java.lang.String)[selectClasspathResource]' + DiscoverySelectors_selectClasspathRoots: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathRoots(java.util.Set)[selectClasspathRoots]' + DiscoverySelectors_selectClass: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClass(java.lang.String)[selectClass]' + DiscoverySelectors_selectDirectory: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectDirectory(java.lang.String)[selectDirectory]' + DiscoverySelectors_selectFile: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectFile(java.lang.String)[selectFile]' + DiscoverySelectors_selectIteration: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectIteration(org.junit.platform.engine.DiscoverySelector,int\...)[selectIteration]' + DiscoverySelectors_selectMethod: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectMethod(java.lang.String)[selectMethod]' + DiscoverySelectors_selectModule: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectModule(java.lang.String)[selectModule]' + DiscoverySelectors_selectNestedClass: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectNestedClass(java.util.List,java.lang.Class)[selectNestedClass]' + DiscoverySelectors_selectNestedMethod: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectNestedMethod(java.util.List,java.lang.Class,java.lang.String)[selectNestedMethod]' + DiscoverySelectors_selectPackage: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectPackage(java.lang.String)[selectPackage]' + DiscoverySelectors_selectUniqueId: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUniqueId(java.lang.String)[selectUniqueId]' + DiscoverySelectors_selectUri: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUri(java.lang.String)[selectUri]' + EngineDiscoveryListener: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/EngineDiscoveryListener.html[EngineDiscoveryListener]' + EngineDiscoveryRequest: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/EngineDiscoveryRequest.html[EngineDiscoveryRequest]' + FileSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/FileSelector.html[FileSelector]' + HierarchicalTestEngine: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.html[HierarchicalTestEngine]' + IterationSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/IterationSelector.html[IterationSelector]' + MethodSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/MethodSelector.html[MethodSelector]' + ModuleSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ModuleSelector.html[ModuleSelector]' + NamespacedHierarchicalStore: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.html[NamespacedHierarchicalStore]' + NestedClassSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedClassSelector.html[NestedClassSelector]' + NestedMethodSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedMethodSelector.html[NestedMethodSelector]' + OutputDirectoryCreator: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/OutputDirectoryCreator.html[OutputDirectoryCreator]' + PackageSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/PackageSelector.html[PackageSelector]' + ParallelExecutionConfigurationStrategy: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.html[ParallelExecutionConfigurationStrategy]' + UniqueIdSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UniqueIdSelector.html[UniqueIdSelector]' + UriSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UriSelector.html[UriSelector]' + TestEngine: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/TestEngine.html[TestEngine]' + # Platform Launcher API + junit-platform-launcher: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/package-summary.html[junit-platform-launcher]' + DiscoveryIssueException: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/DiscoveryIssueException.html[DiscoveryIssueException]' + Launcher: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/Launcher.html[Launcher]' + LauncherConfig: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherConfig.html[LauncherConfig]' + LauncherDiscoveryListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryListener.html[LauncherDiscoveryListener]' + LauncherDiscoveryRequest: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryRequest.html[LauncherDiscoveryRequest]' + LauncherDiscoveryRequestBuilder: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.html[LauncherDiscoveryRequestBuilder]' + LauncherExecutionRequest: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherExecutionRequest.html[LauncherExecutionRequest]' + LauncherExecutionRequestBuilder: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherExecutionRequestBuilder.html[LauncherExecutionRequestBuilder]' + LauncherFactory: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherFactory.html[LauncherFactory]' + LauncherInterceptor: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherInterceptor.html[LauncherInterceptor]' + LauncherSession: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSession.html[LauncherSession]' + LauncherSessionListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSessionListener.html[LauncherSessionListener]' + LoggingListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/LoggingListener.html[LoggingListener]' + PostDiscoveryFilter: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/PostDiscoveryFilter.html[PostDiscoveryFilter]' + SummaryGeneratingListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/SummaryGeneratingListener.html[SummaryGeneratingListener]' + TestExecutionListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestExecutionListener.html[TestExecutionListener]' + TestPlan: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestPlan.html[TestPlan]' + UniqueIdTrackingListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.html[UniqueIdTrackingListener]' + # Platform Reporting + LegacyXmlReportGeneratingListener: '{javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.html[LegacyXmlReportGeneratingListener]' + OpenTestReportGeneratingListener: '{javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.html[OpenTestReportGeneratingListener]' + # Platform Suite + suite-api-package: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/package-summary.html[org.junit.platform.suite.api]' + junit-platform-suite-engine: '{javadoc-root}/org.junit.platform.suite.engine/org/junit/platform/suite/engine/package-summary.html[junit-platform-suite-engine]' + Select: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/Select.html[@Select]' + SelectClasspathResource: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasspathResource.html[@SelectClasspathResource]' + SelectClasses: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasses.html[@SelectClasses]' + SelectDirectories: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectDirectories.html[@SelectDirectories]' + SelectFile: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectFile.html[@SelectFile]' + SelectMethod: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectMethod.html[@SelectMethod]' + SelectModules: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectModules.html[@SelectModules]' + SelectPackages: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectPackages.html[@SelectPackages]' + SelectUris: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectUris.html[@SelectUris]' + # Platform Test Kit + testkit-engine-package: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/package-summary.html[org.junit.platform.testkit.engine]' + EngineExecutionResults: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EngineExecutionResults.html[EngineExecutionResults]' + EngineTestKit: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EngineTestKit.html[EngineTestKit]' + Event: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Event.html[Event]' + EventConditions: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventConditions.html[EventConditions]' + Events: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Events.html[Events]' + EventStatistics: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventStatistics.html[EventStatistics]' + EventType: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventType.html[EventType]' + Executions: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Executions.html[Executions]' + TerminationInfo: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/TerminationInfo.html[TerminationInfo]' + TestExecutionResultConditions: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/TestExecutionResultConditions.html[TestExecutionResultConditions]' + # Jupiter Core API + api-package: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/package-summary.html[org.junit.jupiter.api]' + Assertions: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html[org.junit.jupiter.api.Assertions]' + Assumptions: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assumptions.html[org.junit.jupiter.api.Assumptions]' + AutoClose: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/AutoClose.html[@AutoClose]' + ClassOrderer_ClassName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.ClassName.html[ClassOrderer.ClassName]' + ClassOrderer_Default: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Default.html[ClassOrderer.Default]' + ClassOrderer_DisplayName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.DisplayName.html[ClassOrderer.DisplayName]' + ClassOrderer_OrderAnnotation: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.OrderAnnotation.html[ClassOrderer.OrderAnnotation]' + ClassOrderer_Random: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Random.html[ClassOrderer.Random]' + ClassOrderer: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.html[ClassOrderer]' + ClassTemplate: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassTemplate.html[@ClassTemplate]' + Disabled: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Disabled.html[@Disabled]' + MethodOrderer_Default: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Default.html[MethodOrderer.Default]' + MethodOrderer_DisplayName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.DisplayName.html[MethodOrderer.DisplayName]' + MethodOrderer_MethodName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.MethodName.html[MethodOrderer.MethodName]' + MethodOrderer_OrderAnnotation: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.OrderAnnotation.html[MethodOrderer.OrderAnnotation]' + MethodOrderer_Random: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Random.html[MethodOrderer.Random]' + MethodOrderer: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.html[MethodOrderer]' + Named: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Named.html[Named]' + Order: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Order.html[@Order]' + RepetitionInfo: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/RepetitionInfo.html[RepetitionInfo]' + TestInfo: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestInfo.html[TestInfo]' + TestClassOrder: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestClassOrder.html[@TestClassOrder]' + TestMethodOrder: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestMethodOrder.html[@TestMethodOrder]' + TestReporter: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[TestReporter]' + TestTemplate: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestTemplate.html[@TestTemplate]' + # @DefaultLocale and @DefaultTimeZone + DefaultLocale: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/DefaultLocale.html[@DefaultLocale]' + DefaultTimeZone: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/DefaultTimeZone.html[@DefaultTimeZone]' + LocaleProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/LocaleProvider.html[LocaleProvider]' + TimeZoneProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/TimeZoneProvider.html[TimeZoneProvider]' + ReadsDefaultLocale: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ReadsDefaultLocale.html[@ReadsDefaultLocale]' + ReadsDefaultTimeZone: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ReadsDefaultTimeZone.html[@ReadsDefaultTimeZone]' + WritesDefaultLocale: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/WritesDefaultLocale.html[@WritesDefaultLocale]' + WritesDefaultTimeZone: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/WritesDefaultTimeZone.html[@WritesDefaultTimeZone]' + # Jupiter Parallel API + Execution: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Execution.html[@Execution]' + Isolated: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Isolated.html[@Isolated]' + ResourceLock: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLock.html[@ResourceLock]' + ResourceLockTarget: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLockTarget.html[ResourceLockTarget]' + ResourceLocksProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLocksProvider.html[ResourceLocksProvider]' + Resources: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Resources.html[Resources]' + # Jupiter Extension APIs + extension-api-package: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/package-summary.html[org.junit.jupiter.api.extension]' + AfterAllCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterAllCallback.html[AfterAllCallback]' + AfterClassTemplateInvocationCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterClassTemplateInvocationCallback.html[AfterClassTemplateInvocationCallback]' + AfterEachCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterEachCallback.html[AfterEachCallback]' + AfterTestExecutionCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterTestExecutionCallback.html[AfterTestExecutionCallback]' + ParameterContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterContext.html[ParameterContext]' + BeforeAllCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeAllCallback.html[BeforeAllCallback]' + BeforeClassTemplateInvocationCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeClassTemplateInvocationCallback.html[BeforeClassTemplateInvocationCallback]' + BeforeEachCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeEachCallback.html[BeforeEachCallback]' + BeforeTestExecutionCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.html[BeforeTestExecutionCallback]' + ClassTemplateInvocationContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ClassTemplateInvocationContext.html[ClassTemplateInvocationContext]' + ClassTemplateInvocationContextProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ClassTemplateInvocationContextProvider.html[ClassTemplateInvocationContextProvider]' + ExecutableInvoker: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutableInvoker.html[ExecutableInvoker]' + ExecutionCondition: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutionCondition.html[ExecutionCondition]' + ExtendWith: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtendWith.html[@ExtendWith]' + ExtensionContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.html[ExtensionContext]' + ExtensionContext_Store: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.Store.html[Store]' + ExtensionContext_StoreScope: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.StoreScope.html[StoreScope]' + InvocationInterceptor: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/InvocationInterceptor.html[InvocationInterceptor]' + LifecycleMethodExecutionExceptionHandler: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.html[LifecycleMethodExecutionExceptionHandler]' + ParameterResolver: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterResolver.html[ParameterResolver]' + RegisterExtension: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/RegisterExtension.html[@RegisterExtension]' + TestExecutionExceptionHandler: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.html[TestExecutionExceptionHandler]' + TestInstanceFactory: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstanceFactory.html[TestInstanceFactory]' + TestInstancePostProcessor: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePostProcessor.html[TestInstancePostProcessor]' + TestInstancePreConstructCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.html[TestInstancePreConstructCallback]' + TestInstancePreDestroyCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.html[TestInstancePreDestroyCallback]' + TestTemplateInvocationContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContext.html[TestTemplateInvocationContext]' + TestTemplateInvocationContextProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.html[TestTemplateInvocationContextProvider]' + TestWatcher: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestWatcher.html[TestWatcher]' + PreInterruptCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/PreInterruptCallback.html[PreInterruptCallback]' + # Jupiter Conditions + DisabledForJreRange: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledForJreRange.html[@DisabledForJreRange]' + DisabledIf: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIf.html[@DisabledIf]' + DisabledIfEnvironmentVariable: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.html[@DisabledIfEnvironmentVariable]' + DisabledIfSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfSystemProperty.html[@DisabledIfSystemProperty]' + DisabledInNativeImage: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledInNativeImage.html[@DisabledInNativeImage]' + DisabledOnJre: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnJre.html[@DisabledOnJre]' + DisabledOnOs: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnOs.html[@DisabledOnOs]' + EnabledForJreRange: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledForJreRange.html[@EnabledForJreRange]' + EnabledIf: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIf.html[@EnabledIf]' + EnabledIfEnvironmentVariable: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.html[@EnabledIfEnvironmentVariable]' + EnabledIfSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfSystemProperty.html[@EnabledIfSystemProperty]' + EnabledInNativeImage: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledInNativeImage.html[@EnabledInNativeImage]' + EnabledOnJre: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnJre.html[@EnabledOnJre]' + EnabledOnOs: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnOs.html[@EnabledOnOs]' + JRE: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/JRE.html[JRE]' + # Jupiter I/O + TempDir: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDir.html[@TempDir]' + # Jupiter Params + params-provider-package: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/package-summary.html[org.junit.jupiter.params.provider]' + AfterParameterizedClassInvocation: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/AfterParameterizedClassInvocation.html[@AfterParameterizedClassInvocation]' + AnnotationBasedArgumentConverter: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.html[AnnotationBasedArgumentConverter]' + AnnotationBasedArgumentsProvider: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.html[AnnotationBasedArgumentsProvider]' + AggregateWith: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/AggregateWith.html[@AggregateWith]' + Arguments: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/Arguments.html[Arguments]' + ArgumentsProvider: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/ArgumentsProvider.html[ArgumentsProvider]' + ArgumentsAccessor: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAccessor.html[ArgumentsAccessor]' + ArgumentsAggregator: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAggregator.html[ArgumentsAggregator]' + BeforeParameterizedClassInvocation: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/BeforeParameterizedClassInvocation.html[@BeforeParameterizedClassInvocation]' + CsvArgumentsProvider: '{junit-framework-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java[CsvArgumentsProvider]' + EmptySource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/EmptySource.html[@EmptySource]' + FieldSource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/FieldSource.html[@FieldSource]' + MethodSource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/MethodSource.html[@MethodSource]' + NullAndEmptySource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullAndEmptySource.html[@NullAndEmptySource]' + NullSource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullSource.html[@NullSource]' + Parameter: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/Parameter.html[@Parameter]' + ParameterizedClass: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedClass.html[@ParameterizedClass]' + ParameterizedTest: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedTest.html[@ParameterizedTest]' + ParameterInfo: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/support/ParameterInfo.html[ParameterInfo]' + ValueArgumentsProvider: '{junit-framework-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java[ValueArgumentsProvider]' + # Jupiter Engine + junit-jupiter-engine: '{javadoc-root}/org.junit.jupiter.engine/org/junit/jupiter/engine/package-summary.html[junit-jupiter-engine]' + # Jupiter Extension Implementations + AutoCloseExtension: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java[AutoCloseExtension]' + DisabledCondition: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java[DisabledCondition]' + RepetitionExtension: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java[RepetitionExtension]' + TempDirectory: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java[TempDirectory]' + TestInfoParameterResolver: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java[TestInfoParameterResolver]' + TestReporterParameterResolver: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java[TestReporterParameterResolver]' + TypeBasedParameterResolver: '{current-branch}/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java[TypeBasedParameterResolver]' + # Jupiter Examples + CustomAnnotationParameterResolver: '{current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java[CustomAnnotationParameterResolver]' + CustomTypeParameterResolver: '{current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java[CustomTypeParameterResolver]' + MapOfListsTypeBasedParameterResolver: '{current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java[MapOfListsTypeBasedParameterResolver]' + # Jupiter Migration Support + EnableJUnit4MigrationSupport: '{javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.html[@EnableJUnit4MigrationSupport]' + EnableRuleMigrationSupport: '{javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.html[@EnableRuleMigrationSupport]' + # JUnit Start + JUnit: '{javadoc-root}/org.junit.start/org/junit/start/JUnit.html[JUnit]' + # Vintage + junit-vintage-engine: '{javadoc-root}/org.junit.vintage.engine/org/junit/vintage/engine/package-summary.html[junit-vintage-engine]' + # Examples Repository + junit-examples-repo: '{junit-team}/junit-examples' + junit-jupiter-starter-ant: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-ant[junit-jupiter-starter-ant]' + junit-jupiter-starter-gradle-groovy: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle-groovy[junit-jupiter-starter-gradle-groovy]' + junit-jupiter-starter-gradle-kotlin: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle-kotlin[junit-jupiter-starter-gradle-kotlin]' + junit-jupiter-starter-gradle: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle[junit-jupiter-starter-gradle]' + junit-jupiter-starter-maven: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-maven[junit-jupiter-starter-maven]' + RandomParametersExtension: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-extensions/src/main/java/com/example/random/RandomParametersExtension.java[RandomParametersExtension]' + # Third-party Links + API: 'https://apiguardian-team.github.io/apiguardian/docs/current/api/[@API]' + API_Guardian: 'https://github.com/apiguardian-team/apiguardian[@API Guardian]' + AssertJ: 'https://assertj.github.io/doc/[AssertJ]' + Checkstyle: 'https://checkstyle.sourceforge.io[Checkstyle]' + DiscussionsQA: 'https://github.com/junit-team/junit-framework/discussions/categories/q-a[Q&A category on GitHub Discussions]' + Hamcrest: 'https://hamcrest.org/JavaHamcrest/[Hamcrest]' + Jimfs: 'https://google.github.io/jimfs/[Jimfs]' + Log4j: 'https://logging.apache.org/log4j/2.x/[Log4j]' + Log4j_JDK_Logging_Adapter: 'https://logging.apache.org/log4j/2.x/log4j-jul/index.html[Log4j JDK Logging Adapter]' + Logback: 'https://logback.qos.ch/[Logback]' + LogManager: 'https://docs.oracle.com/en/java/javase/17/docs/api/java.logging/java/util/logging/LogManager.html[LogManager]' + Maven_Central: 'https://central.sonatype.com/[Maven Central]' + MockitoExtension: 'https://github.com/mockito/mockito/blob/release/2.x/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java[MockitoExtension]' + ServiceLoader: '{jdk-javadoc-base-url}/java.base/java/util/ServiceLoader.html[ServiceLoader]' + SpringExtension: 'https://github.com/spring-projects/spring-framework/tree/HEAD/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java[SpringExtension]' + StackOverflow: 'https://stackoverflow.com/questions/tagged/junit5[Stack Overflow]' + Truth: 'https://truth.dev/[Truth]' + OpenTestReporting: 'https://github.com/ota4j-team/open-test-reporting[Open Test Reporting]' + OpenTestReportingCliTool: 'https://github.com/ota4j-team/open-test-reporting#cli-tool-for-validation-and-format-conversion[Open Test Reporting CLI Tool]' diff --git a/documentation/documentation.gradle.kts b/documentation/documentation.gradle.kts index 5697d44144eb..46348e9c5b01 100644 --- a/documentation/documentation.gradle.kts +++ b/documentation/documentation.gradle.kts @@ -5,16 +5,11 @@ import junitbuild.exec.RunConsoleLauncher import junitbuild.extensions.isSnapshot import junitbuild.extensions.javaModuleName import junitbuild.javadoc.ModuleSpecificJavadocFileOption -import org.asciidoctor.gradle.base.AsciidoctorAttributeProvider -import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask import org.gradle.api.tasks.PathSensitivity.RELATIVE -import org.ysb33r.grolifant.api.core.jvm.ExecutionMode.JAVA_EXEC plugins { - alias(libs.plugins.asciidoctorConvert) - alias(libs.plugins.asciidoctorPdf) - alias(libs.plugins.gitPublish) alias(libs.plugins.plantuml) + id("junitbuild.antora-conventions") id("junitbuild.build-parameters") id("junitbuild.kotlin-library-conventions") id("junitbuild.testing-conventions") @@ -84,14 +79,6 @@ dependencies { standaloneConsoleLauncher(projects.junitPlatformConsoleStandalone) } -asciidoctorj { - setJrubyVersion(libs.versions.jruby) - modules { - pdf.version(libs.versions.asciidoctorj.pdf) - } - requires(file("src/docs/asciidoc/resources/themes/rouge_junit.rb")) -} - val buildRevision: String by rootProject.extra val snapshot = version.isSnapshot() val docsVersion = if (snapshot) "snapshot" else version @@ -103,30 +90,6 @@ val userGuidePdfFileName = "junit-user-guide-${version}.pdf" val ota4jDocVersion = libs.versions.opentest4j.map { if (it.isSnapshot()) "snapshot" else it }.get() val apiGuardianDocVersion = libs.versions.apiguardian.map { if (it.isSnapshot()) "snapshot" else it }.get() -gitPublish { - repoUri = "https://github.com/junit-team/docs.junit.org.git" - - branch = "main" - sign = false - fetchDepth = 1 - - username = providers.environmentVariable("GIT_USERNAME") - password = providers.environmentVariable("GIT_PASSWORD") - - contents { - from(docsDir) - into(".") - } - - preserve { - include("**/*") - exclude("$docsVersion/**") - if (replaceCurrentDocs) { - exclude("current/**") - } - } -} - val generatedAsciiDocPath = layout.buildDirectory.dir("generated/asciidoc") val consoleLauncherOptionsFile = generatedAsciiDocPath.map { it.file("console-launcher-options.txt") } val consoleLauncherDiscoverOptionsFile = generatedAsciiDocPath.map { it.file("console-launcher-discover-options.txt") } @@ -270,8 +233,8 @@ tasks { val plantUmlOutputDirectory = plantUml.flatMap { it.outputDirectory } - withType().configureEach { - inputs.files( + val generateAsciidocInputs by registering { + dependsOn( generateConsoleLauncherOptions, generateConsoleLauncherDiscoverOptions, generateConsoleLauncherExecuteOptions, @@ -280,100 +243,6 @@ tasks { generateStandaloneConsoleLauncherShadowedArtifactsFile, plantUmlOutputDirectory ) - - resources { - from(sourceDir) { - include("**/images/**/*.png") - include("**/images/**/*.svg") - } - from(plantUmlOutputDirectory) { - into("user-guide/images") - } - } - - // Temporary workaround for https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/599 - inputs.dir(sourceDir).withPropertyName("sourceDir").withPathSensitivity(RELATIVE) - - attributeProviders += AsciidoctorAttributeProvider { - mapOf( - "version" to version, - "junit4-version" to libs.versions.junit4.get(), - "apiguardian-version" to libs.versions.apiguardian.get(), - "ota4j-version" to libs.versions.opentest4j.get(), - "surefire-version" to libs.versions.surefire.get(), - "release-branch" to releaseBranch, - "docs-version" to docsVersion, - "revnumber" to version, - "consoleLauncherOptionsFile" to consoleLauncherOptionsFile.get(), - "consoleLauncherDiscoverOptionsFile" to consoleLauncherDiscoverOptionsFile.get(), - "consoleLauncherExecuteOptionsFile" to consoleLauncherExecuteOptionsFile.get(), - "consoleLauncherEnginesOptionsFile" to consoleLauncherEnginesOptionsFile.get(), - "experimentalApisTableFile" to experimentalApisTableFile.get(), - "deprecatedApisTableFile" to deprecatedApisTableFile.get(), - "standaloneConsoleLauncherShadowedArtifactsFile" to standaloneConsoleLauncherShadowedArtifactsFile.get(), - "outdir" to outputDir.absolutePath, - "source-highlighter" to "rouge", - "rouge-style" to "junit", - "tabsize" to "4", - "toc" to "left", - "icons" to "font", - "sectanchors" to true, - "idprefix" to "", - "idseparator" to "-", - "jdk-javadoc-base-url" to jdkJavadocBaseUrl - ) - } - - sourceSets["test"].apply { - attributes(mapOf( - "testDir" to java.srcDirs.first(), - "testResourcesDir" to resources.srcDirs.first() - )) - inputs.dir(java.srcDirs.first()) - inputs.dir(resources.srcDirs.first()) - attributes(mapOf("kotlinTestDir" to kotlin.srcDirs.first())) - inputs.dir(kotlin.srcDirs.first()) - } - - jvm { - // To avoid warning, see https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/597 - jvmArgs( - "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", - "--add-opens", "java.base/java.io=ALL-UNNAMED" - ) - } - - notCompatibleWithConfigurationCache("https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/564") - } - - asciidoctor { - sources { - include("**/index.adoc") - } - resources { - from(sourceDir) { - include("tocbot-*/**") - } - } - attributes(mapOf( - "linkToPdf" to uploadPdfs, - "userGuidePdfFileName" to userGuidePdfFileName, - "releaseNotesUrl" to "../release-notes/index.html#release-notes" - )) - } - - asciidoctorPdf { - // Avoid classpath conflicts with other Gradle plugins (e.g. JReleaser) - // Avoid propagating apparent memory leaks in Asciidoctor/JRuby to Gradle daemon. - setExecutionMode(JAVA_EXEC) - jvm { - maxHeapSize = "512M" - } - sources { - include("user-guide/index.adoc") - } - copyAllResources() - attributes(mapOf("releaseNotesUrl" to "https://docs.junit.org/$docsVersion/release-notes/")) } val downloadJavadocElementLists by registering { @@ -496,58 +365,28 @@ tasks { into(layout.buildDirectory.dir("docs/fixedJavadoc")) } - val prepareDocsForUploadToGhPages by registering(Copy::class) { - dependsOn(fixJavadoc, asciidoctor, asciidoctorPdf) - outputs.dir(docsDir) - - from(asciidoctor.map { it.outputDir }) { - include("user-guide/**") - include("release-notes/**") - include("tocbot-*/**") - } - if (uploadPdfs) { - from(asciidoctorPdf.map { it.outputDir }) { - include("**/*.pdf") - rename { userGuidePdfFileName } - } - } - from(fixJavadoc.map { it.destinationDir }) { - into("api") - } - into(docsDir.map { it.dir(docsVersion.toString()) }) - includeEmptyDirs = false - } - - val createCurrentDocsFolder by registering(Copy::class) { - dependsOn(prepareDocsForUploadToGhPages) - onlyIf { replaceCurrentDocs } - - from(docsDir.map { it.dir(docsVersion.toString()) }) - into(docsDir.map { it.dir("current") }) - } - - val configureGitAuthor by registering { - dependsOn(gitPublishReset) - doFirst { - File(gitPublish.repoDir.get().asFile, ".git/config").appendText(""" - [user] - name = JUnit Team - email = team@junit.org - """.trimIndent()) - } - } - - gitPublishCopy { - dependsOn(prepareDocsForUploadToGhPages, createCurrentDocsFolder) - } - - gitPublishCommit { - dependsOn(configureGitAuthor) - } - val prepareGitHubAttestation by registering(Sync::class) { from(attestationClasspath) into(layout.buildDirectory.dir("attestation")) rename("(.*)-SNAPSHOT.jar", "$1-SNAPSHOT+${buildRevision.substring(0, 7)}.jar") } + + generateAntoraYml { + asciidocAttributes.putAll(provider { + mapOf( + "version" to project.version, + "junit4-version" to libs.versions.junit4.get(), + "apiguardian-version" to libs.versions.apiguardian.get(), + "ota4j-version" to libs.versions.opentest4j.get(), + "surefire-version" to libs.versions.surefire.get(), + "release-branch" to releaseBranch, + "docs-version" to docsVersion, + "jdk-javadoc-base-url" to jdkJavadocBaseUrl + ) + }) + } + + generateAntoraResources { + dependsOn(generateAsciidocInputs, fixJavadoc) + } } diff --git a/documentation/src/docs/asciidoc/user-guide/images/extensions_BrokenLifecycleMethodConfigDemo.png b/documentation/modules/ROOT/images/extensions_BrokenLifecycleMethodConfigDemo.png similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/extensions_BrokenLifecycleMethodConfigDemo.png rename to documentation/modules/ROOT/images/extensions_BrokenLifecycleMethodConfigDemo.png diff --git a/documentation/src/docs/asciidoc/user-guide/images/extensions_BrokenLifecycleMethodConfigDemo.txt b/documentation/modules/ROOT/images/extensions_BrokenLifecycleMethodConfigDemo.txt similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/extensions_BrokenLifecycleMethodConfigDemo.txt rename to documentation/modules/ROOT/images/extensions_BrokenLifecycleMethodConfigDemo.txt diff --git a/documentation/src/docs/asciidoc/user-guide/images/extensions_DatabaseTestsDemo.png b/documentation/modules/ROOT/images/extensions_DatabaseTestsDemo.png similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/extensions_DatabaseTestsDemo.png rename to documentation/modules/ROOT/images/extensions_DatabaseTestsDemo.png diff --git a/documentation/src/docs/asciidoc/user-guide/images/extensions_DatabaseTestsDemo.txt b/documentation/modules/ROOT/images/extensions_DatabaseTestsDemo.txt similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/extensions_DatabaseTestsDemo.txt rename to documentation/modules/ROOT/images/extensions_DatabaseTestsDemo.txt diff --git a/documentation/src/docs/asciidoc/user-guide/images/extensions_StoreHierarchy.svg b/documentation/modules/ROOT/images/extensions_StoreHierarchy.svg similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/extensions_StoreHierarchy.svg rename to documentation/modules/ROOT/images/extensions_StoreHierarchy.svg diff --git a/documentation/src/docs/asciidoc/user-guide/images/extensions_lifecycle.png b/documentation/modules/ROOT/images/extensions_lifecycle.png similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/extensions_lifecycle.png rename to documentation/modules/ROOT/images/extensions_lifecycle.png diff --git a/documentation/src/docs/asciidoc/user-guide/images/extensions_lifecycle_source.docx b/documentation/modules/ROOT/images/extensions_lifecycle_source.docx similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/extensions_lifecycle_source.docx rename to documentation/modules/ROOT/images/extensions_lifecycle_source.docx diff --git a/documentation/src/docs/asciidoc/user-guide/images/writing-tests_execution_mode.svg b/documentation/modules/ROOT/images/writing-tests_execution_mode.svg similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/writing-tests_execution_mode.svg rename to documentation/modules/ROOT/images/writing-tests_execution_mode.svg diff --git a/documentation/src/docs/asciidoc/user-guide/images/writing-tests_nested_test_ide.png b/documentation/modules/ROOT/images/writing-tests_nested_test_ide.png similarity index 100% rename from documentation/src/docs/asciidoc/user-guide/images/writing-tests_nested_test_ide.png rename to documentation/modules/ROOT/images/writing-tests_nested_test_ide.png diff --git a/documentation/modules/ROOT/nav.adoc b/documentation/modules/ROOT/nav.adoc new file mode 100644 index 000000000000..568e8e4dc25a --- /dev/null +++ b/documentation/modules/ROOT/nav.adoc @@ -0,0 +1,65 @@ +* xref:overview.adoc[] +* xref:writing-tests/intro.adoc[] +** xref:writing-tests/annotations.adoc[] +** xref:writing-tests/definitions.adoc[] +** xref:writing-tests/test-classes-and-methods.adoc[] +** xref:writing-tests/display-names.adoc[] +** xref:writing-tests/assertions.adoc[] +** xref:writing-tests/assumptions.adoc[] +** xref:writing-tests/exception-handling.adoc[] +** xref:writing-tests/disabling-tests.adoc[] +** xref:writing-tests/conditional-test-execution.adoc[] +** xref:writing-tests/tagging-and-filtering.adoc[] +** xref:writing-tests/test-execution-order.adoc[] +** xref:writing-tests/test-instance-lifecycle.adoc[] +** xref:writing-tests/nested-tests.adoc[] +** xref:writing-tests/dependency-injection-for-constructors-and-methods.adoc[] +** xref:writing-tests/test-interfaces-and-default-methods.adoc[] +** xref:writing-tests/repeated-tests.adoc[] +** xref:writing-tests/parameterized-classes-and-tests.adoc[] +** xref:writing-tests/class-templates.adoc[] +** xref:writing-tests/test-templates.adoc[] +** xref:writing-tests/dynamic-tests.adoc[] +** xref:writing-tests/timeouts.adoc[] +** xref:writing-tests/parallel-execution.adoc[] +** xref:writing-tests/built-in-extensions.adoc[] +* xref:migrating-from-junit4.adoc[] +* xref:running-tests/intro.adoc[] +** xref:running-tests/ide-support.adoc[] +** xref:running-tests/build-support.adoc[] +** xref:running-tests/console-launcher.adoc[] +** xref:running-tests/source-launcher.adoc[] +** xref:running-tests/discovery-selectors.adoc[] +** xref:running-tests/configuration-parameters.adoc[] +** xref:running-tests/tags.adoc[] +** xref:running-tests/capturing-standard-output-error.adoc[] +** xref:running-tests/using-listeners-and-interceptors.adoc[] +** xref:running-tests/stack-trace-pruning.adoc[] +** xref:running-tests/discovery-issues.adoc[] +* xref:extensions/overview.adoc[] +** xref:extensions/registering-extensions.adoc[] +** xref:extensions/conditional-test-execution.adoc[] +** xref:extensions/test-instance-pre-construct-callback.adoc[] +** xref:extensions/test-instance-factories.adoc[] +** xref:extensions/test-instance-post-processing.adoc[] +** xref:extensions/test-instance-pre-destroy-callback.adoc[] +** xref:extensions/parameter-resolution.adoc[] +** xref:extensions/test-result-processing.adoc[] +** xref:extensions/test-lifecycle-callbacks.adoc[] +** xref:extensions/exception-handling.adoc[] +** xref:extensions/pre-interrupt-callback.adoc[] +** xref:extensions/intercepting-invocations.adoc[] +** xref:extensions/providing-invocation-contexts-for-class-templates.adoc[] +** xref:extensions/providing-invocation-contexts-for-test-templates.adoc[] +** xref:extensions/keeping-state-in-extensions.adoc[] +** xref:extensions/supported-utilities-in-extensions.adoc[] +** xref:extensions/relative-execution-order-of-user-code-and-extensions.adoc[] +* Advanced Topics +** xref:advanced-topics/junit-platform-reporting.adoc[] +** xref:advanced-topics/junit-platform-suite-engine.adoc[] +** xref:advanced-topics/testkit.adoc[] +** xref:advanced-topics/launcher-api.adoc[] +** xref:advanced-topics/engines.adoc[] +* xref:api-evolution.adoc[] +* xref:release-notes.adoc[] +* xref:appendix.adoc[] diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc b/documentation/modules/ROOT/pages/advanced-topics/engines.adoc similarity index 86% rename from documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc rename to documentation/modules/ROOT/pages/advanced-topics/engines.adoc index 8876443f0461..c11272db9fd4 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc +++ b/documentation/modules/ROOT/pages/advanced-topics/engines.adoc @@ -1,14 +1,13 @@ -[[test-engines]] -=== Test Engines += Test Engines A `TestEngine` facilitates _discovery_ and _execution_ of tests for a particular programming model. For example, JUnit provides a `TestEngine` that discovers and executes tests written using -the JUnit Jupiter programming model (see <> and <>). +the JUnit Jupiter programming model (see xref:writing-tests/intro.adoc[] and xref:extensions/overview.adoc[]). -[[test-engines-junit]] -==== JUnit Test Engines +[[junit]] +== JUnit Test Engines JUnit provides three `TestEngine` implementations. @@ -18,8 +17,8 @@ JUnit provides three `TestEngine` implementations. * `{junit-platform-suite-engine}`: Executes declarative suites of tests with the JUnit Platform launcher infrastructure. -[[test-engines-custom]] -==== Custom Test Engines +[[custom]] +== Custom Test Engines You can contribute your own custom `{TestEngine}` by implementing the interfaces in the {junit-platform-engine} module and _registering_ your engine. @@ -46,7 +45,7 @@ annotation. For example, the `@Test` and `@TestFactory` annotations in JUnit Jup meta-annotated with `@Testable`. Consult the Javadoc for `{Testable}` for further details. If your custom `TestEngine` needs to be configured, consider allowing users to supply -configuration via <>. Please note, +configuration via xref:running-tests/configuration-parameters.adoc[configuration parameters]. Please note, however, that you are strongly encouraged to use a unique prefix for all configuration parameters supported by your test engine. Doing so will ensure that there are no conflicts between the names of your configuration parameters and those from other test engines. In @@ -58,7 +57,7 @@ parameters. Furthermore, as with the warning above regarding the `junit-` prefix configuration parameters. Although there is currently no official guide on how to implement a custom `TestEngine`, -you can consult the implementation of <> or the implementation of +you can consult the implementation of <> or the implementation of third-party test engines listed in the https://github.com/junit-team/junit-framework/wiki/Third-party-Extensions#junit-platform-test-engines[JUnit wiki]. You will also find various tutorials and blogs on the Internet that demonstrate how to @@ -69,8 +68,8 @@ NOTE: `{HierarchicalTestEngine}` is a convenient abstract base implementation of provide the logic for test discovery. It implements execution of `TestDescriptors` that implement the `Node` interface, including support for parallel execution. -[[test-engines-registration]] -==== Registering a TestEngine +[[registration]] +== Registering a TestEngine `TestEngine` registration is supported via Java's `{ServiceLoader}` mechanism. @@ -79,15 +78,15 @@ For example, the `junit-jupiter-engine` module registers its `org.junit.platform.engine.TestEngine` within the `/META-INF/services` folder in the `junit-jupiter-engine` JAR. -[[test-engines-requirements]] -==== Requirements +[[requirements]] +== Requirements NOTE: The words "must", "must not", "required", "shall", "shall not", "should", "should not", "recommended", "may", and "optional" in this section are to be interpreted as described in https://www.ietf.org/rfc/rfc2119.txt[RFC 2119.] -[[test-engines-requirements-mandatory]] -===== Mandatory requirements +[[requirements-mandatory]] +=== Mandatory requirements For interoperability with build tools and IDEs, `TestEngine` implementations must adhere to the following requirements: @@ -107,8 +106,8 @@ to the following requirements: after their children. If a node is reported as skipped, there _must not_ be any events reported for its descendants. -[[test-engines-requirements-enhanced-compatibility]] -===== Enhanced compatibility +[[requirements-enhanced-compatibility]] +=== Enhanced compatibility Adhering to the following requirements is optional but recommended for enhanced compatibility with build tools and IDEs: @@ -120,9 +119,9 @@ compatibility with build tools and IDEs: * When resolving `UniqueIdSelectors`, a `TestEngine` _should_ only return `TestDescriptor` instances with matching unique IDs including their ancestors but _may_ return additional siblings or other nodes that are required for the execution of the selected tests. -* `TestEngines` _should_ support <> tests and containers so +* `TestEngines` _should_ support xref:running-tests/tags.adoc[tagging] tests and containers so that tag filters can be applied when discovering tests. -* [[test-engines-requirements-cancellation]] A `TestEngine` _should_ cancel its execution +* [[requirements-cancellation]] A `TestEngine` _should_ cancel its execution when the `{CancellationToken}` it is passed as part of the `ExecutionRequest` indicates that cancellation has been requested. In this case, it _should_ report any remaining `TestDescriptors` as skipped but not report any events for their descendants. It _may_ @@ -130,10 +129,10 @@ compatibility with build tools and IDEs: completely. If a `TestEngine` supports cancellation, it should clean up any resources that it has created just like if execution had finished regularly. -[[test-engines-discovery-issues]] -==== Reporting Discovery Issues +[[discovery-issues]] +== Reporting Discovery Issues -Test engines should report <> if they +Test engines should report xref:running-tests/discovery-issues.adoc[discovery issues] if they encounter any problems or potential misconfigurations during test discovery. This is especially important if the issue could lead to tests not being executed at all or only partially. @@ -144,7 +143,7 @@ In order to report a `{DiscoveryIssue}`, a test engine should call the listener around, the `{DiscoveryIssueReporter}` interface should be used. It also provides a way to create a `Condition` that reports a discovery issue if its check fails and may be used as a `Predicate` or `Consumer`. Please refer to the implementations of the -<> for examples. +<> for examples. -Moreover, <> provides a way to write tests for +Moreover, xref:advanced-topics/testkit.adoc#engine-discovery[Engine Test Kit] provides a way to write tests for reported discovery issues. diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc b/documentation/modules/ROOT/pages/advanced-topics/junit-platform-reporting.adoc similarity index 82% rename from documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc rename to documentation/modules/ROOT/pages/advanced-topics/junit-platform-reporting.adoc index 10f7744f54b0..fe74bef80896 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-reporting.adoc +++ b/documentation/modules/ROOT/pages/advanced-topics/junit-platform-reporting.adoc @@ -1,21 +1,20 @@ -[[junit-platform-reporting]] -=== JUnit Platform Reporting += JUnit Platform Reporting The `junit-platform-reporting` artifact contains `{TestExecutionListener}` implementations that generate XML test reports in two flavors: -<> and -<>. +<> and +<>. NOTE: The module also contains other `TestExecutionListener` implementations that can be -used to build custom reporting. See <> for details. +used to build custom reporting. See xref:running-tests/using-listeners-and-interceptors.adoc[] for details. -[[junit-platform-reporting-output-directory]] -==== Output Directory +[[output-directory]] +== Output Directory The JUnit Platform provides an `{OutputDirectoryCreator}` via `{EngineDiscoveryRequest}` -and `{TestPlan}` to registered <> and -<>, respectively. Its root directory can be configured -via the following <>: +and `{TestPlan}` to registered xref:advanced-topics/engines.adoc[test engines] and +xref:running-tests/using-listeners-and-interceptors.adoc[listeners], respectively. Its root directory can be configured +via the following xref:running-tests/configuration-parameters.adoc[configuration parameter]: `junit.platform.reporting.output.dir=`:: Configure the output directory for reporting. By default, `build` is used if a Gradle @@ -28,15 +27,15 @@ directories like `reports/junit-8803697269315188212`. This can be useful when us Gradle's or Maven's parallel execution capabilities which create multiple JVM forks that run concurrently. -[[junit-platform-reporting-open-test-reporting]] -==== Open Test Reporting +[[open-test-reporting]] +== Open Test Reporting `{OpenTestReportGeneratingListener}` writes an XML report for the entire execution in the event-based format specified by {OpenTestReporting} which supports all features of the JUnit Platform such as hierarchical test structures, display names, tags, etc. The listener is auto-registered and can be configured via the following -<>: +xref:running-tests/configuration-parameters.adoc[configuration parameters]: `junit.platform.reporting.open.xml.enabled=true|false`:: Enable/disable writing the report; defaults to `false`. @@ -48,17 +47,17 @@ The listener is auto-registered and can be configured via the following connection is automatically closed when the test execution completes. If enabled, the listener creates an XML report file named `open-test-report.xml` in the -configured <>, unless the +configured <>, unless the `junit.platform.reporting.open.xml.socket` configuration parameter is set, in which case the events are sent to the specified socket instead. -If <> is enabled, the captured output +If xref:running-tests/capturing-standard-output-error.adoc[output capturing] is enabled, the captured output written to `System.out` and `System.err` will be included in the report as well. TIP: The {OpenTestReportingCliTool} can be used to convert from the event-based format to the hierarchical format which is more human-readable. -===== Gradle +=== Gradle For Gradle, writing Open Test Reporting compatible XML reports can be enabled and configured via system properties. The following samples configure its output directory to @@ -102,7 +101,7 @@ tasks.withType().configureEach { } ---- -===== Maven +=== Maven For Maven Surefire/Failsafe, you can enable Open Test Reporting output and configure the resulting XML files to be written to the same directory Surefire/Failsafe uses for its own @@ -141,9 +140,9 @@ XML reports as follows: ---- -===== Console Launcher +=== Console Launcher -When using the <>, you can enable Open Test Reporting +When using the xref:running-tests/console-launcher.adoc[], you can enable Open Test Reporting output by setting the configuration parameters via `--config`: [source,console,subs=attributes+] @@ -162,12 +161,12 @@ $ java -jar junit-platform-console-standalone-{version}.jar \ --config-resource=configuration.properties ---- -[[junit-platform-reporting-legacy-xml]] -==== Legacy XML format +[[legacy-xml]] +== Legacy XML format `{LegacyXmlReportGeneratingListener}` generates a separate XML report for each root in the `{TestPlan}`. Note that the generated XML format is compatible with the de facto standard for JUnit 4 based test reports that was made popular by the Ant build system. -The `LegacyXmlReportGeneratingListener` is used by the <> +The `LegacyXmlReportGeneratingListener` is used by the xref:running-tests/console-launcher.adoc[] as well. diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc b/documentation/modules/ROOT/pages/advanced-topics/junit-platform-suite-engine.adoc similarity index 73% rename from documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc rename to documentation/modules/ROOT/pages/advanced-topics/junit-platform-suite-engine.adoc index 57c1f80dfa5f..8466f1fa2c62 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/junit-platform-suite-engine.adoc +++ b/documentation/modules/ROOT/pages/advanced-topics/junit-platform-suite-engine.adoc @@ -1,20 +1,19 @@ -[[junit-platform-suite-engine]] -=== JUnit Platform Suite Engine += JUnit Platform Suite Engine The Suite Engine supports the declarative selection and execution of tests from _any_ test -engine on the JUnit Platform using the <>. +engine on the JUnit Platform using the xref:advanced-topics/launcher-api.adoc[]. image::junit-platform-suite-engine-diagram.svg[role=text-center] -[[junit-platform-suite-engine-setup]] -==== Setup +[[setup]] +== Setup In addition to the `junit-platform-suite-api` and `junit-platform-suite-engine` artifacts, you need _at least one_ other test engine and its dependencies on the classpath. See -<> for details regarding group IDs, artifact IDs, and versions. +xref:appendix.adoc#dependency-metadata[Dependency Metadata] for details regarding group IDs, artifact IDs, and versions. -[[junit-platform-suite-engine-setup-required-dependencies]] -===== Required Dependencies +[[setup-required-dependencies]] +=== Required Dependencies * `junit-platform-suite-api` in _test_ scope: artifact containing annotations needed to configure a test suite @@ -25,16 +24,16 @@ NOTE: Both of the required dependencies are aggregated in the `junit-platform-su artifact which can be declared in _test_ scope instead of declaring explicit dependencies on `junit-platform-suite-api` and `junit-platform-suite-engine`. -[[junit-platform-suite-engine-setup-transitive-dependencies]] -===== Transitive Dependencies +[[setup-transitive-dependencies]] +=== Transitive Dependencies * `junit-platform-launcher` in _test_ scope * `junit-platform-engine` in _test_ scope * `junit-platform-commons` in _test_ scope * `opentest4j` in _test_ scope -[[junit-platform-suite-engine-example]] -==== @Suite Example +[[example]] +== @Suite Example Annotate a class with `@Suite` to have it marked as a test suite on the JUnit Platform. As seen in the following example, selector and filter annotations can be used to control the @@ -42,7 +41,7 @@ contents of the suite. [source,java,indent=0] ---- -include::{testDir}/example/SuiteDemo.java[tags=user_guide] +include::example$java/example/SuiteDemo.java[tags=user_guide] ---- .Additional Configuration Options @@ -50,7 +49,7 @@ NOTE: There are numerous configuration options for discovering and filtering tes test suite. Please consult the Javadoc of the `{suite-api-package}` package for a full list of supported annotations and further details. -==== @BeforeSuite and @AfterSuite +== @BeforeSuite and @AfterSuite `@BeforeSuite` and `@AfterSuite` annotations can be used on methods inside a `@Suite`-annotated class. They will be executed before and after all tests of the test @@ -58,11 +57,11 @@ suite, respectively. [source,java,indent=0] ---- -include::{testDir}/example/BeforeAndAfterSuiteDemo.java[tags=user_guide] +include::example$java/example/BeforeAndAfterSuiteDemo.java[tags=user_guide] ---- -[[junit-platform-suite-engine-duplicate-test-execution]] -==== Duplicate Test Execution +[[duplicate-test-execution]] +== Duplicate Test Execution Depending on the declared selectors, different suites may contain the same tests, potentially with different configurations. Moreover, tests in a suite are executed in @@ -76,5 +75,5 @@ include only the `junit-platform-suite` engine, or use a custom naming pattern. example, name all suites `*Suite` and all tests `*Test`, and configure your build tool to include only the former. -Alternatively, consider <> to select specific groups of +Alternatively, consider xref:running-tests/tags.adoc[using tags] to select specific groups of tests. diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc b/documentation/modules/ROOT/pages/advanced-topics/launcher-api.adoc similarity index 79% rename from documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc rename to documentation/modules/ROOT/pages/advanced-topics/launcher-api.adoc index 434761485d2a..903591aabb82 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/launcher-api.adoc +++ b/documentation/modules/ROOT/pages/advanced-topics/launcher-api.adoc @@ -1,8 +1,4 @@ -:testDir: ../../../../../src/test/java -:testResourcesDir: ../../../../../src/test/resources - -[[launcher-api]] -=== JUnit Platform Launcher API += JUnit Platform Launcher API One of the prominent goals of the JUnit Platform is to make the interface between JUnit and its programmatic clients – build tools and IDEs – more powerful and stable. The @@ -12,7 +8,7 @@ filtering and configuration that is necessary from the outside. JUnit Platform provides a `Launcher` API that can be used to discover, filter, and execute tests. Moreover, third party test libraries – like Spock or Cucumber – can plug into the JUnit Platform's launching infrastructure by providing a custom -<>. +xref:advanced-topics/engines.adoc[TestEngine]. image::launcher-api-diagram.svg[role=text-center] @@ -21,8 +17,8 @@ The launcher API is in the `{junit-platform-launcher}` module. An example consumer of the launcher API is the `{ConsoleLauncher}` in the `{junit-platform-console}` project. -[[launcher-api-discovery]] -==== Discovering Tests +[[discovery]] +== Discovering Tests Having _test discovery_ as a dedicated feature of the platform frees IDEs and build tools from most of the difficulties they had to go through to identify test classes and test @@ -32,12 +28,12 @@ Usage Example: [source,java,indent=0] ---- -include::{testDir}/example/UsingTheLauncherForDiscoveryDemo.java[tags=imports] +include::example$java/example/UsingTheLauncherForDiscoveryDemo.java[tags=imports] ---- [source,java,indent=0] ---- -include::{testDir}/example/UsingTheLauncherForDiscoveryDemo.java[tags=discovery] +include::example$java/example/UsingTheLauncherForDiscoveryDemo.java[tags=discovery] ---- You can select classes, methods, and all classes in a package or even search for all tests @@ -55,11 +51,11 @@ Clients can register one or more `{LauncherDiscoveryListener}` implementations v discovery. By default, the builder registers an "abort on failure" listener that aborts test discovery after the first discovery failure is encountered. The default `LauncherDiscoveryListener` can be changed via the -`junit.platform.discovery.listener.default` <>. +`junit.platform.discovery.listener.default` xref:running-tests/configuration-parameters.adoc[configuration +parameter]. -[[launcher-api-execution]] -==== Executing Tests +[[execution]] +== Executing Tests To execute tests, clients can use the same `LauncherDiscoveryRequest` as in the discovery phase or create a new request. Test progress and reporting can be achieved by registering @@ -68,7 +64,7 @@ following example. [source,java,indent=0] ---- -include::{testDir}/example/UsingTheLauncherDemo.java[tags=execution] +include::example$java/example/UsingTheLauncherDemo.java[tags=execution] ---- There is no return value for the `execute()` method, but you can use a @@ -81,14 +77,14 @@ events are called in registration order while methods for finish events are call reverse order. Test case execution won't start before all `executionStarted` calls have returned. -[[launcher-api-engines-custom]] -==== Registering a TestEngine +[[engines-custom]] +== Registering a TestEngine -See the dedicated section on <> for +See the dedicated section on xref:advanced-topics/engines.adoc#registration[TestEngine registration] for details. -[[launcher-api-post-discovery-filters-custom]] -==== Registering a PostDiscoveryFilter +[[post-discovery-filters-custom]] +== Registering a PostDiscoveryFilter In addition to specifying post-discovery filters as part of a `{LauncherDiscoveryRequest}` passed to the `{Launcher}` API, `{PostDiscoveryFilter}` implementations will be discovered @@ -99,8 +95,8 @@ For example, an `example.CustomTagFilter` class implementing `PostDiscoveryFilte declared within the `/META-INF/services/org.junit.platform.launcher.PostDiscoveryFilter` file is loaded and applied automatically. -[[launcher-api-launcher-session-listeners-custom]] -==== Registering a LauncherSessionListener +[[launcher-session-listeners-custom]] +== Registering a LauncherSessionListener Registered implementations of `{LauncherSessionListener}` are notified when a `{LauncherSession}` is opened (before a `{Launcher}` first discovers and executes tests) @@ -109,8 +105,8 @@ 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.) -[[launcher-api-launcher-session-listeners-tool-support]] -===== Tool Support +[[launcher-session-listeners-tool-support]] +=== Tool Support The following build tools and IDEs are known to provide full support for `LauncherSession`: @@ -120,8 +116,8 @@ The following build tools and IDEs are known to provide full support for `Launch Other tools might also work but have not been tested explicitly. -[[launcher-api-launcher-session-listeners-tool-example-usage]] -===== Example Usage +[[launcher-session-listeners-tool-example-usage]] +=== Example Usage 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, @@ -135,7 +131,7 @@ executed, could look like this: ---- package example.session; -include::{testDir}/example/session/GlobalSetupTeardownListener.java[tags=user_guide] +include::example$java/example/session/GlobalSetupTeardownListener.java[tags=user_guide] ---- <1> Get the store from the launcher session <2> Lazily create the HTTP server and put it into the store @@ -149,7 +145,7 @@ closed: ---- package example.session; -include::{testDir}/example/session/CloseableHttpServer.java[tags=user_guide] +include::example$java/example/session/CloseableHttpServer.java[tags=user_guide] ---- <1> The `close()` method is called when the launcher session is closed <2> Stop the HTTP server @@ -163,7 +159,7 @@ 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[] +include::example$resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener[] ---- You can now use the resource from your test: @@ -173,7 +169,7 @@ You can now use the resource from your test: ---- package example.session; -include::{testDir}/example/session/HttpTests.java[tags=user_guide] +include::example$java/example/session/HttpTests.java[tags=user_guide] ---- <1> Retrieve the HTTP server instance from the store <2> Get the host string directly from the injected HTTP server instance @@ -181,22 +177,21 @@ include::{testDir}/example/session/HttpTests.java[tags=user_guide] <4> Send a request to the server <5> Check the status code of the response -[[launcher-api-launcher-interceptors-custom]] -==== Registering a LauncherInterceptor +[[launcher-interceptors-custom]] +== Registering a LauncherInterceptor In order to intercept the creation of instances of `{Launcher}` and `{LauncherSessionListener}` and calls to the `discover` and `execute` methods of the former, clients can register custom implementations of `{LauncherInterceptor}` via Java's `{ServiceLoader}` mechanism by setting the -`junit.platform.launcher.interceptors.enabled` <> to `true`. +`junit.platform.launcher.interceptors.enabled` xref:running-tests/configuration-parameters.adoc[configuration parameter] to `true`. [NOTE] ==== Since interceptors are registered before the test run starts, the `junit.platform.launcher.interceptors.enabled` _configuration parameter_ can only be supplied as a JVM system property or via the JUnit Platform configuration file (see -<> for details). This _configuration parameter_ cannot be +xref:running-tests/configuration-parameters.adoc[] for details). This _configuration parameter_ cannot be supplied in the `LauncherDiscoveryRequest` that is passed to the `{Launcher}`. ==== @@ -206,11 +201,11 @@ the JUnit Platform to load test classes and engine implementations. [source,java] ---- -include::{testDir}/example/CustomLauncherInterceptor.java[tags=user_guide] +include::example$java/example/CustomLauncherInterceptor.java[tags=user_guide] ---- -[[launcher-api-launcher-discovery-listeners-custom]] -==== Registering a LauncherDiscoveryListener +[[launcher-discovery-listeners-custom]] +== Registering a LauncherDiscoveryListener In addition to specifying discovery listeners as part of a `{LauncherDiscoveryRequest}` or registering them programmatically via the `{Launcher}` API, custom @@ -223,8 +218,8 @@ For example, an `example.CustomLauncherDiscoveryListener` class implementing `/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener` file is loaded and registered automatically. -[[launcher-api-listeners-custom]] -==== Registering a TestExecutionListener +[[listeners-custom]] +== Registering a TestExecutionListener In addition to the public `{Launcher}` API method for registering test execution listeners programmatically, custom `{TestExecutionListener}` implementations will be discovered at @@ -236,22 +231,22 @@ For example, an `example.CustomTestExecutionListener` class implementing `/META-INF/services/org.junit.platform.launcher.TestExecutionListener` file is loaded and registered automatically. -[[launcher-api-listeners-config]] -==== Configuring a TestExecutionListener +[[listeners-config]] +== Configuring a TestExecutionListener When a `{TestExecutionListener}` is registered programmatically via the `{Launcher}` API, the listener may provide programmatic ways for it to be configured -- for example, via its constructor, setter methods, etc. However, when a `TestExecutionListener` is registered automatically via Java's `ServiceLoader` mechanism (see -<>), there is no way for the user to directly configure the +<>), there is no way for the user to directly configure the listener. In such cases, the author of a `TestExecutionListener` may choose to make the -listener configurable via <>. The +listener configurable via xref:running-tests/configuration-parameters.adoc[configuration parameters]. The listener can then access the configuration parameters via the `TestPlan` supplied to the `testPlanExecutionStarted(TestPlan)` and `testPlanExecutionFinished(TestPlan)` callback methods. See the `{UniqueIdTrackingListener}` for an example. -[[launcher-api-listeners-custom-deactivation]] -==== Deactivating a TestExecutionListener +[[listeners-custom-deactivation]] +== Deactivating a TestExecutionListener Sometimes it can be useful to run a test suite _without_ certain execution listeners being active. For example, you might have custom a `{TestExecutionListener}` that sends the test @@ -271,17 +266,17 @@ deactivated. In other words, any `TestExecutionListener` registered explicitly v In addition, since execution listeners are registered before the test run starts, the `junit.platform.execution.listeners.deactivate` _configuration parameter_ can only be supplied as a JVM system property or via the JUnit Platform configuration file (see -<> for details). This _configuration parameter_ cannot be +xref:running-tests/configuration-parameters.adoc[] for details). This _configuration parameter_ cannot be supplied in the `LauncherDiscoveryRequest` that is passed to the `{Launcher}`. ==== -[[launcher-api-listeners-custom-deactivation-pattern]] -===== Pattern Matching Syntax +[[listeners-custom-deactivation-pattern]] +=== Pattern Matching Syntax -Refer to <> for details. +Refer to xref:running-tests/configuration-parameters.adoc#pattern[Pattern Matching Syntax] for details. -[[launcher-api-launcher-config]] -==== Configuring the Launcher +[[launcher-config]] +== Configuring the Launcher If you require fine-grained control over automatic detection and registration of test engines and listeners, you may create an instance of `{LauncherConfig}` and supply that to @@ -290,22 +285,21 @@ built-in fluent _builder_ API, as demonstrated in the following example. [source,java,indent=0] ---- -include::{testDir}/example/UsingTheLauncherDemo.java[tags=launcherConfig] +include::example$java/example/UsingTheLauncherDemo.java[tags=launcherConfig] ---- -[[launcher-api-dry-run-mode]] -==== Dry-Run Mode +[[dry-run-mode]] +== Dry-Run Mode When running tests via the `{Launcher}` API, you can enable _dry-run mode_ by setting the -`junit.platform.execution.dryRun.enabled` <> to `true`. In this mode, the `{Launcher}` will not actually +`junit.platform.execution.dryRun.enabled` xref:running-tests/configuration-parameters.adoc[configuration parameter] to `true`. In this mode, the `{Launcher}` will not actually execute any tests but will notify registered `{TestExecutionListener}` instances as if all tests had been skipped and their containers had been successful. This can be useful to test changes in the configuration of a build or to verify a listener is called as expected without having to wait for all tests to be executed. -[[launcher-api-managing-state-across-test-engines]] -==== Managing State Across Test Engines +[[managing-state-across-test-engines]] +== Managing State Across Test Engines When running tests on the JUnit Platform, multiple test engines may need to access shared resources. Rather than initializing these resources multiple times, JUnit Platform @@ -324,7 +318,7 @@ global store or creates a new one if it doesn't exist: [source,java] ---- -include::{testDir}/example/FirstCustomEngine.java[tags=user_guide] +include::example$java/example/FirstCustomEngine.java[tags=user_guide] ---- `SecondCustomEngine` follows the same pattern, ensuring that regardless whether it runs @@ -332,7 +326,7 @@ before or after `FirstCustomEngine`, it will use the same socket instance: [source,java] ---- -include::{testDir}/example/SecondCustomEngine.java[tags=user_guide] +include::example$java/example/SecondCustomEngine.java[tags=user_guide] ---- TIP: In this case, the `ServerSocket` can be stored directly in the global store while @@ -346,7 +340,7 @@ For illustration, the following test verifies that both engines are sharing the [source,java,indent=0] ---- -include::{testDir}/example/sharedresources/SharedResourceDemo.java[tags=user_guide] +include::example$java/example/sharedresources/SharedResourceDemo.java[tags=user_guide] ---- By using the Platform's `{NamespacedHierarchicalStore}` API with shared namespaces in this @@ -354,10 +348,10 @@ way, test engines can coordinate resource creation and sharing without direct de between them. Alternatively, it's possible to inject resources into test engines by -<>. +<>. -[[launcher-api-launcher-cancellation]] -==== Cancelling a Running Test Execution +[[launcher-cancellation]] +== Cancelling a Running Test Execution The launcher API provides the ability to cancel a running test execution mid-flight while allowing engines to clean up resources. To request an execution to be cancelled, you need @@ -369,7 +363,7 @@ failed can be achieved as follows. [source,java,indent=0] ---- -include::{testDir}/example/UsingTheLauncherDemo.java[tags=cancellation] +include::example$java/example/UsingTheLauncherDemo.java[tags=cancellation] ---- <1> Create a `{CancellationToken}` <2> Implement a `{TestExecutionListener}` that calls `cancel()` when a test fails @@ -380,9 +374,9 @@ include::{testDir}/example/UsingTheLauncherDemo.java[tags=cancellation] [NOTE] .Test Engine Support for Cancellation ==== -Cancelling tests relies on <> checking and responding to the +Cancelling tests relies on xref:advanced-topics/engines.adoc[] checking and responding to the `{CancellationToken}` appropriately (see -<> for details). The +xref:advanced-topics/engines.adoc#requirements-cancellation[Test Engine Requirements] for details). The `Launcher` will also check the token and cancel test execution when multiple test engines are present at runtime. diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc b/documentation/modules/ROOT/pages/advanced-topics/testkit.adoc similarity index 82% rename from documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc rename to documentation/modules/ROOT/pages/advanced-topics/testkit.adoc index b21678c3f38c..7900df1623d8 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/testkit.adoc +++ b/documentation/modules/ROOT/pages/advanced-topics/testkit.adoc @@ -1,14 +1,11 @@ -:testDir: ../../../../../src/test/java - -[[testkit]] -=== JUnit Platform Test Kit += JUnit Platform Test Kit The `junit-platform-testkit` artifact provides support for executing a test plan on the JUnit Platform and then verifying the expected results. This support is currently limited -to the execution of a single `TestEngine` (see <>). +to the execution of a single `TestEngine` (see <>). -[[testkit-engine]] -==== Engine Test Kit +[[engine]] +== Engine Test Kit The `{testkit-engine-package}` package provides support for discovering and executing a `{TestPlan}` for a given `{TestEngine}` running on the JUnit Platform and then accessing @@ -24,10 +21,10 @@ to build your `LauncherDiscoveryRequest`, you must use one of the `discover()` o The following test class written using JUnit Jupiter will be used in subsequent examples. -[[testkit-engine-ExampleTestCase]] +[[engine-ExampleTestCase]] [source,java,indent=0] ---- -include::{testDir}/example/ExampleTestCase.java[tags=user_guide] +include::example$java/example/ExampleTestCase.java[tags=user_guide] ---- For the sake of brevity, the following sections demonstrate how to test JUnit's own @@ -36,24 +33,24 @@ own `TestEngine` implementation, you need to use its unique engine ID. Alternati may test your own `TestEngine` by supplying an instance of it to the `EngineTestKit.engine(TestEngine)` static factory method. -[[testkit-engine-discovery]] -==== Verifying Test Discovery +[[engine-discovery]] +== Verifying Test Discovery The following test demonstrates how to verify that a `TestPlan` was discovered as expected by the JUnit Jupiter `TestEngine`. [source,java,indent=0] ---- -include::{testDir}/example/testkit/EngineTestKitDiscoveryDemo.java[tags=user_guide] +include::example$java/example/testkit/EngineTestKitDiscoveryDemo.java[tags=user_guide] ---- <1> Select the JUnit Jupiter `TestEngine`. -<2> Select the <> test class. +<2> Select the <> test class. <3> Discover the `TestPlan`. <4> Assert engine root descriptor has expected display name. <5> Assert no discovery issues were encountered. -[[testkit-engine-statistics]] -==== Asserting Execution Statistics +[[engine-statistics]] +== Asserting Execution Statistics One of the most common features of the Test Kit is the ability to assert statistics against events fired during the execution of a `TestPlan`. The following tests demonstrate @@ -62,10 +59,10 @@ For details on what statistics are available, consult the Javadoc for `{EventSta [source,java,indent=0] ---- -include::{testDir}/example/testkit/EngineTestKitStatisticsDemo.java[tags=user_guide] +include::example$java/example/testkit/EngineTestKitStatisticsDemo.java[tags=user_guide] ---- <1> Select the JUnit Jupiter `TestEngine`. -<2> Select the <> test class. +<2> Select the <> test class. <3> Execute the `TestPlan`. <4> Filter by _container_ events. <5> Assert statistics for _container_ events. @@ -74,17 +71,17 @@ include::{testDir}/example/testkit/EngineTestKitStatisticsDemo.java[tags=user_gu NOTE: In the `verifyJupiterContainerStats()` test method, the counts for the `started` and `succeeded` statistics are `2` since the `JupiterTestEngine` and the -<> class are both considered containers. +<> class are both considered containers. -[[testkit-engine-events]] -==== Asserting Events +[[engine-events]] +== Asserting Events -If you find that <> alone is insufficient +If you find that <> alone is insufficient for verifying the expected behavior of test execution, you can work directly with the recorded `{Event}` elements and perform assertions against them. For example, if you want to verify the reason that the `skippedTest()` method in -<> was skipped, you can do that as +<> was skipped, you can do that as follows. [TIP] @@ -99,11 +96,10 @@ events, consult the Javadoc for `{EventConditions}`. [source,java,indent=0] ---- -include::{testDir}/example/testkit/EngineTestKitSkippedMethodDemo.java[tags=user_guide] +include::example$java/example/testkit/EngineTestKitSkippedMethodDemo.java[tags=user_guide] ---- <1> Select the JUnit Jupiter `TestEngine`. -<2> Select the `skippedTest()` method in the <> test class. +<2> Select the `skippedTest()` method in the <> test class. <3> Execute the `TestPlan`. <4> Filter by _test_ events. <5> Save the _test_ `Events` to a local variable. @@ -112,7 +108,7 @@ include::{testDir}/example/testkit/EngineTestKitSkippedMethodDemo.java[tags=user `skippedTest` with `"for demonstration purposes"` as the _reason_. If you want to verify the type of exception thrown from the `failingTest()` method in -<>, you can do that as follows. +<>, you can do that as follows. [TIP] ==== @@ -123,10 +119,10 @@ events and execution results, consult the Javadoc for `{EventConditions}` and [source,java,indent=0] ---- -include::{testDir}/example/testkit/EngineTestKitFailedMethodDemo.java[tags=user_guide] +include::example$java/example/testkit/EngineTestKitFailedMethodDemo.java[tags=user_guide] ---- <1> Select the JUnit Jupiter `TestEngine`. -<2> Select the <> test class. +<2> Select the <> test class. <3> Execute the `TestPlan`. <4> Filter by _test_ events. <5> Assert that the recorded _test_ events contain exactly one failing test named @@ -140,7 +136,7 @@ achieve this via the `assertEventsMatchExactly()` method in the `EngineTestKit` [TIP] ==== Since `assertEventsMatchExactly()` matches conditions exactly in the order in which the -events were fired, <> has been +events were fired, <> has been annotated with `@TestMethodOrder(OrderAnnotation.class)` and each test method has been annotated with `@Order(...)`. This allows us to enforce the order in which the test methods are executed, which in turn allows our `verifyAllJupiterEvents()` test to be @@ -153,10 +149,10 @@ respectively. [source,java,indent=0] ---- -include::{testDir}/example/testkit/EngineTestKitAllEventsDemo.java[tags=user_guide] +include::example$java/example/testkit/EngineTestKitAllEventsDemo.java[tags=user_guide] ---- <1> Select the JUnit Jupiter `TestEngine`. -<2> Select the <> test class. +<2> Select the <> test class. <3> Execute the `TestPlan`. <4> Filter by _all_ events. <5> Print all events to the supplied `writer` for debugging purposes. Debug information diff --git a/documentation/src/docs/asciidoc/user-guide/api-evolution.adoc b/documentation/modules/ROOT/pages/api-evolution.adoc similarity index 88% rename from documentation/src/docs/asciidoc/user-guide/api-evolution.adoc rename to documentation/modules/ROOT/pages/api-evolution.adoc index 601299666251..87570809f5f7 100644 --- a/documentation/src/docs/asciidoc/user-guide/api-evolution.adoc +++ b/documentation/modules/ROOT/pages/api-evolution.adoc @@ -1,5 +1,5 @@ -[[api-evolution]] -== API Evolution += API Evolution +:page-toclevels: 1 One of the major goals of the JUnit Platform architecture is to improve maintainers' capabilities to evolve JUnit despite its being used in many projects. With JUnit 4 a lot @@ -10,8 +10,8 @@ sometimes impossible. That's why JUnit now uses a defined lifecycle for all publicly available interfaces, classes, and methods. -[[api-evolution-version-and-status]] -=== API Version and Status +[[version-and-status]] +== API Version and Status Every published artifact has a version number `..`, and all publicly available interfaces, classes, and methods are annotated with {API} from the @@ -38,25 +38,25 @@ If the `@API` annotation is present on a type, it is considered to be applicable public members of that type as well. A member is allowed to declare a different `status` value of lower stability. -[[api-evolution-experimental-apis]] -=== Experimental APIs +[[experimental-apis]] +== Experimental APIs The following tables list which APIs are currently designated as _experimental_ via `@API(status = EXPERIMENTAL)`. Caution should be taken when relying on such APIs. -include::{experimentalApisTableFile}[] +include::partial$experimental-apis-table.adoc[] -[[api-evolution-deprecated-apis]] -=== Deprecated APIs +[[deprecated-apis]] +== Deprecated APIs The following tables list which APIs are currently designated as _deprecated_ via `@API(status = DEPRECATED)`. You should avoid using deprecated APIs whenever possible, since such APIs will likely be removed in an upcoming release. -include::{deprecatedApisTableFile}[] +include::partial$deprecated-apis-table.adoc[] -[[api-evolution-tooling]] -=== @API Tooling Support +[[tooling]] +== @API Tooling Support The {API_Guardian} project plans to provide tooling support for publishers and consumers of APIs annotated with {API}. For example, the tooling support will likely provide a diff --git a/documentation/src/docs/asciidoc/user-guide/appendix.adoc b/documentation/modules/ROOT/pages/appendix.adoc similarity index 81% rename from documentation/src/docs/asciidoc/user-guide/appendix.adoc rename to documentation/modules/ROOT/pages/appendix.adoc index ea11c07b7dea..4cb40e7240bd 100644 --- a/documentation/src/docs/asciidoc/user-guide/appendix.adoc +++ b/documentation/modules/ROOT/pages/appendix.adoc @@ -1,8 +1,7 @@ -[[appendix]] -== Appendix += Appendix [[reproducible-builds]] -=== Reproducible Builds +== Reproducible Builds JUnit aims for its non-javadoc JARs to be https://reproducible-builds.org/[reproducible]. @@ -14,7 +13,7 @@ Central/Sonatype and produce the same output artifact locally, confirming that t artifacts in the repositories were actually generated from this source code. [[dependency-metadata]] -=== Dependency Metadata +== Dependency Metadata Artifacts for final releases and milestones are deployed to {Maven_Central}. Consult https://central.sonatype.org/consume/[Sonatype's documentation] for how to consume those @@ -37,16 +36,16 @@ of the above artifacts and their versions. ==== To ensure that all JUnit artifacts are compatible with each other, their versions should be aligned. -If you rely on <> for dependency management, +If you rely on xref:running-tests/build-support.adoc#spring-boot[Spring Boot] for dependency management, please see the corresponding section. Otherwise, instead of managing individual versions of the JUnit artifacts, it is recommended to apply the <> to your project. -Please refer to the corresponding sections for <> or -<>. +Please refer to the corresponding sections for xref:running-tests/build-support.adoc#maven-bom[Maven] or +xref:running-tests/build-support.adoc#gradle-bom[Gradle]. ==== [[dependency-metadata-junit-platform]] -==== JUnit Platform +=== JUnit Platform * *Group ID*: `org.junit.platform` * *Version*: `{version}` @@ -57,35 +56,35 @@ Please refer to the corresponding sections for <> for details. + See xref:running-tests/console-launcher.adoc[] for details. `junit-platform-console-standalone`:: An executable _Fat JAR_ that contains all dependencies is provided in Maven Central under the https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone] - directory. See <> for details. + directory. See xref:running-tests/console-launcher.adoc[] for details. `junit-platform-engine`:: - Public API for test engines. See <> for details. + Public API for test engines. See xref:advanced-topics/launcher-api.adoc#engines-custom[Registering a TestEngine] for details. `junit-platform-launcher`:: Public API for configuring and launching test plans -- typically used by IDEs and - build tools. See <> for details. + build tools. See xref:advanced-topics/launcher-api.adoc[] for details. `junit-platform-reporting`:: `TestExecutionListener` implementations that generate test reports -- typically used - by IDEs and build tools. See <> for details. + by IDEs and build tools. See xref:advanced-topics/junit-platform-reporting.adoc[] for details. `junit-platform-suite`:: JUnit Platform Suite artifact that transitively pulls in dependencies on `junit-platform-suite-api` and `junit-platform-suite-engine` for simplified dependency management in build tools such as Gradle and Maven. `junit-platform-suite-api`:: Annotations for configuring test suites on the JUnit Platform. Supported by the - <>. + xref:advanced-topics/junit-platform-suite-engine.adoc[JUnit Platform Suite Engine]. `junit-platform-suite-engine`:: Engine that executes test suites on the JUnit Platform; only required at runtime. See - <> for details. + xref:advanced-topics/junit-platform-suite-engine.adoc[JUnit Platform Suite Engine] for details. `junit-platform-testkit`:: Provides support for executing a test plan for a given `TestEngine` and then accessing the results via a fluent API to verify the expected results. [[dependency-metadata-junit-jupiter]] -==== JUnit Jupiter +=== JUnit Jupiter * *Group ID*: `org.junit.jupiter` * *Version*: `{version}` @@ -95,17 +94,17 @@ Please refer to the corresponding sections for <> and <>. + JUnit Jupiter API for xref:writing-tests/intro.adoc[writing tests] and xref:extensions/overview.adoc[extensions]. `junit-jupiter-engine`:: JUnit Jupiter test engine implementation; only required at runtime. `junit-jupiter-params`:: - Support for <> in JUnit Jupiter. + Support for xref:writing-tests/parameterized-classes-and-tests.adoc[] in JUnit Jupiter. `junit-jupiter-migrationsupport`:: _Deprecated_ support for migrating from JUnit 4 to JUnit Jupiter; only required for support for JUnit 4's `@Ignore` annotation and for running selected JUnit 4 rules. [[dependency-metadata-junit-vintage]] -==== JUnit Vintage +=== JUnit Vintage * *Group ID*: `org.junit.vintage` * *Version*: `{version}` @@ -116,7 +115,7 @@ Please refer to the corresponding sections for < annotationType)` +* `Optional findAnnotation(Class annotationType)` +* `List findRepeatableAnnotations(Class annotationType)` +==== + +[NOTE] +.Accessing the test-scoped `ExtensionContext` +==== +You may override the `getTestInstantiationExtensionContextScope(...)` method to return +`TEST_METHOD` to support injecting test specific data into constructor parameters of the +test class instance. Doing so causes a test-specific `{ExtensionContext}` to be used while +resolving constructor parameters, unless the +xref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] is set to `PER_CLASS`. +==== + +[TIP] +.Parameter resolution for methods called from extensions +==== +Other extensions can also leverage registered `ParameterResolvers` for method and +constructor invocations, using the `{ExecutableInvoker}` available via the +`getExecutableInvoker()` method in the `ExtensionContext`. +==== + +[[conflicts]] +== Parameter Conflicts + +If multiple implementations of `ParameterResolver` that support the same type are +registered for a test, a `ParameterResolutionException` will be thrown, with a +message to indicate that competing resolvers have been discovered. See the following +example: + +[source,java,indent=0] +.Conflicting parameter resolution due to multiple resolvers claiming support for integers +---- +include::example$java/example/extensions/ParameterResolverConflictDemo.java[tags=user_guide] +---- + +If the conflicting `ParameterResolver` implementations are applied to different test +methods as shown in the following example, no conflict occurs. + +[source,java,indent=0] +.Fine-grained registration to avoid conflict +---- +include::example$java/example/extensions/ParameterResolverNoConflictDemo.java[tags=user_guide] +---- + +If the conflicting `ParameterResolver` implementations need to be applied to the same test +method, you can implement a custom type or custom annotation as illustrated by +`{CustomTypeParameterResolver}` and `{CustomAnnotationParameterResolver}`, respectively. + +[source,java,indent=0] +.Custom type to resolve duplicate types +---- +include::example$java/example/extensions/ParameterResolverCustomTypeDemo.java[tags=user_guide] +---- + +A custom annotation makes the duplicate type distinguishable from its counterpart: + +[source,java,indent=0] +.Custom annotation to resolve duplicate types +---- +include::example$java/example/extensions/ParameterResolverCustomAnnotationDemo.java[tags=user_guide] +---- + +JUnit includes some built-in parameter resolvers that can cause conflicts if a resolver +attempts to claim their supported types. For example, `{TestInfo}` provides metadata about +tests. See xref:writing-tests/dependency-injection-for-constructors-and-methods.adoc[] for details. Third-party frameworks such +as Spring may also define parameter resolvers. Apply one of the techniques in this section +to resolve any conflicts. + +Parameterized tests are another potential source of conflict. Ensure that tests annotated +with `@ParameterizedTest` are not also annotated with `@Test` and see +xref:writing-tests/parameterized-classes-and-tests.adoc#tests-consuming-arguments[Consuming Arguments] for more details. diff --git a/documentation/modules/ROOT/pages/extensions/pre-interrupt-callback.adoc b/documentation/modules/ROOT/pages/extensions/pre-interrupt-callback.adoc new file mode 100644 index 000000000000..d046c1e29a4e --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/pre-interrupt-callback.adoc @@ -0,0 +1,6 @@ += Pre-Interrupt Callback + +`{PreInterruptCallback}` defines the API for `Extensions` that wish to react on +timeouts before the `Thread.interrupt()` is called. + +Please refer to xref:writing-tests/timeouts.adoc#debugging[Debugging Timeouts] for additional information. 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 new file mode 100644 index 000000000000..fa1b7936b10a --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-class-templates.adoc @@ -0,0 +1,41 @@ += Providing Invocation Contexts for Class Templates + +A `{ClassTemplate}` class can only be executed when at least one +`{ClassTemplateInvocationContextProvider}` is registered. Each such provider is +responsible for providing a `Stream` of `{ClassTemplateInvocationContext}` instances. +Each context may specify a custom display name and a list of additional extensions that +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}`. + +[source,java,indent=0] +.A class template with accompanying extension +---- +include::example$java/example/ClassTemplateDemo.java[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 +`apple` and `banana` as specified by the invocation context. Each invocation registers a +custom `{TestInstancePostProcessor}` which is used to inject a value into a field. The +output when using the `ConsoleLauncher` is as follows. + +.... +└─ ClassTemplateDemo ✔ + ├─ apple ✔ + │ ├─ notNull() ✔ + │ └─ wellKnown() ✔ + └─ banana ✔ + ├─ notNull() ✔ + └─ wellKnown() ✔ +.... + +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 +parameters, by preparing the test class instance differently, or multiple times without +modifying the context. +Please refer to the implementations 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 new file mode 100644 index 000000000000..2d55a58a5ae3 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-test-templates.adoc @@ -0,0 +1,35 @@ += Providing Invocation Contexts for Test Templates + +A `{TestTemplate}` method can only be executed when at least one +`{TestTemplateInvocationContextProvider}` is registered. Each such provider is responsible +for providing a `Stream` of `{TestTemplateInvocationContext}` instances. Each context may +specify a custom display name and a list of additional extensions that will only be used +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}`. + +[source,java,indent=0] +.A test template with accompanying extension +---- +include::example$java/example/TestTemplateDemo.java[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 +invocation registers a custom `{ParameterResolver}` which is used to resolve the method +parameter. The output when using the `ConsoleLauncher` is as follows. + +.... +└─ testTemplate(String) ✔ + ├─ apple ✔ + └─ banana ✔ +.... + +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 +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 +to provide their functionality. diff --git a/documentation/modules/ROOT/pages/extensions/registering-extensions.adoc b/documentation/modules/ROOT/pages/extensions/registering-extensions.adoc new file mode 100644 index 000000000000..e25eb404f063 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/registering-extensions.adoc @@ -0,0 +1,330 @@ += Registering Extensions + +Extensions can be registered _declaratively_ via +<>, _programmatically_ via +<>, or _automatically_ via +Java's <> mechanism. + +[[registration-declarative]] +== Declarative Extension Registration + +Developers can register one or more extensions _declaratively_ by annotating a test +interface, test class, test method, or custom _xref:writing-tests/annotations.adoc#annotations[composed +annotation]_ with `@ExtendWith(...)` and supplying class references for the extensions to +register. `@ExtendWith` may also be declared on fields or on parameters in test class +constructors, in test methods, and in `@BeforeAll`, `@AfterAll`, `@BeforeEach`, and +`@AfterEach` lifecycle methods. + +For example, to register a `WebServerExtension` for a particular test method, you would +annotate the test method as follows. We assume the `WebServerExtension` starts a local web +server and injects the server's URL into parameters annotated with `@WebServerUrl`. + +[source,java,indent=0] +---- +@Test +@ExtendWith(WebServerExtension.class) +void getProductList(@WebServerUrl String serverUrl) { + WebClient webClient = new WebClient(); + // Use WebClient to connect to web server using serverUrl and verify response + assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus()); +} +---- + +To register the `WebServerExtension` for all tests in a particular class and its +subclasses, you would annotate the test class as follows. + +[source,java,indent=0] +---- +@ExtendWith(WebServerExtension.class) +class MyTests { + // ... +} +---- + +Multiple extensions can be registered together like this: + +[source,java,indent=0] +---- +@ExtendWith({ DatabaseExtension.class, WebServerExtension.class }) +class MyFirstTests { + // ... +} +---- + +As an alternative, multiple extensions can be registered separately like this: + +[source,java,indent=0] +---- +@ExtendWith(DatabaseExtension.class) +@ExtendWith(WebServerExtension.class) +class MySecondTests { + // ... +} +---- + +[TIP] +.Extension Registration Order +==== +Extensions registered declaratively via `@ExtendWith` at the class level, method level, or +parameter level will be executed in the order in which they are declared in the source +code. For example, the execution of tests in both `MyFirstTests` and `MySecondTests` will +be extended by the `DatabaseExtension` and `WebServerExtension`, **in exactly that order**. +==== + +If you wish to combine multiple extensions in a reusable way, you can define a custom +_xref:writing-tests/annotations.adoc#annotations[composed annotation]_ and use `@ExtendWith` as a +_meta-annotation_ as in the following code listing. Then `@DatabaseAndWebServerExtension` +can be used in place of `@ExtendWith({ DatabaseExtension.class, WebServerExtension.class })`. + +[source,java,indent=0] +---- +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith({ DatabaseExtension.class, WebServerExtension.class }) +public @interface DatabaseAndWebServerExtension { +} +---- + +The above examples demonstrate how `@ExtendWith` can be applied at the class level or at +the method level; however, for certain use cases it makes sense for an extension to be +registered declaratively at the field or parameter level. Consider a +`RandomNumberExtension` which generates random numbers that can be injected into a field or +via a parameter in a constructor, test method, or lifecycle method. If the extension +provides a `@Random` annotation that is meta-annotated with +`@ExtendWith(RandomNumberExtension.class)` (see listing below), the extension can be used +transparently as in the following `RandomNumberDemo` example. + +[source,java,indent=0] +---- +include::example$java/example/extensions/Random.java[tags=user_guide] +---- + +[source,java,indent=0] +---- +include::example$java/example/extensions/RandomNumberDemo.java[tags=user_guide] +---- + +[[RandomNumberExtension]] +The following code listing provides an example of how one might choose to implement such a +`RandomNumberExtension`. This implementation works for the use cases in +`RandomNumberDemo`; however, it may not prove robust enough to cover all use cases -- for +example, the random number generation support is limited to integers; it uses +`java.util.Random` instead of `java.security.SecureRandom`; etc. In any case, it is +important to note which extension APIs are implemented and for what reasons. + +Specifically, `RandomNumberExtension` implements the following extension APIs: + +- `BeforeAllCallback`: to support static field injection +- `TestInstancePostProcessor`: to support non-static field injection +- `ParameterResolver`: to support constructor and method injection + +[source,java,indent=0] +---- +include::example$java/example/extensions/RandomNumberExtension.java[tags=user_guide] +---- + +[TIP] +.Extension Registration Order for `@ExtendWith` on Fields +==== +Extensions registered declaratively via `@ExtendWith` on fields will be ordered relative +to `@RegisterExtension` fields and other `@ExtendWith` fields using an algorithm that is +deterministic but intentionally nonobvious. However, `@ExtendWith` fields can be ordered +using the `@Order` annotation. See the <> tip for `@RegisterExtension` fields for details. +==== + +[TIP] +.Extension Inheritance +==== +Extensions registered declaratively via `@ExtendWith` on fields in superclasses will be +inherited. + +See <> for details. +==== + +NOTE: `@ExtendWith` fields may be either `static` or non-static. The documentation on +<> and +<> for +`@RegisterExtension` fields also applies to `@ExtendWith` fields. + +[[registration-programmatic]] +== Programmatic Extension Registration + +Developers can register extensions _programmatically_ by annotating fields in test classes +with `{RegisterExtension}`. + +When an extension is registered _declaratively_ via +<>, it can typically only be configured +via annotations. In contrast, when an extension is registered via `@RegisterExtension`, it +can be configured _programmatically_ -- for example, in order to pass arguments to the +extension's constructor, a static factory method, or a builder API. + +[[registration-programmatic-order]] +[TIP] +.Extension Registration Order +==== +By default, extensions registered programmatically via `@RegisterExtension` or +declaratively via `@ExtendWith` on fields will be ordered using an algorithm that is +deterministic but intentionally nonobvious. This ensures that subsequent runs of a test +suite execute extensions in the same order, thereby allowing for repeatable builds. +However, there are times when extensions need to be registered in an explicit order. To +achieve that, annotate `@RegisterExtension` fields or `@ExtendWith` fields with `{Order}`. + +Any `@RegisterExtension` field or `@ExtendWith` field not annotated with `@Order` will be +ordered using the _default_ order which has a value of `Integer.MAX_VALUE / 2`. This +allows `@Order` annotated extension fields to be explicitly ordered before or after +non-annotated extension fields. Extensions with an explicit order value less than the +default order value will be registered before non-annotated extensions. Similarly, +extensions with an explicit order value greater than the default order value will be +registered after non-annotated extensions. For example, assigning an extension an explicit +order value that is greater than the default order value allows _before_ callback +extensions to be registered last and _after_ callback extensions to be registered first, +relative to other programmatically registered extensions. +==== + +[TIP] +.Extension Inheritance +==== +Extensions registered via `@RegisterExtension` or `@ExtendWith` on fields in superclasses +will be inherited. + +See <> for details. +==== + +NOTE: `@RegisterExtension` fields must not be `null` (at evaluation time) but may be +either `static` or non-static. + +[[registration-programmatic-static-fields]] +=== Static Fields + +If a `@RegisterExtension` field is `static`, the extension will be registered after +extensions that are registered at the class level via `@ExtendWith`. Such _static +extensions_ are not limited in which extension APIs they can implement. Extensions +registered via static fields may therefore implement class-level and instance-level +extension APIs such as `BeforeAllCallback`, `AfterAllCallback`, +`TestInstancePostProcessor`, and `TestInstancePreDestroyCallback` as well as method-level +extension APIs such as `BeforeEachCallback`, etc. + +In the following example, the `server` field in the test class is initialized +programmatically by using a builder pattern supported by the `WebServerExtension`. The +configured `WebServerExtension` will be automatically registered as an extension at the +class level -- for example, in order to start the server before all tests in the class +and then stop the server after all tests in the class have completed. In addition, static +lifecycle methods annotated with `@BeforeAll` or `@AfterAll` as well as `@BeforeEach`, +`@AfterEach`, and `@Test` methods can access the instance of the extension via the +`server` field if necessary. + +[source,java,indent=0] +.Registering an extension via a static field in Java +---- +include::example$java/example/registration/WebServerDemo.java[tags=user_guide] +---- + +[[registration-programmatic-static-fields-kotlin]] +==== Static Fields in Kotlin + +The Kotlin programming language does not have the concept of a `static` field. However, +the compiler can be instructed to generate a `private static` field using the `@JvmStatic` +annotation in Kotlin. If you want the Kotlin compiler to generate a `public static` field, +you can use the `@JvmField` annotation instead. + +The following example is a version of the `WebServerDemo` from the previous section that +has been ported to Kotlin. + +[source,kotlin,indent=0] +.Registering an extension via a static field in Kotlin +---- +include::example$kotlin/example/registration/KotlinWebServerDemo.kt[tags=user_guide] +---- + +[[registration-programmatic-instance-fields]] +=== Instance Fields + +If a `@RegisterExtension` field is non-static (i.e., an instance field), the extension +will be registered after the test class has been instantiated and after each registered +`TestInstancePostProcessor` has been given a chance to post-process the test instance +(potentially injecting the instance of the extension to be used into the annotated +field). Thus, if such an _instance extension_ implements class-level or instance-level +extension APIs such as `BeforeAllCallback`, `AfterAllCallback`, or +`TestInstancePostProcessor`, those APIs will not be honored. Instance extensions will be +registered _before_ extensions that are registered at the method level via `@ExtendWith`. + +In the following example, the `docs` field in the test class is initialized +programmatically by invoking a custom `lookUpDocsDir()` method and supplying the result +to the static `forPath()` factory method in the `DocumentationExtension`. The configured +`DocumentationExtension` will be automatically registered as an extension at the method +level. In addition, `@BeforeEach`, `@AfterEach`, and `@Test` methods can access the +instance of the extension via the `docs` field if necessary. + +[source,java,indent=0] +.An extension registered via an instance field +---- +include::example$java/example/registration/DocumentationDemo.java[tags=user_guide] +---- + +[[registration-automatic]] +== Automatic Extension Registration + +In addition to <> +and <> support +using annotations, JUnit Jupiter also supports _global extension registration_ via Java's +`{ServiceLoader}` mechanism, allowing third-party extensions to be auto-detected and +automatically registered based on what is available in the classpath. + +Specifically, a custom extension can be registered by supplying its fully qualified class +name in a file named `org.junit.jupiter.api.extension.Extension` within the +`/META-INF/services` folder in its enclosing JAR file. + +[[registration-automatic-enabling]] +=== Enabling Automatic Extension Detection + +Auto-detection is an advanced feature and is therefore not enabled by default. To enable +it, set the `junit.jupiter.extensions.autodetection.enabled` _configuration parameter_ to +`true`. This can be supplied as a JVM system property, as a _configuration parameter_ in +the `LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform +configuration file (see xref:running-tests/configuration-parameters.adoc[] for details). + +For example, to enable auto-detection of extensions, you can start your JVM with the +following system property. + +`-Djunit.jupiter.extensions.autodetection.enabled=true` + +When auto-detection is enabled, extensions discovered via the `{ServiceLoader}` mechanism +will be added to the extension registry after JUnit Jupiter's global extensions (e.g., +support for `TestInfo`, `TestReporter`, etc.). + +[[registration-automatic-filtering]] +=== Filtering Auto-detected Extensions + +The list of auto-detected extensions can be filtered using include and exclude patterns +via the following xref:running-tests/configuration-parameters.adoc[configuration parameters]: + +`junit.jupiter.extensions.autodetection.include=`:: + Comma-separated list of _include_ patterns for auto-detected extensions. +`junit.jupiter.extensions.autodetection.exclude=`:: + Comma-separated list of _exclude_ patterns for auto-detected extensions. + +Include patterns are applied _before_ exclude patterns. If both include and exclude +patterns are provided, only extensions that match at least one include pattern and do not +match any exclude pattern will be auto-detected. + +See xref:running-tests/configuration-parameters.adoc#pattern[Pattern Matching Syntax] for details on the pattern syntax. + +[[registration-inheritance]] +== Extension Inheritance + +Registered extensions are inherited within test class hierarchies with top-down semantics. +Similarly, extensions registered at the class-level are inherited at the method-level. +This applies to all extensions, independent of how they are registered (declaratively or +programmatically). + +This means that extensions registered declaratively via `@ExtendWith` on a superclass will +be registered before extensions registered declaratively via `@ExtendWith` on a subclass. + +Similarly, extensions registered programmatically via `@RegisterExtension` or +`@ExtendWith` on fields in a superclass will be registered before extensions registered +programmatically via `@RegisterExtension` or `@ExtendWith` on fields in a subclass, unless +`@Order` is used to alter that behavior (see <> for details). + +NOTE: A specific extension implementation can only be registered once for a given +extension context and its parent contexts. Consequently, any attempt to register a +duplicate extension implementation will be ignored. 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 new file mode 100644 index 000000000000..6c88a1ed6d29 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/relative-execution-order-of-user-code-and-extensions.adoc @@ -0,0 +1,238 @@ += Relative Execution Order of User Code and Extensions + +When executing a test class that contains one or more test methods, a number of extension +callbacks are called in addition to the user-supplied test and lifecycle methods. + +NOTE: See also: xref:writing-tests/test-execution-order.adoc[] + +[[overview]] +== User and Extension Code + +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 +test method and will be repeated for every test method in the test class. + +[[diagram]] +.User code and extension code +image::extensions_lifecycle.png[] + +The following table further explains the sixteen steps in the +<> diagram. + +. *interface* `*org.junit.jupiter.api.extension.BeforeAllCallback*` + +extension code executed before all tests of the container are executed +. *annotation* `*org.junit.jupiter.api.BeforeAll*` + +user code executed before all tests of the container are executed +. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler +#handleBeforeAllMethodExecutionException*` + +extension code for handling exceptions thrown from `@BeforeAll` methods +. *interface* `*org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback*` + +extension code executed before each class template invocation is executed (only applicable +if the test class is a xref:writing-tests/class-templates.adoc[class template]) +. *interface* `*org.junit.jupiter.api.extension.BeforeEachCallback*` + +extension code executed before each test is executed +. *annotation* `*org.junit.jupiter.api.BeforeEach*` + +user code executed before each test is executed +. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler +#handleBeforeEachMethodExecutionException*` + +extension code for handling exceptions thrown from `@BeforeEach` methods +. *interface* `*org.junit.jupiter.api.extension.BeforeTestExecutionCallback*` + +extension code executed immediately before a test is executed +. *annotation* `*org.junit.jupiter.api.Test*` + +user code of the actual test method +. *interface* `*org.junit.jupiter.api.extension.TestExecutionExceptionHandler*` + +extension code for handling exceptions thrown during a test +. *interface* `*org.junit.jupiter.api.extension.AfterTestExecutionCallback*` + +extension code executed immediately after test execution and its corresponding exception handlers +. *annotation* `*org.junit.jupiter.api.AfterEach*` + +user code executed after each test is executed +. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler +#handleAfterEachMethodExecutionException*` + +extension code for handling exceptions thrown from `@AfterEach` methods +. *interface* `*org.junit.jupiter.api.extension.AfterEachCallback*` + +extension code executed after each test is executed +. *interface* `*org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback*` + +extension code executed after each class template invocation is executed (only applicable +if the test class is a xref:writing-tests/class-templates.adoc[class template]) +. *annotation* `*org.junit.jupiter.api.AfterAll*` + +user code executed after all tests of the container are executed +. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler +#handleAfterAllMethodExecutionException*` + +extension code for handling exceptions thrown from `@AfterAll` methods +. *interface* `*org.junit.jupiter.api.extension.AfterAllCallback*` + +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 +please consult the respective Javadoc for each annotation and extension. + +All invocations of user code methods in the above table can additionally be intercepted +by implementing xref:extensions/intercepting-invocations.adoc[`InvocationInterceptor`]. + +[[wrapping-behavior]] +== Wrapping Behavior of Callbacks + +JUnit Jupiter always guarantees _wrapping_ behavior for multiple registered extensions +that implement lifecycle callbacks such as `BeforeAllCallback`, `AfterAllCallback`, +`BeforeClassTemplateInvocationCallback`, `AfterClassTemplateInvocationCallback`, +`BeforeEachCallback`, `AfterEachCallback`, `BeforeTestExecutionCallback`, and +`AfterTestExecutionCallback`. + +That means that, given two extensions `Extension1` and `Extension2` with `Extension1` +registered before `Extension2`, any "before" callbacks implemented by `Extension1` are +guaranteed to execute **before** any "before" callbacks implemented by `Extension2`. +Similarly, given the two same two extensions registered in the same order, any "after" +callbacks implemented by `Extension1` are guaranteed to execute **after** any "after" +callbacks implemented by `Extension2`. `Extension1` is therefore said to _wrap_ +`Extension2`. + +JUnit Jupiter also guarantees _wrapping_ behavior within class and interface hierarchies +for user-supplied _lifecycle methods_ (see xref:writing-tests/definitions.adoc[]). + +* `@BeforeAll` methods are inherited from superclasses as long as they are not + _overridden_. Furthermore, `@BeforeAll` methods from superclasses will be executed + **before** `@BeforeAll` methods in subclasses. +** Similarly, `@BeforeAll` methods declared in an interface are inherited as long as they + are not _overridden_, and `@BeforeAll` methods from an interface will be executed + **before** `@BeforeAll` methods in the class that implements the interface. +* `@AfterAll` methods are inherited from superclasses as long as they are not + _overridden_. Furthermore, `@AfterAll` methods from superclasses will be executed + **after** `@AfterAll` methods in subclasses. +** Similarly, `@AfterAll` methods declared in an interface are inherited as long as they + are not _overridden_, and `@AfterAll` methods from an interface will be executed + **after** `@AfterAll` methods in the class that implements the interface. +* `@BeforeEach` methods are inherited from superclasses as long as they are not + _overridden_. Furthermore, `@BeforeEach` methods from superclasses will be executed + **before** `@BeforeEach` methods in subclasses. +** Similarly, `@BeforeEach` methods declared as interface default methods are inherited as + long as they are not _overridden_, and `@BeforeEach` default methods will be executed + **before** `@BeforeEach` methods in the class that implements the interface. +* `@AfterEach` methods are inherited from superclasses as long as they are not + _overridden_. Furthermore, `@AfterEach` methods from superclasses will be executed + **after** `@AfterEach` methods in subclasses. +** Similarly, `@AfterEach` methods declared as interface default methods are inherited as + long as they are not _overridden_, and `@AfterEach` default methods will be executed + **after** `@AfterEach` methods in the class that implements the interface. + +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 +user-supplied callback methods and callback methods in extensions. + +[source,java,indent=0] +.Extension1 +---- +include::example$java/example/callbacks/Extension1.java[tags=user_guide] +---- + +[source,java,indent=0] +.Extension2 +---- +include::example$java/example/callbacks/Extension2.java[tags=user_guide] +---- + +[source,java,indent=0] +.AbstractDatabaseTests +---- +include::example$java/example/callbacks/AbstractDatabaseTests.java[tags=user_guide] +---- + +[source,java,indent=0] +.DatabaseTestsDemo +---- +include::example$java/example/callbacks/DatabaseTestsDemo.java[tags=user_guide] +---- + +When the `DatabaseTestsDemo` test class is executed, the following is logged. + +---- +@BeforeAll AbstractDatabaseTests.createDatabase() +@BeforeAll DatabaseTestsDemo.beforeAll() + Extension1.beforeEach() + Extension2.beforeEach() + @BeforeEach AbstractDatabaseTests.connectToDatabase() + @BeforeEach DatabaseTestsDemo.insertTestDataIntoDatabase() + @Test DatabaseTestsDemo.testDatabaseFunctionality() + @AfterEach DatabaseTestsDemo.deleteTestDataFromDatabase() + @AfterEach AbstractDatabaseTests.disconnectFromDatabase() + Extension2.afterEach() + Extension1.afterEach() +@BeforeAll DatabaseTestsDemo.afterAll() +@AfterAll AbstractDatabaseTests.destroyDatabase() +---- + +The following sequence diagram helps to shed further light on what actually goes on within +the `JupiterTestEngine` when the `DatabaseTestsDemo` test class is executed. + +//// +PNG generated using ZenUML: https://app.zenuml.com + +See corresponding *.txt file in images folder for the source. +//// +image::extensions_DatabaseTestsDemo.png[caption='',title='DatabaseTestsDemo'] + +JUnit Jupiter does **not** guarantee the execution order of multiple lifecycle methods +that are declared within a _single_ test class or test interface. It may at times appear +that JUnit Jupiter invokes such methods in alphabetical order. However, that is not +precisely true. The ordering is analogous to the ordering for `@Test` methods within a +single test class. + +[NOTE] +==== +Lifecycle methods that are declared within a _single_ test class or test interface will be +ordered using an algorithm that is deterministic but intentionally non-obvious. This +ensures that subsequent runs of a test suite execute lifecycle methods in the same order, +thereby allowing for repeatable builds. +==== + +In addition, JUnit Jupiter does **not** support _wrapping_ behavior for multiple lifecycle +methods declared within a single test class or test interface. + +The following example demonstrates this behavior. Specifically, the lifecycle method +configuration is _broken_ due to the order in which the locally declared lifecycle methods +are executed. + +* Test data is inserted _before_ the database connection has been opened, which results in + a failure to connect to the database. +* 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 +---- +include::example$java/example/callbacks/BrokenLifecycleMethodConfigDemo.java[tags=user_guide] +---- + +When the `BrokenLifecycleMethodConfigDemo` test class is executed, the following is logged. + +---- +Extension1.beforeEach() +Extension2.beforeEach() + @BeforeEach BrokenLifecycleMethodConfigDemo.insertTestDataIntoDatabase() + @BeforeEach BrokenLifecycleMethodConfigDemo.connectToDatabase() + @Test BrokenLifecycleMethodConfigDemo.testDatabaseFunctionality() + @AfterEach BrokenLifecycleMethodConfigDemo.disconnectFromDatabase() + @AfterEach BrokenLifecycleMethodConfigDemo.deleteTestDataFromDatabase() +Extension2.afterEach() +Extension1.afterEach() +---- + +The following sequence diagram helps to shed further light on what actually goes on within +the `JupiterTestEngine` when the `BrokenLifecycleMethodConfigDemo` test class is executed. + +//// +PNG generated using ZenUML: https://app.zenuml.com + +See corresponding *.txt file in images folder for the source. +//// +image::extensions_BrokenLifecycleMethodConfigDemo.png[caption='',title='BrokenLifecycleMethodConfigDemo'] + +[TIP] +==== +Due to the aforementioned behavior, the JUnit Team recommends that developers declare at +most one of each type of _lifecycle method_ (see xref:writing-tests/definitions.adoc[]) per test +class or test interface unless there are no dependencies between such lifecycle methods. +==== diff --git a/documentation/modules/ROOT/pages/extensions/supported-utilities-in-extensions.adoc b/documentation/modules/ROOT/pages/extensions/supported-utilities-in-extensions.adoc new file mode 100644 index 000000000000..bacf8790a3e2 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/supported-utilities-in-extensions.adoc @@ -0,0 +1,69 @@ += Supported Utilities in Extensions + +The `junit-platform-commons` artifact provides _maintained_ utilities for working with +annotations, classes, reflection, classpath scanning, and conversion tasks. These +utilities can be found in the `{junit-platform-support-package}` and its subpackages. +`TestEngine` and `Extension` authors are encouraged to use these supported utilities in +order to align with the behavior of the JUnit Platform and JUnit Jupiter. + +[[annotations]] +== Annotation Support + +`AnnotationSupport` provides static utility methods that operate on annotated elements +(e.g., packages, annotations, classes, interfaces, constructors, methods, and fields). +These include methods to check whether an element is annotated or meta-annotated with a +particular annotation, to search for specific annotations, and to find annotated methods +and fields in a class or interface. Some of these methods search on implemented +interfaces and within class hierarchies to find annotations. Consult the Javadoc for +`{AnnotationSupport}` for further details. + +NOTE: The `isAnnotated()` methods do not find repeatable annotations. To check for repeatable annotations, +use one of the `findRepeatableAnnotations()` methods and verify that the returned list is not empty. + +NOTE: See also: <> + +[[classes]] +== Class Support + +`ClassSupport` provides static utility methods for working with classes (i.e., instances +of `java.lang.Class`). Consult the Javadoc for `{ClassSupport}` for further details. + +[[reflection]] +== Reflection Support + +`ReflectionSupport` provides static utility methods that augment the standard JDK +reflection and class-loading mechanisms. These include methods to scan the classpath in +search of classes matching specified predicates, to load and create new instances of a +class, and to find and invoke methods. Some of these methods traverse class hierarchies +to locate matching methods. Consult the Javadoc for `{ReflectionSupport}` for further +details. + +NOTE: See also: <> + +[[modifier]] +== Modifier Support + +`ModifierSupport` provides static utility methods for working with member and class +modifiers -- for example, to determine if a member is declared as `public`, `private`, +`abstract`, `static`, etc. Consult the Javadoc for `{ModifierSupport}` for further +details. + +[[conversion]] +== Conversion Support + +`ConversionSupport` (in the `org.junit.platform.commons.support.conversion` package) +provides support for converting from strings to primitive types and their corresponding +wrapper types, date and time types from the `java.time package`, and some additional +common Java types such as `File`, `BigDecimal`, `BigInteger`, `Currency`, `Locale`, `URI`, +`URL`, `UUID`, etc. Consult the Javadoc for `{ConversionSupport}` for further details. + +[[search-semantics]] +== Field and Method Search Semantics + +Various methods in `AnnotationSupport` and `ReflectionSupport` use search algorithms that +traverse type hierarchies to locate matching fields and methods – for example, +`AnnotationSupport.findAnnotatedFields(...)`, `ReflectionSupport.findMethods(...)`, etc. + +The field and method search algorithms adhere to standard Java semantics regarding whether +a given field or method is visible or overridden according to the rules of the Java +language. diff --git a/documentation/modules/ROOT/pages/extensions/test-instance-factories.adoc b/documentation/modules/ROOT/pages/extensions/test-instance-factories.adoc new file mode 100644 index 000000000000..0710cf755f42 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/test-instance-factories.adoc @@ -0,0 +1,32 @@ += Test Instance Factories + +`{TestInstanceFactory}` defines the API for `Extensions` that wish to _create_ test class +instances. + +Common use cases include acquiring the test instance from a dependency injection +framework or invoking a static factory method to create the test class instance. + +If no `TestInstanceFactory` is registered, the framework will invoke the _sole_ +constructor for the test class to instantiate it, potentially resolving constructor +arguments via registered `ParameterResolver` extensions. + +Extensions that implement `TestInstanceFactory` can be registered on test interfaces, +top-level test classes, or `@Nested` test classes. + +[WARNING] +==== +Registering multiple extensions that implement `TestInstanceFactory` for any single class +will result in an exception being thrown for all tests in that class, in any subclass, +and in any nested class. Note that any `TestInstanceFactory` registered in a superclass +or _enclosing_ class (i.e., in the case of a `@Nested` test class) is _inherited_. It is +the user's responsibility to ensure that only a single `TestInstanceFactory` is +registered for any specific test class. +==== + +[NOTE] +.Accessing the test-scoped `ExtensionContext` +==== +You may override the `getTestInstantiationExtensionContextScope(...)` method to return +`TEST_METHOD` to make test-specific data available to your extension implementation or if +you want to xref:extensions/keeping-state-in-extensions.adoc[keep state] on the test method level. +==== diff --git a/documentation/modules/ROOT/pages/extensions/test-instance-post-processing.adoc b/documentation/modules/ROOT/pages/extensions/test-instance-post-processing.adoc new file mode 100644 index 000000000000..93bab7eaac29 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/test-instance-post-processing.adoc @@ -0,0 +1,18 @@ += Test Instance Post-processing + +`{TestInstancePostProcessor}` defines the API for `Extensions` that wish to _post +process_ test instances. + +Common use cases include injecting dependencies into the test instance, invoking custom +initialization methods on the test instance, etc. + +For a concrete example, consult the source code for the `{MockitoExtension}` and the +`{SpringExtension}`. + +[NOTE] +.Accessing the test-scoped `ExtensionContext` +==== +You may override the `getTestInstantiationExtensionContextScope(...)` method to return +`TEST_METHOD` to make test-specific data available to your extension implementation or if +you want to xref:extensions/keeping-state-in-extensions.adoc[keep state] on the test method level. +==== diff --git a/documentation/modules/ROOT/pages/extensions/test-instance-pre-construct-callback.adoc b/documentation/modules/ROOT/pages/extensions/test-instance-pre-construct-callback.adoc new file mode 100644 index 000000000000..0d72d10018f2 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/test-instance-pre-construct-callback.adoc @@ -0,0 +1,17 @@ += Test Instance Pre-construct Callback + +`{TestInstancePreConstructCallback}` defines the API for `Extensions` that wish to be invoked +_prior_ to test instances being constructed (by a constructor call or via +`{TestInstanceFactory}`). + +This extension provides a symmetric call to `{TestInstancePreDestroyCallback}` and is useful +in combination with other extensions to prepare constructor parameters or keeping track of test +instances and their lifecycle. + +[NOTE] +.Accessing the test-scoped `ExtensionContext` +==== +You may override the `getTestInstantiationExtensionContextScope(...)` method to return +`TEST_METHOD` to make test-specific data available to your extension implementation or if +you want to xref:extensions/keeping-state-in-extensions.adoc[keep state] on the test method level. +==== diff --git a/documentation/modules/ROOT/pages/extensions/test-instance-pre-destroy-callback.adoc b/documentation/modules/ROOT/pages/extensions/test-instance-pre-destroy-callback.adoc new file mode 100644 index 000000000000..4816c3a465c5 --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/test-instance-pre-destroy-callback.adoc @@ -0,0 +1,7 @@ += Test Instance Pre-destroy Callback + +`{TestInstancePreDestroyCallback}` defines the API for `Extensions` that wish to process +test instances _after_ they have been used in tests and _before_ they are destroyed. + +Common use cases include cleaning dependencies that have been injected into the +test instance, invoking custom de-initialization methods on the test instance, etc. diff --git a/documentation/modules/ROOT/pages/extensions/test-lifecycle-callbacks.adoc b/documentation/modules/ROOT/pages/extensions/test-lifecycle-callbacks.adoc new file mode 100644 index 000000000000..2b0190598f5e --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/test-lifecycle-callbacks.adoc @@ -0,0 +1,58 @@ += Test Lifecycle Callbacks + +The following interfaces define the APIs for extending tests at various points in the +test execution lifecycle. Consult the following sections for examples and the Javadoc for +each of these interfaces in the `{extension-api-package}` package for further details. + +* `{BeforeAllCallback}` +** `{BeforeClassTemplateInvocationCallback}` (only applicable for + xref:writing-tests/class-templates.adoc[class templates]) +*** `{BeforeEachCallback}` +**** `{BeforeTestExecutionCallback}` +**** `{AfterTestExecutionCallback}` +*** `{AfterEachCallback}` +** `{AfterClassTemplateInvocationCallback}` (only applicable for + xref:writing-tests/class-templates.adoc[class templates]) +* `{AfterAllCallback}` + +.Implementing Multiple Extension APIs +NOTE: Extension developers may choose to implement any number of these interfaces +within a single extension. Consult the source code of the `{SpringExtension}` for a +concrete example. + +[[before-after-execution]] +== Before and After Test Execution Callbacks + +`{BeforeTestExecutionCallback}` and `{AfterTestExecutionCallback}` define the APIs for +`Extensions` that wish to add behavior that will be executed _immediately before_ and +_immediately after_ a test method is executed, respectively. As such, these callbacks are +well suited for timing, tracing, and similar use cases. If you need to implement +callbacks that are invoked _around_ `@BeforeEach` and `@AfterEach` methods, implement +`BeforeEachCallback` and `AfterEachCallback` instead. + +The following example shows how to use these callbacks to calculate and log the execution +time of a test method. `TimingExtension` implements both `BeforeTestExecutionCallback` +and `AfterTestExecutionCallback` in order to time and log the test execution. + +[[timing-extension]] +[source,java,indent=0] +.An extension that times and logs the execution of test methods +---- +include::example$java/example/timing/TimingExtension.java[tags=user_guide] +---- + +Since the `TimingExtensionTests` class registers the `TimingExtension` via `@ExtendWith`, +its tests will have this timing applied when they execute. + +[source,java,indent=0] +.A test class that uses the example TimingExtension +---- +include::example$java/example/timing/TimingExtensionTests.java[tags=user_guide] +---- + +The following is an example of the logging produced when `TimingExtensionTests` is run. + +.... +INFO: Method [sleep20ms] took 24 ms. +INFO: Method [sleep50ms] took 53 ms. +.... diff --git a/documentation/modules/ROOT/pages/extensions/test-result-processing.adoc b/documentation/modules/ROOT/pages/extensions/test-result-processing.adoc new file mode 100644 index 000000000000..24fa7d04315e --- /dev/null +++ b/documentation/modules/ROOT/pages/extensions/test-result-processing.adoc @@ -0,0 +1,51 @@ += Test Result Processing + +`{TestWatcher}` defines the API for extensions that wish to process the results of _test +method_ executions. Specifically, a `TestWatcher` will be invoked with contextual +information for the following events. + +* `testDisabled`: invoked after a disabled _test method_ has been skipped +* `testSuccessful`: invoked after a _test method_ has completed successfully +* `testAborted`: invoked after a _test method_ has been aborted +* `testFailed`: invoked after a _test method_ has failed + +NOTE: In contrast to the definition of "test method" presented in +xref:writing-tests/definitions.adoc[], in this context _test method_ refers to any `@Test` method +or `@TestTemplate` method (for example, a `@RepeatedTest` or `@ParameterizedTest`). + +Extensions implementing this interface can be registered at the class level, instance +level, or method level. When registered at the class level, a `TestWatcher` will be +invoked for any contained _test method_ including those in `@Nested` classes. When +registered at the method level, a `TestWatcher` will only be invoked for the _test method_ +for which it was registered. + +[WARNING] +==== +If a `TestWatcher` is registered via a non-static (instance) field – for example, using +`@RegisterExtension` – and the test class is configured with +`@TestInstance(Lifecycle.PER_METHOD)` semantics (which is the default lifecycle mode), the +`TestWatcher` will **not** be invoked with events for `@TestTemplate` methods (for +example, `@RepeatedTest` or `@ParameterizedTest`). + +To ensure that a `TestWatcher` is invoked for all _test methods_ in a given class, it is +therefore recommended that the `TestWatcher` be registered at the class level with +`@ExtendWith` or via a `static` field with `@RegisterExtension` or `@ExtendWith`. +==== + +If there is a failure at the class level — for example, an exception thrown by a +`@BeforeAll` method — no test results will be reported. Similarly, if the test class is +disabled via an `ExecutionCondition` — for example, `@Disabled` — no test results will be +reported. + +In contrast to other Extension APIs, a `TestWatcher` is not permitted to adversely +influence the execution of tests. Consequently, any exception thrown by a method in the +`TestWatcher` API will be logged at `WARNING` level and will not be allowed to propagate +or fail test execution. + +[WARNING] +==== +Any instances of `ExtensionContext.Store.CloseableResource` stored in the `Store` of the +provided `{ExtensionContext}` will be closed _before_ methods in the `TestWatcher` API are +invoked (see xref:extensions/keeping-state-in-extensions.adoc[]). You can use the parent context's `Store` to +work with such resources. +==== diff --git a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc b/documentation/modules/ROOT/pages/migrating-from-junit4.adoc similarity index 84% rename from documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc rename to documentation/modules/ROOT/pages/migrating-from-junit4.adoc index 099b875bdd12..679b8b59acd7 100644 --- a/documentation/src/docs/asciidoc/user-guide/migration-from-junit4.adoc +++ b/documentation/modules/ROOT/pages/migrating-from-junit4.adoc @@ -1,7 +1,4 @@ -:testDir: ../../../../src/test/java - -[[migrating-from-junit4]] -== Migrating from JUnit 4 += Migrating from JUnit 4 Although the JUnit Jupiter programming model and extension model do not support JUnit 4 features such as `Rules` and `Runners` natively, it is not expected that source code @@ -16,8 +13,8 @@ classpath does not lead to any conflicts. It is therefore safe to maintain exist 4 tests alongside JUnit Jupiter tests and migrate them gradually. -[[migrating-from-junit4-running]] -=== Running JUnit 4 Tests on the JUnit Platform +[[running]] +== Running JUnit 4 Tests on the JUnit Platform [WARNING] ==== @@ -26,10 +23,10 @@ migrating tests to JUnit Jupiter or another testing framework with native JUnit support. By default, if the JUnit Vintage engine is registered and discovers at least one test -class, it reports a <> of INFO severity. +class, it reports a xref:running-tests/discovery-issues.adoc[discovery issue] of INFO severity. You can prevent this discovery issue from being reported by setting the `junit.vintage.discovery.issue.reporting.enabled` -<> to `false`. +xref:running-tests/configuration-parameters.adoc[configuration parameter] to `false`. ==== Make sure that the `junit-vintage-engine` artifact is in your test runtime path. In that @@ -39,23 +36,23 @@ launcher. See the example projects in the {junit-examples-repo}[`junit-examples`] repository to find out how this is done with Gradle and Maven. -[[migrating-from-junit4-categories-support]] -==== Categories Support +[[categories-support]] +=== Categories Support For test classes or methods that are annotated with `@Category`, the _JUnit Vintage test -engine_ exposes the category's fully qualified class name as a <> +engine_ exposes the category's fully qualified class name as a xref:running-tests/tags.adoc[tag] for the corresponding test class or test method. For example, if a test method is annotated with `@Category(Example.class)`, it will be tagged with `"com.acme.Example"`. Similar to the `Categories` runner in JUnit 4, this information can be used to filter the -discovered tests before executing them (see <> for details). +discovered tests before executing them (see xref:running-tests/intro.adoc[] for details). -[[migrating-from-junit4-parallel-execution]] -=== Parallel Execution +[[parallel-execution]] +== Parallel Execution The JUnit Vintage test engine supports parallel execution of top-level test classes and test methods, allowing existing JUnit 3 and JUnit 4 tests to benefit from improved performance through concurrent test execution. It can be enabled and configured using the following -<>: +xref:running-tests/configuration-parameters.adoc[configuration parameters]: `junit.vintage.execution.parallel.enabled=true|false`:: Enable/disable parallel execution (defaults to `false`). Requires opt-in for `classes` @@ -71,8 +68,8 @@ concurrent test execution. It can be enabled and configured using the following Specifies the size of the thread pool to be used for parallel execution. By default, the number of available processors is used. -[[migrating-from-junit4-parallel-execution-class-level]] -==== Parallelization at Class Level +[[parallel-execution-class-level]] +=== Parallelization at Class Level Let's assume we have two test classes `FooTest` and `BarTest` with each class containing three unit tests. Now, let's enable parallel execution of test classes: @@ -96,8 +93,8 @@ ForkJoinPool-1-worker-1 - BarTest::test3 ForkJoinPool-1-worker-2 - FooTest::test3 ---- -[[migrating-from-junit4-parallel-execution-method-level]] -==== Parallelization at Method Level +[[parallel-execution-method-level]] +=== Parallelization at Method Level Alternatively, we can enable parallel test execution at a method level, rather than the class level: @@ -122,8 +119,8 @@ ForkJoinPool-1-worker-2 - FooTest::test2 ForkJoinPool-1-worker-1 - FooTest::test3 ---- -[[migrating-from-junit4-parallel-execution-class-and-method-level]] -==== Full Parallelization +[[parallel-execution-class-and-method-level]] +=== Full Parallelization Finally, we can also enable parallelization at both class and method level: @@ -147,8 +144,8 @@ ForkJoinPool-1-worker-5 - BarTest::test2 ForkJoinPool-1-worker-4 - BarTest::test1 ---- -[[migrating-from-junit4-parallel-execution-pool-size]] -==== Configuring the Pool Size +[[parallel-execution-pool-size]] +=== Configuring the Pool Size The default thread pool size is equal to the number of available processors. However, we can also configure the pool size explicitly: @@ -179,8 +176,8 @@ As we can see, even though we set the thread pool size was four, only three thre used in this case. This happens because the pool adjusts the number of active threads based on workload and system needs. -[[migrating-from-junit4-parallel-execution-disabled]] -==== Sequential Execution +[[parallel-execution-disabled]] +=== Sequential Execution On the other hand, if we disable parallel execution, the `VintageTestEngine` will execute all tests sequentially, regardless of the other properties: @@ -195,8 +192,8 @@ junit.vintage.execution.parallel.methods=true Similarly, tests will be executed sequentially if you enable parallel execution in general but enable neither class-level nor method-level parallelization. -[[migrating-from-junit4-tips]] -=== Migration Tips +[[tips]] +== Migration Tips The following are topics that you should be aware of when migrating existing JUnit 4 tests to JUnit Jupiter. @@ -214,29 +211,29 @@ tests to JUnit Jupiter. * `@BeforeClass` and `@AfterClass` no longer exist; use `@BeforeAll` and `@AfterAll` instead. * `@Ignore` no longer exists: use `@Disabled` or one of the other built-in - <> instead - - See also <>. + xref:writing-tests/conditional-test-execution.adoc[execution conditions] instead + - See also <>. * `@Category` no longer exists; use `@Tag` instead. * `@RunWith` no longer exists; superseded by `@ExtendWith`. - For `@RunWith(Enclosed.class)` use `@Nested`. - - For `@RunWith(Parameterized.class)` see <>. + - For `@RunWith(Parameterized.class)` see <>. * `@Rule` and `@ClassRule` no longer exist; superseded by `@ExtendWith` and `@RegisterExtension`. - - See also <>. + - See also <>. * `@Test(expected = ...)` and the `ExpectedException` rule no longer exist; use `Assertions.assertThrows(...)` instead. - - See <> if you still need to use + - See <> if you still need to use `ExpectedException`. * Assertions and assumptions in JUnit Jupiter accept the failure message as their last argument instead of the first one. - - See <> for details. + - See <> for details. -[[migrating-from-junit4-tips-parameterized]] -==== Parameterized test classes +[[tips-parameterized]] +=== Parameterized test classes Unless `@UseParametersRunnerFactory` is used, a JUnit 4 parameterized test class can be converted into a JUnit Jupiter -<> by following these steps: +xref:writing-tests/parameterized-classes-and-tests.adoc[`@ParameterizedClass`] by following these steps: . Replace `@RunWith(Parameterized.class)` with `@ParameterizedClass`. . Add a class-level `@MethodSource("methodName")` annotation where `methodName` is the @@ -254,18 +251,18 @@ converted into a JUnit Jupiter [source,java,indent=0] .Before ---- -include::{testDir}/example/ParameterizedMigrationDemo.java[tags=before] +include::example$java/example/ParameterizedMigrationDemo.java[tags=before] ---- [source,java,indent=0] .After ---- -include::{testDir}/example/ParameterizedMigrationDemo.java[tags=after] +include::example$java/example/ParameterizedMigrationDemo.java[tags=after] ---- ==== -[[migrating-from-junit4-rule-support]] -=== Limited JUnit 4 Rule Support +[[rule-support]] +== Limited JUnit 4 Rule Support WARNING: _JUnit 4 rule support_ is deprecated for removal since version 6.0.0. Please migrate to the corresponding APIs and extensions provided by JUnit Jupiter. @@ -295,14 +292,14 @@ This limited form of `Rule` support can be switched on by the class-level annota all rule migration support extensions: `VerifierSupport`, `ExternalResourceSupport`, and `ExpectedExceptionSupport`. You may alternatively choose to annotate your test class with `@EnableJUnit4MigrationSupport` which registers migration support for rules _and_ JUnit -4's `@Ignore` annotation (see <>). +4's `@Ignore` annotation (see <>). However, if you intend to develop a new extension for JUnit Jupiter please use the new extension model of JUnit Jupiter instead of the rule-based model of JUnit 4. -[[migrating-from-junit4-ignore-annotation-support]] -=== JUnit 4 @Ignore Support +[[ignore-annotation-support]] +== JUnit 4 @Ignore Support WARNING: _JUnit 4 `@Ignore` support_ is deprecated for removal since version 6.0.0. Please use JUnit Jupiter's `@Disabled` annotation instead. @@ -315,18 +312,18 @@ To use `@Ignore` with JUnit Jupiter based tests, configure a _test_ dependency o `junit-jupiter-migrationsupport` module in your build and then annotate your test class with `@ExtendWith(IgnoreCondition.class)` or `{EnableJUnit4MigrationSupport}` (which automatically registers the `IgnoreCondition` along with -<>). The `IgnoreCondition` is an +<>). The `IgnoreCondition` is an `{ExecutionCondition}` that disables test classes or test methods that are annotated with `@Ignore`. [source,java,indent=0] ---- -include::{testDir}/example/IgnoredTestsDemo.java[tags=user_guide] +include::example$java/example/IgnoredTestsDemo.java[tags=user_guide] ---- -[[migrating-from-junit4-failure-message-arguments]] -=== Failure Message Arguments +[[failure-message-arguments]] +== Failure Message Arguments The `Assumptions` and `Assertions` classes in JUnit Jupiter declare arguments in a different order than in JUnit 4. In JUnit 4 assertion and assumption methods accept the diff --git a/documentation/src/docs/asciidoc/user-guide/overview.adoc b/documentation/modules/ROOT/pages/overview.adoc similarity index 56% rename from documentation/src/docs/asciidoc/user-guide/overview.adoc rename to documentation/modules/ROOT/pages/overview.adoc index d1e19b48962e..5a48feea6e43 100644 --- a/documentation/src/docs/asciidoc/user-guide/overview.adoc +++ b/documentation/modules/ROOT/pages/overview.adoc @@ -1,5 +1,5 @@ -[[overview]] -== Overview += Overview +:page-aliases: user-guide/index.adoc The goal of this document is to provide comprehensive reference documentation for programmers writing tests, extension authors, and engine authors as well as build tool @@ -11,26 +11,26 @@ This document is also available as a link:{userGuidePdfFileName}[PDF download]. endif::linkToPdf[] endif::backend-html5[] -[[overview-what-is-junit]] -=== What is JUnit? +[[what-is-junit]] +== What is JUnit? JUnit is composed of several different modules from three different sub-projects. **JUnit {version} = _JUnit Platform_ + _JUnit Jupiter_ + _JUnit Vintage_** -The **JUnit Platform** serves as a foundation for <> on the JVM. It also defines the `{TestEngine}` API for developing a testing +The **JUnit Platform** serves as a foundation for xref:advanced-topics/launcher-api.adoc[launching testing +frameworks] on the JVM. It also defines the `{TestEngine}` API for developing a testing framework that runs on the platform. Furthermore, the platform provides a -<> to launch the platform from the -command line and the <> for running a custom test suite using +xref:running-tests/console-launcher.adoc[Console Launcher] to launch the platform from the +command line and the xref:advanced-topics/junit-platform-suite-engine.adoc[] for running a custom test suite using one or more test engines on the platform. First-class support for the JUnit Platform also -exists in popular IDEs (see <>, -<>, <>, and -<>) and build tools (see <>, -<>, and <>). +exists in popular IDEs (see xref:running-tests/ide-support.adoc#intellij-idea[IntelliJ IDEA], +xref:running-tests/ide-support.adoc#eclipse[Eclipse], xref:running-tests/ide-support.adoc#netbeans[NetBeans], and +xref:running-tests/ide-support.adoc#vscode[Visual Studio Code]) and build tools (see xref:running-tests/build-support.adoc#gradle[Gradle], +xref:running-tests/build-support.adoc#maven[Maven], and xref:running-tests/build-support.adoc#ant[Ant]). -**JUnit Jupiter** is the combination of the <> and -<> for writing JUnit tests and extensions. The Jupiter +**JUnit Jupiter** is the combination of the xref:writing-tests/intro.adoc[programming model] and +xref:extensions/overview.adoc[extension model] for writing JUnit tests and extensions. The Jupiter sub-project provides a `TestEngine` for running Jupiter based tests on the platform. **JUnit Vintage** provides a `TestEngine` for running JUnit 3 and JUnit 4 based tests on @@ -39,43 +39,43 @@ path. Note, however, that the JUnit Vintage engine is deprecated and should only temporarily while migrating tests to JUnit Jupiter or another testing framework with native JUnit Platform support. -[[overview-java-versions]] -=== Supported Java Versions +[[java-versions]] +== Supported Java Versions JUnit requires Java 17 (or higher) at runtime. However, you can still test code that has been compiled with previous versions of the JDK. -[[overview-getting-help]] -=== Getting Help +[[getting-help]] +== Getting Help Ask JUnit-related questions on {StackOverflow} or use the {DiscussionsQA}. -[[overview-getting-started]] -=== Getting Started +[[getting-started]] +== Getting Started -[[overview-getting-started-junit-artifacts]] -==== Downloading JUnit Artifacts +[[getting-started-junit-artifacts]] +=== Downloading JUnit Artifacts To find out what artifacts are available for download and inclusion in your project, refer -to <>. To set up dependency management for your build, refer to -<> and the <>. +to xref:appendix.adoc#dependency-metadata[Dependency Metadata]. To set up dependency management for your build, refer to +xref:running-tests/build-support.adoc[] and the <>. -[[overview-getting-started-features]] -==== JUnit Features +[[getting-started-features]] +=== JUnit Features To find out what features are available in JUnit {version} and how to use them, read the corresponding sections of this User Guide, organized by topic. -* <> -* <> -* <> -* <> +* xref:writing-tests/intro.adoc[Writing Tests in JUnit Jupiter] +* xref:migrating-from-junit4.adoc[Migrating from JUnit 4 to JUnit Jupiter] +* xref:running-tests/intro.adoc[] +* xref:extensions/overview.adoc[Extension Model for JUnit Jupiter] * Advanced Topics - - <> - - <> + - xref:advanced-topics/launcher-api.adoc[] + - xref:advanced-topics/testkit.adoc[] -[[overview-getting-started-example-projects]] -==== Example Projects +[[getting-started-example-projects]] +=== Example Projects To see complete, working examples of projects that you can copy and experiment with, the {junit-examples-repo}[`junit-examples`] repository is a good place to start. The diff --git a/documentation/modules/ROOT/pages/release-notes.adoc b/documentation/modules/ROOT/pages/release-notes.adoc new file mode 100644 index 000000000000..f816e5a663dd --- /dev/null +++ b/documentation/modules/ROOT/pages/release-notes.adoc @@ -0,0 +1,20 @@ += Release Notes +:page-aliases: release-notes/index.adoc +// +// + +This document contains the change log for all JUnit releases since 6.0 GA. + +Please refer to the xref:overview.adoc[User Guide] for comprehensive +reference documentation for programmers writing tests, extension authors, and engine +authors as well as build tool and IDE vendors. + +include::partial$release-notes/release-notes-6.1.0-M2.adoc[] + +include::partial$release-notes/release-notes-6.1.0-M1.adoc[] + +include::partial$release-notes/release-notes-6.0.2.adoc[] + +include::partial$release-notes/release-notes-6.0.1.adoc[] + +include::partial$release-notes/release-notes-6.0.0.adoc[] diff --git a/documentation/modules/ROOT/pages/running-tests/build-support.adoc b/documentation/modules/ROOT/pages/running-tests/build-support.adoc new file mode 100644 index 000000000000..36978dcb5559 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/build-support.adoc @@ -0,0 +1,547 @@ += Build Support + +[[gradle]] +== Gradle + +Starting with https://docs.gradle.org/4.6/release-notes.html[version 4.6], Gradle provides +https://docs.gradle.org/current/userguide/java_testing.html#using_junit5[native support] +for executing tests on the JUnit Platform. To enable it, you need to specify +`useJUnitPlatform()` within a `test` task declaration in `build.gradle`: + +[source,groovy,indent=0] +[subs=attributes+] +---- +test { + useJUnitPlatform() +} +---- + +Filtering by xref:running-tests/tags.adoc[tags], +xref:running-tests/tags.adoc#expressions[tag expressions], or engines is also supported: + +[source,groovy,indent=0] +[subs=attributes+] +---- +test { + useJUnitPlatform { + includeTags("fast", "smoke & feature-a") + // excludeTags("slow", "ci") + includeEngines("junit-jupiter") + // excludeEngines("junit-vintage") + } +} +---- + +Please refer to the +https://docs.gradle.org/current/userguide/java_testing.html[official Gradle documentation] +for a comprehensive list of options. + +[[gradle-bom]] +=== Aligning dependency versions + +TIP: See <> for details on how to override the version +of JUnit used in your Spring Boot application. + +Unless you're using Spring Boot which defines its own way of managing dependencies, it is +recommended to use the JUnit Platform xref:appendix.adoc#dependency-metadata-junit-bom[Bill of Materials (BOM)] to align the +versions of all JUnit artifacts. + +[source,groovy,indent=0] +[subs=attributes+] +.Explicit platform dependency on the BOM +---- +dependencies { + testImplementation(platform("org.junit:junit-bom:{version}")) + testImplementation("org.junit.jupiter:junit-jupiter") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} +---- + +Using the BOM allows you to omit the version when declaring dependencies on all artifacts +with the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs. + +Since all JUnit artifacts declare a +https://docs.gradle.org/current/userguide/platforms.html[platform] dependency on the BOM, +you usually don't need to declare an explicit dependency on it yourself. Instead, it's +sufficient to declare _one_ regular dependency that includes a version number. Gradle will +then pull in the BOM automatically so you can omit the version for all other JUnit +artifacts. + +[source,groovy,indent=0] +[subs=attributes+] +.Implicit platform dependency on the BOM +---- +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:{version}") // <1> + testRuntimeOnly("org.junit.platform:junit-platform-launcher") // <2> +} +---- +<1> Dependency declaration with explicit version. Pulls in the `junit-bom` automatically. +<2> Dependency declaration without version. The version is supplied by the `junit-bom`. + +[WARNING] +.Declaring a dependency on junit-platform-launcher +==== +Even though pre-8.0 versions of Gradle don't require declaring an explicit +dependency on `junit-platform-launcher`, it is recommended to do so to ensure the versions +of JUnit artifacts on the test runtime classpath are aligned. + +Moreover, doing so is recommended and in some cases even required when importing the +project into an IDE like xref:running-tests/ide-support.adoc#eclipse[Eclipse] or +xref:running-tests/ide-support.adoc#intellij-idea[IntelliJ IDEA]. +==== + +[[gradle-engines-configure]] +=== Configuring Test Engines + +In order to run any tests at all, a `TestEngine` implementation must be on the classpath. + +To configure support for JUnit Jupiter based tests, configure a `testImplementation` dependency +on the dependency-aggregating JUnit Jupiter artifact similar to the following. + +[source,groovy,indent=0] +[subs=attributes+] +---- +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:{version}") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} +---- + +Alternatively, you can use Gradle's +https://docs.gradle.org/current/userguide/jvm_test_suite_plugin.html[JVM Test Suite] +support. + +[source,kotlin,indent=0] +[subs=attributes+] +.Kotlin DSL +---- +testing { + suites { + named("test") { + useJUnitJupiter("{version}") + } + } +} +---- + +[source,groovy,indent=0] +[subs=attributes+] +.Groovy DSL +---- +testing { + suites { + test { + useJUnitJupiter("{version}") + } + } +} +---- + +The JUnit Platform can run JUnit 4 based tests as long as you configure a `testImplementation` +dependency on JUnit 4 and a `testRuntimeOnly` dependency on the JUnit Vintage `TestEngine` +implementation similar to the following. + +[source,groovy,indent=0] +[subs=attributes+] +---- +dependencies { + testImplementation("junit:junit:{junit4-version}") + testRuntimeOnly("org.junit.vintage:junit-vintage-engine:{version}") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} +---- + +[[gradle-config-params]] +=== Configuration Parameters + +The standard Gradle `test` task currently does not provide a dedicated DSL to set JUnit +Platform xref:running-tests/configuration-parameters.adoc[configuration parameters] to influence test +discovery and execution. However, you can provide configuration parameters within the +build script via system properties (as shown below) or via the +`junit-platform.properties` file. + +[source,groovy,indent=0] +---- +test { + // ... + systemProperty("junit.jupiter.conditions.deactivate", "*") + systemProperty("junit.jupiter.extensions.autodetection.enabled", true) + systemProperty("junit.jupiter.testinstance.lifecycle.default", "per_class") + // ... +} +---- + +[[gradle-logging]] +=== Configuring Logging (optional) + +JUnit uses the Java Logging APIs in the `java.util.logging` package (a.k.a. _JUL_) to +emit warnings and debug information. Please refer to the official documentation of +`{LogManager}` for configuration options. + +Alternatively, it's possible to redirect log messages to other logging frameworks such as +{Log4j} or {Logback}. To use a logging framework that provides a custom implementation of +`{LogManager}`, set the `java.util.logging.manager` system property to the _fully +qualified class name_ of the `{LogManager}` implementation to use. The example below +demonstrates how to configure Log4j{nbsp}2.x (see {Log4j_JDK_Logging_Adapter} for +details). + +[source,groovy,indent=0] +[subs=attributes+] +---- +test { + systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager") + // Avoid overhead (see https://logging.apache.org/log4j/2.x/manual/jmx.html#enabling-jmx) + systemProperty("log4j2.disableJmx", "true") +} +---- + +Other logging frameworks provide different means to redirect messages logged using +`java.util.logging`. For example, for {Logback} you can use the +https://www.slf4j.org/legacy.html#jul-to-slf4j[JUL to SLF4J Bridge] by adding it as a +dependency to the test runtime classpath. + +[[maven]] +== Maven + +Maven Surefire and Maven Failsafe provide +https://maven.apache.org/surefire/maven-surefire-plugin/examples/junit-platform.html[native support] +for executing tests on the JUnit Platform. The `pom.xml` file in the +`{junit-jupiter-starter-maven}` project demonstrates how to use the Maven Surefire plugin +and can serve as a starting point for configuring your Maven build. + +[WARNING] +.Minimum required version of Maven Surefire/Failsafe +==== +As of JUnit 6.0, the minimum required version of Maven Surefire/Failsafe is 3.0.0. +==== + +[[maven-bom]] +=== Aligning dependency versions + +Unless you're using Spring Boot which defines its own way of managing dependencies, it is +recommended to use the JUnit Platform xref:appendix.adoc#dependency-metadata-junit-bom[Bill of Materials (BOM)] to align the +versions of all JUnit artifacts. + +[source,xml,indent=0] +[subs=attributes+] +---- + + + + org.junit + junit-bom + {version} + pom + import + + + +---- + +Using the BOM allows you to omit the version when declaring dependencies on all artifacts +with the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs. + +TIP: See <> for details on how to override the version +of JUnit used in your Spring Boot application. + +[[maven-engines-configure]] +=== Configuring Test Engines + +In order to have Maven Surefire or Maven Failsafe run any tests at all, at least one +`TestEngine` implementation must be added to the test classpath. + +To configure support for JUnit Jupiter based tests, configure `test` scoped dependencies +on the JUnit Jupiter API and the JUnit Jupiter `TestEngine` implementation similar to the +following. + +[source,xml,indent=0] +[subs=attributes+] +---- + + + + + org.junit.jupiter + junit-jupiter + {version} + test + + + + + + + maven-surefire-plugin + {surefire-version} + + + maven-failsafe-plugin + {surefire-version} + + + + +---- + +Maven Surefire and Maven Failsafe can run JUnit 4 based tests alongside Jupiter tests as +long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintage +`TestEngine` implementation similar to the following. + +[source,xml,indent=0] +[subs=attributes+] +---- + + + + + junit + junit + {junit4-version} + test + + + org.junit.vintage + junit-vintage-engine + {version} + test + + + + + + + + maven-surefire-plugin + {surefire-version} + + + maven-failsafe-plugin + {surefire-version} + + + + +---- + +[[maven-filter-test-class-names]] +=== Filtering by Test Class Names + +The Maven Surefire Plugin will scan for test classes whose fully qualified names match +the following patterns. + +- `+++**/Test*.java+++` +- `+++**/*Test.java+++` +- `+++**/*Tests.java+++` +- `+++**/*TestCase.java+++` + +Moreover, it will exclude all nested classes (including static member classes) by default. + +Note, however, that you can override this default behavior by configuring explicit +`include` and `exclude` rules in your `pom.xml` file. For example, to keep Maven Surefire +from excluding static member classes, you can override its exclude rules as follows. + +[source,xml,indent=0] +[subs=attributes+] +.Overriding exclude rules of Maven Surefire +---- + + + + + maven-surefire-plugin + {surefire-version} + + + + + + + + + +---- + +Please see the +https://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html[Inclusions and Exclusions of Tests] +documentation for Maven Surefire for details. + +[[maven-filter-tags]] +=== Filtering by Tags + +You can filter tests by xref:running-tests/tags.adoc[tags] or +xref:running-tests/tags.adoc#expressions[tag expressions] using the following configuration +properties. + +- to include _tags_ or _tag expressions_, use `groups`. +- to exclude _tags_ or _tag expressions_, use `excludedGroups`. + +[source,xml,indent=0] +[subs=attributes+] +---- + + + + + maven-surefire-plugin + {surefire-version} + + acceptance | !feature-a + integration, regression + + + + + +---- + +[[maven-config-params]] +=== Configuration Parameters + +You can set JUnit Platform xref:running-tests/configuration-parameters.adoc[configuration parameters] to +influence test discovery and execution by declaring the `configurationParameters` +property and providing key-value pairs using the Java `Properties` file syntax (as shown +below) or via the `junit-platform.properties` file. + +[source,xml,indent=0] +[subs=attributes+] +---- + + + + + maven-surefire-plugin + {surefire-version} + + + + junit.jupiter.conditions.deactivate = * + junit.jupiter.extensions.autodetection.enabled = true + junit.jupiter.testinstance.lifecycle.default = per_class + + + + + + + +---- + +[[ant]] +== Ant + +Starting with version `1.10.3`, link:https://ant.apache.org/[Ant] has a +link:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher`] task that +provides native support for launching tests on the JUnit Platform. The `junitlauncher` +task is solely responsible for launching the JUnit Platform and passing it the selected +collection of tests. The JUnit Platform then delegates to registered test engines to +discover and execute the tests. + +The `junitlauncher` task attempts to align as closely as possible with native Ant +constructs such as +link:https://ant.apache.org/manual/Types/resources.html#collection[resource collections] +for allowing users to select the tests that they want executed by test engines. This gives +the task a consistent and natural feel when compared to many other core Ant tasks. + +Starting with version `1.10.6` of Ant, the `junitlauncher` task supports +link:https://ant.apache.org/manual/Tasks/junitlauncher.html#fork[forking the tests in a separate JVM]. + +The `build.xml` file in the `{junit-jupiter-starter-ant}` project demonstrates how to use +the task and can serve as a starting point. + +=== Basic Usage + +The following example demonstrates how to configure the `junitlauncher` task to select a +single test class (i.e., `com.example.project.CalculatorTests`). + +[source,xml,indent=0] +---- + + + + + + + + + + + +---- + +The `test` element allows you to specify a single test class that you want to be selected +and executed. The `classpath` element allows you to specify the classpath to be used to +launch the JUnit Platform. This classpath will also be used to locate test classes that +are part of the execution. + +The following example demonstrates how to configure the `junitlauncher` task to select +test classes from multiple locations. + +[source,xml,indent=0] +---- + + + + + + + + + + + + + + + + +---- + +In the above example, the `testclasses` element allows you to select multiple test +classes that reside in different locations. + +For further details on usage and configuration options please refer to the official Ant +documentation for the +link:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher` task]. + +[[spring-boot]] +== Spring Boot + +link:https://spring.io/projects/spring-boot[Spring Boot] provides automatic support for +managing the version of JUnit used in your project. In addition, the +`spring-boot-starter-test` artifact automatically includes testing libraries such as JUnit +Jupiter, AssertJ, Mockito, etc. + +If your build relies on dependency management support from Spring Boot, you should not +import JUnit's xref:appendix.adoc#dependency-metadata-junit-bom[Bill of Materials (BOM)] in your build script since that would +result in duplicate (and potentially conflicting) management of JUnit dependencies. + +If you need to override the version of a dependency used in your Spring Boot application, +you have to override the exact name of the +link:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#appendix.dependency-versions.properties[version property] +defined in the BOM used by the Spring Boot plugin. For example, the name of the JUnit +Jupiter version property in Spring Boot is `junit-jupiter.version`. The mechanism for +changing a dependency version is documented for both +link:https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#managing-dependencies.dependency-management-plugin.customizing[Gradle] +and +link:https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/#using.parent-pom[Maven]. + +With Gradle you can override the JUnit Jupiter version by including the following in your +`build.gradle` file. + +[source,groovy,indent=0] +[subs=attributes+] +---- + ext['junit-jupiter.version'] = '{version}' +---- + +With Maven you can override the JUnit Jupiter version by including the following in your +`pom.xml` file. + +[source,xml,indent=0] +[subs=attributes+] +---- + + {version} + +---- diff --git a/documentation/modules/ROOT/pages/running-tests/capturing-standard-output-error.adoc b/documentation/modules/ROOT/pages/running-tests/capturing-standard-output-error.adoc new file mode 100644 index 000000000000..a15cb60ab1f8 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/capturing-standard-output-error.adoc @@ -0,0 +1,18 @@ += Capturing Standard Output/Error + +The JUnit Platform provides opt-in support for capturing output printed to `System.out` +and `System.err`. To enable it, set the `junit.platform.output.capture.stdout` and/or +`junit.platform.output.capture.stderr` xref:running-tests/configuration-parameters.adoc[configuration +parameter] to `true`. In addition, you may configure the maximum number of buffered bytes +to be used per executed test or container using `junit.platform.output.capture.maxBuffer`. + +If enabled, the JUnit Platform captures the corresponding output and publishes it as a +report entry using the `stdout` or `stderr` keys to all registered +`{TestExecutionListener}` instances immediately before reporting the test or container as +finished. + +Please note that the captured output will only contain output emitted by the thread that +was used to execute a container or test. Any output by other threads will be omitted +because particularly when +xref:writing-tests/parallel-execution.adoc[executing tests in parallel] it would be impossible +to attribute it to a specific test or container. diff --git a/documentation/modules/ROOT/pages/running-tests/configuration-parameters.adoc b/documentation/modules/ROOT/pages/running-tests/configuration-parameters.adoc new file mode 100644 index 000000000000..8688d0e27862 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/configuration-parameters.adoc @@ -0,0 +1,76 @@ += Configuration Parameters + +In addition to instructing the platform which test classes and test engines to include, +which packages to scan, etc., it is sometimes necessary to provide additional custom +configuration parameters that are specific to a particular test engine, listener, or +registered extension. For example, the JUnit Jupiter `TestEngine` supports _configuration +parameters_ for the following use cases. + +- xref:writing-tests/test-instance-lifecycle.adoc#default[Changing the Default Test Instance Lifecycle] +- xref:extensions/registering-extensions.adoc#registration-automatic-enabling[Enabling Automatic Extension Detection] +- xref:extensions/conditional-test-execution.adoc#deactivation[Deactivating Conditions] +- xref:writing-tests/display-names.adoc#generator-default[Setting the Default Display Name Generator] + +_Configuration Parameters_ are text-based key-value pairs that can be supplied to test +engines running on the JUnit Platform via one of the following mechanisms. + +1. The `configurationParameter()` and `configurationParameters()` methods in + `LauncherDiscoveryRequestBuilder` which + is used to build a request supplied to the xref:advanced-topics/launcher-api.adoc[Launcher API]. + + + When running tests via one of the tools provided by the JUnit Platform you can specify + configuration parameters as follows: + * xref:running-tests/console-launcher.adoc[Console Launcher]: use the `--config` command-line + option. + * xref:running-tests/build-support.adoc#gradle-config-params[Gradle]: use the `systemProperty` or + `systemProperties` DSL. + * xref:running-tests/build-support.adoc#maven-config-params[Maven Surefire provider]: use the + `configurationParameters` property. +2. The `configurationParametersResources()` method in `LauncherDiscoveryRequestBuilder`. + + + When running tests via the xref:running-tests/console-launcher.adoc[Console Launcher] you can + specify custom configuration files using the `--config-resource` command-line option. +3. JVM system properties. +4. The JUnit Platform default configuration file: a file named `junit-platform.properties` + in the root of the class path that follows the syntax rules for Java `Properties` + files. + +NOTE: Configuration parameters are looked up in the exact order defined above. +Consequently, configuration parameters supplied directly to the `Launcher` take +precedence over those supplied via custom configuration files, system properties, and the +default configuration file. Similarly, configuration parameters supplied via system +properties take precedence over those supplied via the default configuration file. + +[[pattern]] +== Pattern Matching Syntax + +This section describes the pattern matching syntax that is applied to the _configuration +parameters_ used for the following features. + +- xref:extensions/conditional-test-execution.adoc#deactivation[Deactivating Conditions] +- xref:advanced-topics/launcher-api.adoc#listeners-custom-deactivation[Deactivating a TestExecutionListener] +- xref:running-tests/stack-trace-pruning.adoc[] +- xref:extensions/registering-extensions.adoc#registration-automatic-filtering[Filtering Auto-detected Extensions] + +If the value for the given _configuration parameter_ consists solely of an asterisk +(`+++*+++`), the pattern will match against all candidate classes. Otherwise, the value +will be treated as a comma-separated list of patterns where each pattern will be matched +against the fully qualified class name (_FQCN_) of each candidate class. Any dot (`.`) in +a pattern will match against a dot (`.`) or a dollar sign (`$`) in a FQCN. Any asterisk +(`+++*+++`) will match against one or more characters in a FQCN. All other characters in a +pattern will be matched one-to-one against a FQCN. + +Examples: + +- `+++*+++`: matches all candidate classes. +- `+++org.junit.*+++`: matches all candidate classes under the `org.junit` base package and + any of its subpackages. +- `+++*.MyCustomImpl+++`: matches every candidate class whose simple class name is exactly + `MyCustomImpl`. +- `+++*System*+++`: matches every candidate class whose FQCN contains `System`. +- `+++*System*+++, +++*Unit*+++`: matches every candidate class whose FQCN contains + `System` or `Unit`. +- `org.example.MyCustomImpl`: matches the candidate class whose FQCN is exactly + `org.example.MyCustomImpl`. +- `org.example.MyCustomImpl, org.example.TheirCustomImpl`: matches candidate classes whose + FQCN is exactly `org.example.MyCustomImpl` or `org.example.TheirCustomImpl`. diff --git a/documentation/modules/ROOT/pages/running-tests/console-launcher.adoc b/documentation/modules/ROOT/pages/running-tests/console-launcher.adoc new file mode 100644 index 000000000000..1e7da4d9bd48 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/console-launcher.adoc @@ -0,0 +1,181 @@ += Console Launcher + +The `{ConsoleLauncher}` is a command-line Java application that lets you launch the JUnit +Platform from the console. For example, it can be used to run JUnit Vintage and JUnit +Jupiter tests and print test execution results to the console. + +An executable _Fat JAR_ (`junit-platform-console-standalone-{version}.jar`) that +contains the contents of all of its dependencies is published in the {Maven_Central} +repository under the +https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone] +directory. It contains the contents of the following artifacts: + +include::partial$console-launcher-standalone-shadowed-artifacts.adoc[] + +[NOTE] +==== +Since the `junit-platform-console-standalone` JAR contains the contents of all of its +dependencies, its Maven POM does not declare any dependencies. + +Furthermore, it is not very likely that you would need to include a dependency on the +`junit-platform-console-standalone` artifact in your project's Maven POM or Gradle build +script. On the contrary, the executable `junit-platform-console-standalone` JAR is +typically invoked directly from the command line or a shell script without a build script. + +If you need to declare dependencies in your build script on some of the artifacts +contained in the `junit-platform-console-standalone` artifact, you should declare +dependencies only on the JUnit artifacts that are used in your project. To simplify +dependency management of JUnit artifacts in your build, you may wish to use the +`junit-jupiter` aggregator artifact or `junit-bom`. See xref:appendix.adoc#dependency-metadata[Dependency Metadata] for +details. +==== + +You can https://docs.oracle.com/javase/tutorial/deployment/jar/run.html[run] the +standalone `ConsoleLauncher` as shown below. + +[source,console,subs=attributes+] +---- +$ java -jar junit-platform-console-standalone-{version}.jar execute + +├─ JUnit Vintage +│ └─ example.JUnit4Tests +│ └─ standardJUnit4Test ✔ +└─ JUnit Jupiter + ├─ StandardTests + │ ├─ succeedingTest() ✔ + │ └─ skippedTest() ↷ for demonstration purposes + └─ A special test case + ├─ Custom test name containing spaces ✔ + ├─ ╯°□°)╯ ✔ + └─ 😱 ✔ + +Test run finished after 64 ms +[ 5 containers found ] +[ 0 containers skipped ] +[ 5 containers started ] +[ 0 containers aborted ] +[ 5 containers successful ] +[ 0 containers failed ] +[ 6 tests found ] +[ 1 tests skipped ] +[ 5 tests started ] +[ 0 tests aborted ] +[ 5 tests successful ] +[ 0 tests failed ] +---- + +You can also run the standalone `ConsoleLauncher` as shown below (for example, to include +all jars in a directory): + +[source,console,subs=attributes+] +---- +$ java -cp classes:testlib/* org.junit.platform.console.ConsoleLauncher +---- + +[[options]] +== Subcommands and Options + +The `{ConsoleLauncher}` provides the following subcommands: + +---- +include::partial$console-launcher-options.txt[] +---- + +[[options-discovering-tests]] +=== Discovering tests + +---- +include::partial$console-launcher-discover-options.txt[] +---- + +[[options-executing-tests]] +=== Executing tests + +.Exit Code +NOTE: On successful runs, the `{ConsoleLauncher}` exits with a status code of `0`. +All non-zero codes indicate an error of some sort. For example, status code `1` +is returned if any containers or tests failed. If no tests are discovered and the +`--fail-if-no-tests` command-line option is supplied, the `ConsoleLauncher` exits +with a status code of `2`. Unexpected or invalid user input yields a status code +of `3`. An exit code of `-1` indicates an unspecified error condition. + +---- +include::partial$console-launcher-execute-options.txt[] +---- + +[[options-listing-test-engines]] +=== Listing test engines + +---- +include::partial$console-launcher-engines-options.txt[] +---- + +[[argument-files]] +== Argument Files (@-files) + +On some platforms you may run into system limitations on the length of a command line when +creating a command line with lots of options or with long arguments. + +The `ConsoleLauncher` supports _argument files_, also known as _@-files_. Argument files +are files that themselves contain arguments to be passed to the command. When the +underlying https://github.com/remkop/picocli[picocli] command line parser encounters an +argument beginning with the character `@`, it expands the contents of that file into the +argument list. + +The arguments within a file can be separated by spaces or newlines. If an argument +contains embedded whitespace, the whole argument should be wrapped in double or single +quotes -- for example, `"-f=My Files/Stuff.java"`. + +If the argument file does not exist or cannot be read, the argument will be treated +literally and will not be removed. This will likely result in an "unmatched argument" +error message. You can troubleshoot such errors by executing the command with the +`picocli.trace` system property set to `DEBUG`. + +Multiple _@-files_ may be specified on the command line. The specified path may be +relative to the current directory or absolute. + +You can pass a real parameter with an initial `@` character by escaping it with an +additional `@` symbol. For example, `@@somearg` will become `@somearg` and will not be +subject to expansion. + +[[redirecting-stdout-and-stderr]] +== Redirecting Standard Output/Error to Files + +You can redirect the `System.out` (stdout) and `System.err` (stderr) output streams to +files using the `--redirect-stdout` and `--redirect-stderr` options: + +[source,console,subs=attributes+] +---- +$ java -jar junit-platform-console-standalone-{version}.jar \ + --redirect-stdout=stdout.txt \ + --redirect-stderr=stderr.txt +---- + +[NOTE] +==== +If the `--redirect-stdout` and `--redirect-stderr` arguments point to the same file, both +output streams will be redirected to that file. + +The default charset is used for writing to the files. +==== + +[[color-customization]] +== Color Customization + +The colors used in the output of the `{ConsoleLauncher}` can be customized. +The option `--single-color` will apply a built-in monochrome style, while +`--color-palette` will accept a properties file to override the +https://en.wikipedia.org/wiki/ANSI_escape_code#Colors[ANSI SGR] color styling. +The properties file below demonstrates the default style: + +[source,properties,indent=0] +---- +SUCCESSFUL = 32 +ABORTED = 33 +FAILED = 31 +SKIPPED = 35 +CONTAINER = 35 +TEST = 34 +DYNAMIC = 35 +REPORTED = 37 +---- diff --git a/documentation/modules/ROOT/pages/running-tests/discovery-issues.adoc b/documentation/modules/ROOT/pages/running-tests/discovery-issues.adoc new file mode 100644 index 000000000000..c2f358be82b4 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/discovery-issues.adoc @@ -0,0 +1,35 @@ += Discovery Issues + +Test engines may encounter issues during test discovery. For example, the declaration of a +test class or method may be invalid. To avoid such issues from going unnoticed, the JUnit +Platform provides a xref:advanced-topics/engines.adoc#discovery-issues[mechanism for test engines] to +report them with different severity levels: + +INFO:: +Indicates that the engine encountered something that could be potentially problematic, but +could also happen due to a valid setup or configuration. + +WARNING:: +Indicates that the engine encountered something that is problematic and might lead to +unexpected behavior or will be removed or changed in a future release. + +ERROR:: +Indicates that the engine encountered something that is definitely problematic and will +lead to unexpected behavior. + +If an engine reports an issue with a severity equal to or higher than a configurable +_critical_ severity, its tests will not be executed. Instead, the engine will be reported +as failed during execution with a `{DiscoveryIssueException}` listing all critical issues. +Non-critical issues will be logged but will not prevent the engine from executing its +tests. The `junit.platform.discovery.issue.severity.critical` +xref:running-tests/configuration-parameters.adoc[configuration parameter] can be used to set the critical +severity level. Currently, the default value is `ERROR` but it may be changed in a future +release. + +TIP: To surface all discovery issues in your project, it is recommended to set the +`junit.platform.discovery.issue.severity.critical` configuration parameter to `INFO`. + +In addition, registered `{LauncherDiscoveryListener}` implementations can receive +discovery issues via the `issueEncountered()` method. This allows IDEs and build tools to +report issues to the user in a more user-friendly way. For example, IDEs may choose to +display all issues in a list or table. diff --git a/documentation/modules/ROOT/pages/running-tests/discovery-selectors.adoc b/documentation/modules/ROOT/pages/running-tests/discovery-selectors.adoc new file mode 100644 index 000000000000..5ee75c0d9044 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/discovery-selectors.adoc @@ -0,0 +1,29 @@ += Discovery Selectors + +The JUnit Platform provides a rich set of discovery selectors that can be used to specify +which tests should be discovered or executed. + +Discovery selectors can be created programmatically using the factory methods in the +`{DiscoverySelectors}` class, specified declaratively via annotations when using the +xref:advanced-topics/junit-platform-suite-engine.adoc[], via options of the xref:running-tests/console-launcher.adoc[], or +generically as strings via their identifiers. + +The following discovery selectors are provided out of the box: + +|=== +| Java Type | API | Annotation | Console Launcher | Identifier + +| `{ClasspathResourceSelector}` | `{DiscoverySelectors_selectClasspathResource}` | `{SelectClasspathResource}` | `--select-resource /foo.csv` | `resource:/foo.csv` +| `{ClasspathRootSelector}` | `{DiscoverySelectors_selectClasspathRoots}` | -- | `--scan-classpath bin` | `classpath-root:bin` +| `{ClassSelector}` | `{DiscoverySelectors_selectClass}` | `{SelectClasses}` | `--select-class com.acme.Foo` | `class:com.acme.Foo` +| `{DirectorySelector}` | `{DiscoverySelectors_selectDirectory}` | `{SelectDirectories}` | `--select-directory foo/bar` | `directory:foo/bar` +| `{FileSelector}` | `{DiscoverySelectors_selectFile}` | `{SelectFile}` | `--select-file dir/foo.txt` | `file:dir/foo.txt` +| `{IterationSelector}` | `{DiscoverySelectors_selectIteration}` | `{Select}("")` | `--select-iteration method=com.acme.Foo#m[1..2]` | `iteration:method:com.acme.Foo#m[1..2]` +| `{MethodSelector}` | `{DiscoverySelectors_selectMethod}` | `{SelectMethod}` | `--select-method com.acme.Foo#m` | `method:com.acme.Foo#m` +| `{ModuleSelector}` | `{DiscoverySelectors_selectModule}` | `{SelectModules}` | `--select-module com.acme` | `module:com.acme` +| `{NestedClassSelector}` | `{DiscoverySelectors_selectNestedClass}` | `{Select}("")` | `--select ` | `nested-class:com.acme.Foo/Bar` +| `{NestedMethodSelector}` | `{DiscoverySelectors_selectNestedMethod}` | `{Select}("")` | `--select ` | `nested-method:com.acme.Foo/Bar#m` +| `{PackageSelector}` | `{DiscoverySelectors_selectPackage}` | `{SelectPackages}` | `--select-package com.acme.foo` | `package:com.acme.foo` +| `{UniqueIdSelector}` | `{DiscoverySelectors_selectUniqueId}` | `{Select}("")` | `--select-unique-id ` | `uid:[engine:Foo]/[segment:Bar]` +| `{UriSelector}` | `{DiscoverySelectors_selectUri}` | `{SelectUris}` | `--select-uri \file:///foo.txt` | `uri:file:///foo.txt` +|=== diff --git a/documentation/modules/ROOT/pages/running-tests/ide-support.adoc b/documentation/modules/ROOT/pages/running-tests/ide-support.adoc new file mode 100644 index 000000000000..2cb3d8b551f0 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/ide-support.adoc @@ -0,0 +1,101 @@ += IDE Support + +[[intellij-idea]] +== IntelliJ IDEA + +IntelliJ IDEA supports running tests on the JUnit Platform since version 2016.2. For more +information, please consult this https://jb.gg/junit-idea/[IntelliJ IDEA resource]. Note, +however, that it is recommended to use IDEA 2017.3 or newer since more recent versions of +IDEA download the following JARs automatically based on the API version used in the +project: `junit-platform-launcher`, `junit-jupiter-engine`, and `junit-vintage-engine`. + +In order to use a different JUnit version (e.g., {version}), you may need to +include the corresponding versions of the `junit-platform-launcher`, +`junit-jupiter-engine`, and `junit-vintage-engine` JARs in the classpath. + +.Additional Gradle Dependencies +[source,groovy] +[subs=attributes+] +---- +testImplementation(platform("org.junit:junit-bom:{version}")) +testRuntimeOnly("org.junit.platform:junit-platform-launcher") +testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") +testRuntimeOnly("org.junit.vintage:junit-vintage-engine") +---- + +.Additional Maven Dependencies +[source,xml] +[subs=attributes+] +---- + + + + org.junit.platform + junit-platform-launcher + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.vintage + junit-vintage-engine + test + + + + + + org.junit + junit-bom + {version} + pom + import + + + +---- + +[[eclipse]] +== Eclipse + +Eclipse IDE offers support for the JUnit Platform since the Eclipse Oxygen.1a (4.7.1a) +release. + +For more information on using JUnit Platform in Eclipse consult the official _Eclipse +support for JUnit 5_ section of the +https://www.eclipse.org/eclipse/news/4.7.1a/#junit-5-support[Eclipse Project Oxygen.1a +(4.7.1a) - New and Noteworthy] documentation. + +[[netbeans]] +== NetBeans + +NetBeans offers support for JUnit Jupiter and the JUnit Platform since the +https://netbeans.apache.org/download/nb100/nb100.html[Apache NetBeans 10.0 release]. + +For more information consult the JUnit 5 section of the +https://netbeans.apache.org/download/nb100/index.html#_junit_5[Apache NetBeans 10.0 +release notes]. + +[[vscode]] +== Visual Studio Code + +https://code.visualstudio.com/[Visual Studio Code] supports JUnit Jupiter and the JUnit +Platform via the +https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-test[Java Test +Runner] extension which is installed by default as part of the +https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack[Java +Extension Pack]. + +For more information consult the _Testing_ section of the +https://code.visualstudio.com/docs/languages/java#_testing[Java in Visual Studio Code] +documentation. + +[[other]] +== Other IDEs + +If you are using an editor or IDE other than one of those listed in the previous sections, +and it doesn't support running tests on the JUnit Platform, you can use the +xref:running-tests/console-launcher.adoc[] to run them from the command line. diff --git a/documentation/modules/ROOT/pages/running-tests/intro.adoc b/documentation/modules/ROOT/pages/running-tests/intro.adoc new file mode 100644 index 000000000000..82162ef9cb47 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/intro.adoc @@ -0,0 +1,3 @@ += Running Tests + +This section explains how to run tests from IDEs and build tools. diff --git a/documentation/modules/ROOT/pages/running-tests/source-launcher.adoc b/documentation/modules/ROOT/pages/running-tests/source-launcher.adoc new file mode 100644 index 000000000000..151cfd54b99f --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/source-launcher.adoc @@ -0,0 +1,31 @@ += Source Launcher + +Starting with Java 25 it is possible to write minimal source code test programs +using the `org.junit.start` module. For example, like in a `HelloTests.java` +file reading: + +```java +import module org.junit.start; + +void main() { + JUnit.run(); +} + +@Test +void stringLength() { + Assertions.assertEquals(11, "Hello JUnit".length()); +} +``` +With all required modular JAR files available in a local `lib/` directory, the +following Java 25+ command will discover and execute tests using the JUnit Platform. +It will also print the result tree to the console. + +```shell +java --module-path lib --add-modules org.junit.start HelloTests.java +╷ +└─ JUnit Jupiter ✔ + └─ HelloTests ✔ + └─ stringLength() ✔ +``` + +Find JUnit's class API documentation here: {JUnit} diff --git a/documentation/modules/ROOT/pages/running-tests/stack-trace-pruning.adoc b/documentation/modules/ROOT/pages/running-tests/stack-trace-pruning.adoc new file mode 100644 index 000000000000..a3161def7e75 --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/stack-trace-pruning.adoc @@ -0,0 +1,13 @@ += Stack Trace Pruning + +The JUnit Platform provides built-in support for pruning stack traces produced by failing +tests. This feature is enabled by default but can be disabled by setting the +`junit.platform.stacktrace.pruning.enabled` _configuration parameter_ to `false`. + +When enabled, all calls from the `org.junit`, `jdk.internal.reflect`, and `sun.reflect` +packages are removed from the stack trace, unless the calls occur after the test itself +or any of its ancestors. For that reason, calls to `{Assertions}` or `{Assumptions}` will +never be excluded. + +In addition, all elements prior to and including the first call from the JUnit Platform +`Launcher` will be removed. diff --git a/documentation/modules/ROOT/pages/running-tests/tags.adoc b/documentation/modules/ROOT/pages/running-tests/tags.adoc new file mode 100644 index 000000000000..c99cfea4802a --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/tags.adoc @@ -0,0 +1,73 @@ += Tags + +Tags are a JUnit Platform concept for marking and filtering tests. The programming model +for adding tags to containers and tests is defined by the testing framework. For example, +in JUnit Jupiter based tests, the `@Tag` annotation (see +xref:writing-tests/tagging-and-filtering.adoc[]) should be used. For JUnit 4 based tests, the +Vintage engine maps `@Category` annotations to tags (see +xref:migrating-from-junit4.adoc#categories-support[Categories Support]). Other testing frameworks may define their +own annotation or other means for users to specify tags. + +[[syntax-rules]] +== Syntax Rules for Tags + +Regardless how a tag is specified, the JUnit Platform enforces the following rules: + +* A tag must not be `null` or _blank_. +* A _stripped_ tag must not contain whitespace. +* A _stripped_ tag must not contain ISO control characters. +* A _stripped_ tag must not contain any of the following _reserved characters_. +- `,`: _comma_ +- `(`: _left parenthesis_ +- `)`: _right parenthesis_ +- `&`: _ampersand_ +- `|`: _vertical bar_ +- `!`: _exclamation point_ + +NOTE: In the above context, "stripped" means that leading and trailing whitespace +characters have been removed using `java.lang.String.strip()`. + +[[expressions]] +== Tag Expressions + +Tag expressions are boolean expressions with the operators `!`, `&` and `|`. In addition, +`(` and `)` can be used to adjust for operator precedence. + +Two special expressions are supported, `any()` and `none()`, which select all tests _with_ +any tags at all, and all tests _without_ any tags, respectively. +These special expressions may be combined with other expressions just like normal tags. + +.Operators (in descending order of precedence) +|=== +| Operator | Meaning | Associativity + +| `!` | not | right +| `&` | and | left +| `\|` | or | left +|=== + +If you are tagging your tests across multiple dimensions, tag expressions help you to +select which tests to execute. When tagging by test type (e.g., _micro_, _integration_, +_end-to-end_) and feature (e.g., *product*, *catalog*, *shipping*), the following tag +expressions can be useful. + +[%header,cols="40,60"] +|=== +| Tag Expression +| Selection + +| `+++product+++` +| all tests for *product* + +| `+++catalog \| shipping+++` +| all tests for *catalog* plus all tests for *shipping* + +| `+++catalog & shipping+++` +| all tests for the intersection between *catalog* and *shipping* + +| `+++product & !end-to-end+++` +| all tests for *product*, but not the _end-to-end_ tests + +| `+++(micro \| integration) & (product \| shipping)+++` +| all _micro_ or _integration_ tests for *product* or *shipping* +|=== diff --git a/documentation/modules/ROOT/pages/running-tests/using-listeners-and-interceptors.adoc b/documentation/modules/ROOT/pages/running-tests/using-listeners-and-interceptors.adoc new file mode 100644 index 000000000000..e69ad7165e4b --- /dev/null +++ b/documentation/modules/ROOT/pages/running-tests/using-listeners-and-interceptors.adoc @@ -0,0 +1,87 @@ += Using Listeners and Interceptors + +The JUnit Platform provides the following listener APIs that allow JUnit, third parties, +and custom user code to react to events fired at various points during the discovery and +execution of a `TestPlan`. + +* `{LauncherSessionListener}`: receives events when a `{LauncherSession}` is opened and + closed. +* `{LauncherInterceptor}`: intercepts test discovery and execution in the context of a + `LauncherSession`. +* `{LauncherDiscoveryListener}`: receives events that occur during test discovery. +* `{TestExecutionListener}`: receives events that occur during test execution. + +The `LauncherSessionListener` API is typically implemented by build tools or IDEs and +registered automatically for you in order to support some feature of the build tool or IDE. + +The `LauncherDiscoveryListener` and `TestExecutionListener` APIs are often implemented in +order to produce some form of report or to display a graphical representation of the test +plan in an IDE. Such listeners may be implemented and automatically registered by a build +tool or IDE, or they may be included in a third-party library – potentially registered +for you automatically. You can also implement and register your own listeners. + +For details on registering and configuring listeners, see the following sections of this +guide. + +* xref:advanced-topics/launcher-api.adoc#launcher-session-listeners-custom[Registering a LauncherSessionListener] +* xref:advanced-topics/launcher-api.adoc#launcher-interceptors-custom[Registering a LauncherInterceptor] +* xref:advanced-topics/launcher-api.adoc#launcher-discovery-listeners-custom[Registering a LauncherDiscoveryListener] +* xref:advanced-topics/launcher-api.adoc#listeners-custom[Registering a TestExecutionListener] +* xref:advanced-topics/launcher-api.adoc#listeners-config[Configuring a TestExecutionListener] +* xref:advanced-topics/launcher-api.adoc#listeners-custom-deactivation[Deactivating a TestExecutionListener] + +The JUnit Platform provides the following listeners which you may wish to use with your +test suite. + +xref:advanced-topics/junit-platform-reporting.adoc[] :: + `{LegacyXmlReportGeneratingListener}` can be used via the + xref:running-tests/console-launcher.adoc[] or registered manually to generate XML reports + compatible with the de facto standard for JUnit 4 based test reports. ++ +`{OpenTestReportGeneratingListener}` generates an XML report in the event-based format +specified by {OpenTestReporting}. It is auto-registered and can be enabled and +configured via xref:running-tests/configuration-parameters.adoc[]. ++ +See xref:advanced-topics/junit-platform-reporting.adoc[] for details. + +<> :: + `FlightRecordingExecutionListener` and `FlightRecordingDiscoveryListener` that generate + Java Flight Recorder events during test discovery and execution. + +`{LoggingListener}` :: + `TestExecutionListener` for logging informational messages for all events via a + `BiConsumer` that consumes `Throwable` and `Supplier`. + +`{SummaryGeneratingListener}` :: + `TestExecutionListener` that generates a summary of the test execution which can be + printed via a `PrintWriter`. + +`{UniqueIdTrackingListener}` :: + `TestExecutionListener` that that tracks the unique IDs of all tests that were skipped + or executed during the execution of the `TestPlan` and generates a file containing the + unique IDs once execution of the `TestPlan` has finished. + +[[recorder]] +== Flight Recorder Support + +The JUnit Platform provides opt-in support for generating Flight Recorder events. +https://openjdk.java.net/jeps/328[JEP 328] describes the Java Flight Recorder (JFR) as +follows. + +> Flight Recorder records events originating from applications, the JVM, and the OS. +Events are stored in a single file that can be attached to bug reports and examined by +support engineers, allowing after-the-fact analysis of issues in the period leading up +to a problem. + +In order to record Flight Recorder events generated while running tests, you need to +start flight recording when launching a test suite via the following java command line +option. + + -XX:StartFlightRecording:filename=... + +Please consult the manual of your build tool for the appropriate commands. + +To analyze the recorded events, use the +https://docs.oracle.com/en/java/javase/17/docs/specs/man/jfr.html[jfr] +command line tool shipped with recent JDKs or open the recording file with +https://jdk.java.net/jmc/[JDK Mission Control]. diff --git a/documentation/modules/ROOT/pages/writing-tests/annotations.adoc b/documentation/modules/ROOT/pages/writing-tests/annotations.adoc new file mode 100644 index 000000000000..33d1c1e9f7a7 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/annotations.adoc @@ -0,0 +1,172 @@ += Annotations + +JUnit Jupiter supports the following annotations for configuring tests and extending the +framework. + +Unless otherwise stated, all core annotations are located in the `{api-package}` package +in the `junit-jupiter-api` module. + +`*@Test*`:: Denotes that a method is a test method. Unlike JUnit 4's `@Test` annotation, +this annotation does not declare any attributes, since test extensions in JUnit Jupiter +operate based on their own dedicated annotations. Such methods are inherited unless they +are overridden. + +`*@ParameterizedTest*`:: Denotes that a method is a +xref:writing-tests/parameterized-classes-and-tests.adoc[parameterized test]. Such methods are inherited +unless they are overridden. + +`*@RepeatedTest*`:: Denotes that a method is a test template for a +xref:writing-tests/repeated-tests.adoc[repeated test]. Such methods are inherited unless they +are overridden. + +`*@TestFactory*`:: Denotes that a method is a test factory for +xref:writing-tests/dynamic-tests.adoc[dynamic tests]. Such methods are inherited unless they are +overridden. + +`*@TestTemplate*`:: Denotes that a method is a +xref:writing-tests/test-templates.adoc[template for a test case] designed to be invoked multiple +times depending on the number of invocation contexts returned by the registered +xref:extensions/providing-invocation-contexts-for-test-templates.adoc[providers]. Such methods are inherited unless they are +overridden. + +`*@TestClassOrder*`:: Used to configure the +xref:writing-tests/test-execution-order.adoc#classes[test class execution order] for `@Nested` +test classes in the annotated test class. Such annotations are inherited. + +`*@TestMethodOrder*`:: Used to configure the +xref:writing-tests/test-execution-order.adoc#methods[test method execution order] for the +annotated test class; similar to JUnit 4's `@FixMethodOrder`. Such annotations are +inherited. + +`*@TestInstance*`:: Used to configure the +xref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] for the annotated test +class. Such annotations are inherited. + +`*@DisplayName*`:: Declares a custom xref:writing-tests/display-names.adoc[display name] for the +test class or test method. Such annotations are not inherited. + +`*@DisplayNameGeneration*`:: Declares a custom +xref:writing-tests/display-names.adoc#generator[display name generator] for the test class. Such +annotations are inherited. + +`*@BeforeEach*`:: Denotes that the annotated method should be executed _before_ *each* +`@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current +class; analogous to JUnit 4's `@Before`. Such methods are inherited unless they are +overridden. + +`*@AfterEach*`:: Denotes that the annotated method should be executed _after_ *each* +`@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current +class; analogous to JUnit 4's `@After`. Such methods are inherited unless they are +overridden. + +`*@BeforeAll*`:: Denotes that the annotated method should be executed _before_ *all* +`@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current +top-level or `@Nested` test class; analogous to JUnit 4's `@BeforeClass`. Such methods are +inherited unless they are overridden and must be `static` unless the "per-class" +xref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] is used. + +`*@AfterAll*`:: Denotes that the annotated method should be executed _after_ *all* +`@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current +top-level or `@Nested` test class; analogous to JUnit 4's `@AfterClass`. Such methods are +inherited unless they are overridden and must be `static` unless the "per-class" +xref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] is used. + +`*@ParameterizedClass*`:: Denotes that the annotated class is a +xref:writing-tests/parameterized-classes-and-tests.adoc[parameterized class]. Such annotations are +inherited. + +`*@BeforeParameterizedClassInvocation*`:: Denotes that the annotated method should be +executed once _before_ each invocation of a +xref:writing-tests/parameterized-classes-and-tests.adoc[parameterized class]. Such methods are inherited +unless they are overridden. + +`*@AfterParameterizedClassInvocation*`:: Denotes that the annotated method should be +executed once _after_ each invocation of a +xref:writing-tests/parameterized-classes-and-tests.adoc[parameterized class]. Such methods are inherited +unless they are overridden. + +`*@ClassTemplate*`:: Denotes that the annotated class is a +xref:writing-tests/class-templates.adoc[template for a test class] designed to be executed +multiple times depending on the number of invocation contexts returned by the registered +xref:extensions/providing-invocation-contexts-for-class-templates.adoc[providers]. Such annotations are inherited. + +`*@Nested*`:: Denotes that the annotated class is a non-static +xref:writing-tests/nested-tests.adoc[nested test class]. Such annotations are not inherited. + +`*@Tag*`:: Used to declare +xref:writing-tests/tagging-and-filtering.adoc[tags for filtering tests], either at the class or +method level; analogous to test groups in TestNG or Categories in JUnit 4. Such +annotations are inherited at the class level but not at the method level. + +`*@Disabled*`:: Used to xref:writing-tests/disabling-tests.adoc[disable] a test class or test method; +analogous to JUnit 4's `@Ignore`. Such annotations are not inherited. + +`*@AutoClose*`:: Denotes that the annotated field represents a resource that will be +xref:writing-tests/built-in-extensions.adoc#AutoClose[automatically closed] after test +execution. Such fields are inherited. + +`*@Timeout*`:: Used to fail a test, test factory, test template, or lifecycle method if +its execution exceeds a given duration. Such annotations are inherited. + +`*@TempDir*`:: Used to supply a +xref:writing-tests/built-in-extensions.adoc#TempDirectory[temporary directory] via field +injection or parameter injection in a test class constructor, lifecycle method, or test +method; located in the `org.junit.jupiter.api.io` package. Such fields are inherited. + +`*@ExtendWith*`:: Used to +xref:extensions/registering-extensions.adoc#registration-declarative[register extensions declaratively]. Such +annotations are inherited. + +`*@RegisterExtension*`:: Used to +xref:extensions/registering-extensions.adoc#registration-programmatic[register extensions programmatically] via fields. +Such fields are inherited. + +WARNING: Some annotations may currently be _experimental_. Consult the table in +xref:api-evolution.adoc#experimental-apis[Experimental APIs] for details. + +[[annotations]] +== Meta-Annotations and Composed Annotations + +JUnit Jupiter annotations can be used as _meta-annotations_. That means that you can +define your own _composed annotation_ that will automatically _inherit_ the semantics of +its meta-annotations. + +For example, instead of copying and pasting `@Tag("fast")` throughout your code base (see +xref:writing-tests/tagging-and-filtering.adoc[]), you can create a custom _composed annotation_ +named `@Fast` as follows. `@Fast` can then be used as a drop-in replacement for +`@Tag("fast")`. + +[source,java,indent=0] +---- +include::example$java/example/Fast.java[tags=user_guide] +---- + +The following `@Test` method demonstrates usage of the `@Fast` annotation. + +[source,java,indent=0] +---- +@Fast +@Test +void myFastTest() { + // ... +} +---- + +You can even take that one step further by introducing a custom `@FastTest` annotation +that can be used as a drop-in replacement for `@Tag("fast")` _and_ `@Test`. + +[source,java,indent=0] +---- +include::example$java/example/FastTest.java[tags=user_guide] +---- + +JUnit automatically recognizes the following as a `@Test` method that is tagged with +"fast". + +[source,java,indent=0] +---- +@FastTest +void myFastTest() { + // ... +} +---- diff --git a/documentation/modules/ROOT/pages/writing-tests/assertions.adoc b/documentation/modules/ROOT/pages/writing-tests/assertions.adoc new file mode 100644 index 000000000000..71a36ee2c467 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/assertions.adoc @@ -0,0 +1,97 @@ += Assertions + +JUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds a few +that lend themselves well to being used with Java lambdas. All JUnit Jupiter assertions +are `static` methods in the `{Assertions}` class. + +Assertion methods optionally accept the assertion message as their third parameter, which +can be either a `String` or a `Supplier`. + +When using a `Supplier` (e.g., a lambda expression), the message is evaluated +lazily. This can provide a performance benefit, especially if message construction is +complex or time-consuming, as it is only evaluated when the assertion fails. + +[source,java,indent=0] +---- +include::example$java/example/AssertionsDemo.java[tags=user_guide] +---- + +[[preemptive-timeouts]] +[WARNING] +.Preemptive Timeouts with `assertTimeoutPreemptively()` +==== +The various `assertTimeoutPreemptively()` methods in the `Assertions` class execute +the provided `executable` or `supplier` in a different thread than that of the calling +code. This behavior can lead to undesirable side effects if the code that is executed +within the `executable` or `supplier` relies on `java.lang.ThreadLocal` storage. + +One common example of this is the transactional testing support in the Spring Framework. +Specifically, Spring's testing support binds transaction state to the current thread (via +a `ThreadLocal`) before a test method is invoked. Consequently, if an `executable` or +`supplier` provided to `assertTimeoutPreemptively()` invokes Spring-managed components +that participate in transactions, any actions taken by those components will not be rolled +back with the test-managed transaction. On the contrary, such actions will be committed to +the persistent store (e.g., relational database) even though the test-managed transaction +is rolled back. + +Similar side effects may be encountered with other frameworks that rely on +`ThreadLocal` storage. +==== + +[[kotlin]] +== Kotlin Assertion Support + +JUnit Jupiter also comes with a few assertion methods that lend themselves well to being +used in https://kotlinlang.org/[Kotlin]. All JUnit Jupiter Kotlin assertions are top-level +functions in the `org.junit.jupiter.api` package. + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/KotlinAssertionsDemo.kt[tags=user_guide] +---- + +[[third-party]] +== Third-party Assertion Libraries + +Even though the assertion facilities provided by JUnit Jupiter are sufficient for many +testing scenarios, there are times when more power and additional functionality are +desired or required. In such cases, the JUnit team recommends the use of third-party +assertion libraries such as {AssertJ}, {Hamcrest}, {Truth}, etc. Developers are therefore +free to use the assertion library of their choice. + +For example, the following demonstrates how to use the `assertThat()` support from AssertJ +in a JUnit Jupiter test. As long as the AssertJ library has been added to the classpath, +you can statically import methods such as `assertThat()`, `assertThatException()`, etc. +from `org.assertj.core.api.Assertions` and then use them in tests like in the +`assertWithAssertJ()` method below. + +[source,java,indent=0] +---- +include::example$java/example/AssertJAssertionsDemo.java[tags=user_guide] +---- + +[TIP] +.Excluding Jupiter’s Assertions From a Project’s Classpath +==== +If you would like to enforce that all your tests use a certain third-party assertion +library instead of Jupiter's, you can set up a rule using {Checkstyle} or another static +analysis tool that fails the build if Jupiter's `Assertions` class is used. + +[source,xml] +---- + + + + + + + + + + + + + + +---- +==== diff --git a/documentation/modules/ROOT/pages/writing-tests/assumptions.adoc b/documentation/modules/ROOT/pages/writing-tests/assumptions.adoc new file mode 100644 index 000000000000..59e05de54c0a --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/assumptions.adoc @@ -0,0 +1,31 @@ += Assumptions + +Assumptions are typically used whenever it does not make sense to continue execution of a +given test — for example, if the test depends on something that does not exist in the +current runtime environment. + +* When an assumption is valid, the assumption method does not throw an exception, and + execution of the test continues as usual. +* When an assumption is invalid, the assumption method throws an exception of type + `org.opentest4j.TestAbortedException` to signal that the test should be aborted instead + of marked as a failure. + +JUnit Jupiter comes with a subset of the _assumption_ methods that JUnit 4 provides and +adds a few that lend themselves well to being used with Java lambda expressions and method +references. + +All JUnit Jupiter assumptions are static methods in the `{Assumptions}` class. + +[source,java,indent=0] +---- +include::example$java/example/AssumptionsDemo.java[tags=user_guide] +---- + +NOTE: It is also possible to use methods from JUnit 4's `org.junit.Assume` class for +assumptions. Specifically, JUnit Jupiter supports JUnit 4's `AssumptionViolatedException` +to signal that a test should be aborted instead of marked as a failure. + +TIP: If you use AssertJ for assertions, you may also wish to use AssertJ for assumptions. +To do so, you can statically import the `assumeThat()` method from +`org.assertj.core.api.Assumptions` and then use AssertJ's fluent API to specify your +assumptions. diff --git a/documentation/modules/ROOT/pages/writing-tests/built-in-extensions.adoc b/documentation/modules/ROOT/pages/writing-tests/built-in-extensions.adoc new file mode 100644 index 000000000000..3d5fa42d45c5 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/built-in-extensions.adoc @@ -0,0 +1,294 @@ += Built-in Extensions + +While the JUnit team encourages reusable extensions to be packaged and maintained in +separate libraries, JUnit Jupiter includes a few user-facing extension implementations +that are considered so generally useful that users shouldn't have to add another +dependency. + +[[TempDirectory]] +== The @TempDir Extension + +The built-in `{TempDirectory}` extension is used to create and clean up a temporary +directory for an individual test or all tests in a test class. It is registered by +default. To use it, annotate a non-final, unassigned field of type `java.nio.file.Path` or +`java.io.File` with `{TempDir}` or add a parameter of type `java.nio.file.Path` or +`java.io.File` annotated with `@TempDir` to a test class constructor, lifecycle method, or +test method. + +For example, the following test declares a parameter annotated with `@TempDir` for a +single test method, creates and writes to a file in the temporary directory, and checks +its content. + +[source,java,indent=0] +.A test method that requires a temporary directory +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_parameter_injection] +---- + +You can inject multiple temporary directories by specifying multiple annotated parameters. + +[source,java,indent=0] +.A test method that requires multiple temporary directories +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_multiple_directories] +---- + +The following example stores a _shared_ temporary directory in a `static` field. This +allows the same `sharedTempDir` to be used in all lifecycle methods and test methods of +the test class. For better isolation, you should use an instance field or constructor +injection so that each test method uses a separate directory. + +[source,java,indent=0] +.A test class that shares a temporary directory across test methods +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_field_injection] +---- + +The `@TempDir` annotation has an optional `cleanup` attribute that can be set to either +`NEVER`, `ON_SUCCESS`, or `ALWAYS`. If the cleanup mode is set to `NEVER`, the temporary +directory will not be deleted after the test completes. If it is set to `ON_SUCCESS`, the +temporary directory will only be deleted after the test if the test completed successfully. + +The default cleanup mode is `ALWAYS`. You can use the +`junit.jupiter.tempdir.cleanup.mode.default` +xref:running-tests/configuration-parameters.adoc[configuration parameter] to override this default. + +[source,java,indent=0] +.A test class with a temporary directory that doesn't get cleaned up +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_cleanup_mode] +---- + +`@TempDir` supports the programmatic creation of temporary directories via the optional +`factory` attribute. This is typically used to gain control over the temporary directory +creation, like defining the parent directory or the file system that should be used. + +Factories can be created by implementing `TempDirFactory`. Implementations must provide a +no-args constructor and should not make any assumptions regarding when and how many times +they are instantiated, but they can assume that their `createTempDirectory(...)` and +`close()` methods will both be called once per instance, in this order, and from the same +thread. + +The default implementation available in Jupiter delegates directory creation to +`java.nio.file.Files::createTempDirectory` which uses the default file system and the +system's temporary directory as the parent directory. It passes `junit-` as the prefix +string of the generated directory name to help identify it as a created by JUnit. + +The following example defines a factory that uses the test name as the directory name +prefix instead of the `junit` constant value. + +[source,java,indent=0] +.A test class with a temporary directory having the test name as the directory name prefix +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_factory_name_prefix] +---- + +It is also possible to use an in-memory file system like `{Jimfs}` for the creation of the +temporary directory. The following example demonstrates how to achieve that. + +[source,java,indent=0] +.A test class with a temporary directory created with the Jimfs in-memory file system +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_factory_jimfs] +---- + +`@TempDir` can also be used as a xref:writing-tests/annotations.adoc#annotations[meta-annotation] to +reduce repetition. The following code listing shows how to create a custom `@JimfsTempDir` +annotation that can be used as a drop-in replacement for +`@TempDir(factory = JimfsTempDirFactory.class)`. + +[source,java,indent=0] +.A custom annotation meta-annotated with `@TempDir` +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation] +---- + +The following example demonstrates how to use the custom `@JimfsTempDir` annotation. + +[source,java,indent=0] +.A test class using the custom annotation +---- +include::example$java/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation_usage] +---- + +Meta-annotations or additional annotations on the field or parameter the `TempDir` +annotation is declared on might expose additional attributes to configure the factory. +Such annotations and related attributes can be accessed via the `AnnotatedElementContext` +parameter of the `createTempDirectory(...)` method. + +You can use the `junit.jupiter.tempdir.factory.default` xref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the fully qualified class name of the +`TempDirFactory` you would like to use by default. Just like for factories configured via +the `factory` attribute of the `@TempDir` annotation, the supplied class has to implement +the `TempDirFactory` interface. The default factory will be used for all `@TempDir` +annotations unless the `factory` attribute of the annotation specifies a different factory. + +In summary, the factory for a temporary directory is determined according to the following +precedence rules: + +1. The `factory` attribute of the `@TempDir` annotation, if present +2. The default `TempDirFactory` configured via the configuration +parameter, if present +3. Otherwise, `org.junit.jupiter.api.io.TempDirFactory$Standard` will be used. + +[[AutoClose]] +== The @AutoClose Extension + +The built-in `{AutoCloseExtension}` automatically closes resources associated with fields. +It is registered by default. To use it, annotate a field in a test class with +`{AutoClose}`. + +`@AutoClose` fields may be either `static` or non-static. If the value of an `@AutoClose` +field is `null` when it is evaluated the field will be ignored, but a warning message will +be logged to inform you. + +By default, `@AutoClose` expects the value of the annotated field to implement a `close()` +method that will be invoked to close the resource. However, developers can customize the +name of the close method via the `value` attribute. For example, `@AutoClose("shutdown")` +instructs JUnit to look for a `shutdown()` method to close the resource. + +`@AutoClose` fields are inherited from superclasses. Furthermore, `@AutoClose` fields from +subclasses will be closed before `@AutoClose` fields in superclasses. + +When multiple `@AutoClose` fields exist within a given test class, the order in which the +resources are closed depends on an algorithm that is deterministic but intentionally +nonobvious. This ensures that subsequent runs of a test suite close resources in the same +order, thereby allowing for repeatable builds. + +The `AutoCloseExtension` implements the `AfterAllCallback` and +`TestInstancePreDestroyCallback` extension APIs. Consequently, a `static` `@AutoClose` +field will be closed after all tests in the current test class have completed, effectively +after `@AfterAll` methods have executed for the test class. A non-static `@AutoClose` +field will be closed before the current test class instance is destroyed. Specifically, if +the test class is configured with `@TestInstance(Lifecycle.PER_METHOD)` semantics, a +non-static `@AutoClose` field will be closed after the execution of each test method, test +factory method, or test template method. However, if the test class is configured with +`@TestInstance(Lifecycle.PER_CLASS)` semantics, a non-static `@AutoClose` field will not +be closed until the current test class instance is no longer needed, which means after +`@AfterAll` methods and after all `static` `@AutoClose` fields have been closed. + +The following example demonstrates how to annotate an instance field with `@AutoClose` so +that the resource is automatically closed after test execution. In this example, we assume +that the default `@TestInstance(Lifecycle.PER_METHOD)` semantics apply. + +[source,java,indent=0] +.A test class using `@AutoClose` to close a resource +---- +include::example$java/example/AutoCloseDemo.java[tags=user_guide_example] +---- +<1> Annotate an instance field with `@AutoClose`. +<2> `WebClient` implements `java.lang.AutoCloseable` which defines a `close()` method that + will be invoked after each `@Test` method. + +[[DefaultLocaleAndTimeZone]] +== The @DefaultLocale and @DefaultTimeZone Extensions + +The `{DefaultLocale}` and `{DefaultTimeZone}` annotations can be used to change the values +returned from `Locale.getDefault()` and `TimeZone.getDefault()`, respectively, which are +often used implicitly when no specific locale or time zone is chosen. Both annotations +work on the test class level and on the test method level, and are inherited from +higher-level containers. After the annotated element has been executed, the initial +default value is restored. + +[[DefaultLocale]] +=== @DefaultLocale + +The default `Locale` can be specified using an +{jdk-javadoc-base-url}/java.base/java/util/Locale.html#forLanguageTag-java.lang.String-[IETF BCP 47 language tag string]. + +[source,java,indent=0] +---- +include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tags=default_locale_language] +---- + +Alternatively, the default `Locale` can be created using the following attributes from +which a {jdk-javadoc-base-url}/java.base/java/util/Locale.Builder.html[`Locale.Builder`] +can create an instance: + +* `language` or +* `language` and `country` or +* `language`, `country`, and `variant` + +NOTE: The variant needs to be a string which follows the +https://www.rfc-editor.org/rfc/rfc5646.html[IETF BCP 47 / RFC 5646] syntax + +[source,java,indent=0] +---- +include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_language_alternatives] +---- + +Mixing language tag configuration (via the annotation's `value` attributed) and +attributed-based configuration will cause an exception to be thrown. Furthermore, a +`variant` can only be specified if `country` is also specified. Otherwise, an exception +will be thrown. + +Any method-level `@DefaultLocale` configurations will override class-level configurations. + +[source,java,indent=0] +---- +include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_class_level] +---- + +NOTE: A class-level configuration means that the specified locale is set before and reset +after each individual test in the annotated class. + +If your use case is not covered, you can implement the `{LocaleProvider}` interface. + +[source,java,indent=0] +---- +include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_with_provider] +---- + +NOTE: The provider implementation must have a no-args (or the default) constructor. + +[[DefaultTimeZone]] +=== @DefaultTimeZone + +The default `TimeZone` is specified according to the +{jdk-javadoc-base-url}/java.base/java/util/TimeZone.html#getTimeZone(java.lang.String)[TimeZone.getTimeZone(String)] +method. + +[source,java,indent=0] +---- +include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_zone] +---- + +Any method level `@DefaultTimeZone` configurations will override class level configurations: + +[source,java,indent=0] +---- +include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_class_level] +---- + +NOTE: A class-level configuration means that the specified time zone is set before and +reset after each individual test in the annotated class. + +If your use case is not covered, you can implement the `{TimeZoneProvider}` interface. + +[source,java,indent=0] +---- +include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_time_zone_with_provider] +---- + +NOTE: The provider implementation must have a no-args (or the default) constructor. + +=== Thread Safety + +Since the default locale and time zone are global state, reading and writing them during +xref:writing-tests/parallel-execution.adoc[parallel test execution] can lead to unpredictable +results and flaky tests. The `@DefaultLocale` and `@DefaultTimeZone` extensions are +prepared for that and tests annotated with them will never execute in parallel (thanks to +`{ResourceLock}`) to guarantee correct test results. + +However, this does not cover all possible cases. Tested code that reads or writes default +locale and time zone _independently_ of the extensions can still run in parallel to them +and may thus behave erratically when, for example, it unexpectedly reads a locale set by +the extension in another thread. Tests that cover code that reads or writes the default +locale or time zone need to be annotated with the respective annotation: + +* `{ReadsDefaultLocale}` +* `{ReadsDefaultTimeZone}` +* `{WritesDefaultLocale}` +* `{WritesDefaultTimeZone}` + +Tests annotated in this way will never execute in parallel with tests annotated with +`@DefaultLocale` or `@DefaultTimeZone`. diff --git a/documentation/modules/ROOT/pages/writing-tests/class-templates.adoc b/documentation/modules/ROOT/pages/writing-tests/class-templates.adoc new file mode 100644 index 000000000000..b82f9f1e9353 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/class-templates.adoc @@ -0,0 +1,12 @@ += Class Templates + +A `{ClassTemplate}` is not a regular test class but rather a template for the contained +test cases. As such, it is designed to be invoked multiple times depending on invocation +contexts returned by the registered providers. Thus, it must be used in conjunction with a +registered `{ClassTemplateInvocationContextProvider}` extension. +Each invocation of a class template behaves like the execution of a regular test class +with full support for the same lifecycle callbacks and extensions. Please refer to +xref:extensions/providing-invocation-contexts-for-class-templates.adoc[] for usage examples. + +NOTE: xref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Classes] are a built-in +specialization of class templates. diff --git a/documentation/modules/ROOT/pages/writing-tests/conditional-test-execution.adoc b/documentation/modules/ROOT/pages/writing-tests/conditional-test-execution.adoc new file mode 100644 index 000000000000..712c1a06b3ff --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/conditional-test-execution.adoc @@ -0,0 +1,222 @@ += Conditional Test Execution + +The xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`] extension API in JUnit Jupiter allows +developers to either _enable_ or _disable_ a test class or test method based on certain +conditions _programmatically_. The simplest example of such a condition is the built-in +`{DisabledCondition}` which supports the `{Disabled}` annotation (see +xref:writing-tests/disabling-tests.adoc[]). + +In addition to `@Disabled`, JUnit Jupiter also supports several other annotation-based +conditions in the `org.junit.jupiter.api.condition` package that allow developers to +enable or disable test classes and test methods _declaratively_. If you wish to provide +details about why they might be disabled, every annotation associated with these built-in +conditions has a `disabledReason` attribute available for that purpose. + +When multiple `ExecutionCondition` extensions are registered, a test class or test method +is disabled as soon as one of the conditions returns _disabled_. If a test class is +disabled, all test methods within that class are automatically disabled as well. If a test +method is disabled, that prevents execution of the test method and method-level lifecycle +callbacks such as `@BeforeEach` methods, `@AfterEach` methods, and corresponding extension +APIs. However, that does not prevent the test class from being instantiated, and it does +not prevent the execution of class-level lifecycle callbacks such as `@BeforeAll` methods, +`@AfterAll` methods, and corresponding extension APIs. + +See xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`] and the following sections for +details. + +[TIP] +.Composed Annotations +==== +Note that any of the _conditional_ annotations listed in the following sections may also +be used as a meta-annotation in order to create a custom _composed annotation_. For +example, the `@TestOnMac` annotation in the +<> shows how you can +combine `@Test` and `@EnabledOnOs` in a single, reusable annotation. +==== + +[NOTE] +==== +_Conditional_ annotations in JUnit Jupiter are not `@Inherited`. Consequently, if you wish +to apply the same semantics to subclasses, each conditional annotation must be redeclared +on each subclass. +==== + +[WARNING] +==== +Unless otherwise stated, each of the _conditional_ annotations listed in the following +sections can only be declared once on a given test interface, test class, or test method. +If a conditional annotation is directly present, indirectly present, or meta-present +multiple times on a given element, only the first such annotation discovered by JUnit will +be used; any additional declarations will be silently ignored. Note, however, that each +conditional annotation may be used in conjunction with other conditional annotations in +the `org.junit.jupiter.api.condition` package. +==== + +[[os]] +== Operating System and Architecture Conditions + +A container or test may be enabled or disabled on a particular operating system, +architecture, or combination of both via the `{EnabledOnOs}` and `{DisabledOnOs}` +annotations. + +[[os-demo]] +[source,java,indent=0] +.Conditional execution based on operating system +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_os] +---- + +[[architectures-demo]] +[source,java,indent=0] +.Conditional execution based on architecture +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_architecture] +---- + +[[jre]] +== Java Runtime Environment Conditions + +A container or test may be enabled or disabled on particular versions of the Java Runtime +Environment (JRE) via the `{EnabledOnJre}` and `{DisabledOnJre}` annotations or on a +particular range of versions of the JRE via the `{EnabledForJreRange}` and +`{DisabledForJreRange}` annotations. The range effectively defaults to `JRE.JAVA_8` as the +lower bound and `JRE.OTHER` as the upper bound, which allows usage of half open ranges. + +The following listing demonstrates the use of these annotations with predefined {JRE} enum +constants. + +[source,java,indent=0] +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_jre] +---- + +Since the enum constants defined in {JRE} are static for any given JUnit release, you +might find that you need to configure a Java version that is not supported by the `JRE` +enum. For example, when JUnit Jupiter 5.12 was released the `JRE` enum defined `JAVA_25` +as the highest supported Java version. However, you may wish to run your tests against +later versions of Java. To support such use cases, you can specify arbitrary Java versions +via the `versions` attributes in `@EnabledOnJre` and `@DisabledOnJre` and via the +`minVersion` and `maxVersion` attributes in `@EnabledForJreRange` and +`@DisabledForJreRange`. + +The following listing demonstrates the use of these annotations with arbitrary Java +versions. + +[source,java,indent=0] +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_jre_arbitrary_versions] +---- + +[[native]] +== Native Image Conditions + +A container or test may be enabled or disabled within a +https://www.graalvm.org/reference-manual/native-image/[GraalVM native image] via the +`{EnabledInNativeImage}` and `{DisabledInNativeImage}` annotations. These annotations are +typically used when running tests within a native image using the Gradle and Maven +plug-ins from the GraalVM https://graalvm.github.io/native-build-tools/latest/[Native +Build Tools] project. + +[source,java,indent=0] +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_native] +---- + +[[system-properties]] +== System Property Conditions + +A container or test may be enabled or disabled based on the value of the `named` JVM +system property via the `{EnabledIfSystemProperty}` and `{DisabledIfSystemProperty}` +annotations. The value supplied via the `matches` attribute will be interpreted as a +regular expression. + +[source,java,indent=0] +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_system_property] +---- + +[TIP] +==== +`{EnabledIfSystemProperty}` and `{DisabledIfSystemProperty}` are _repeatable annotations_. +Consequently, these annotations may be declared multiple times on a test interface, test +class, or test method. Specifically, these annotations will be found if they are directly +present, indirectly present, or meta-present on a given element. +==== + +[[environment-variables]] +== Environment Variable Conditions + +A container or test may be enabled or disabled based on the value of the `named` +environment variable from the underlying operating system via the +`{EnabledIfEnvironmentVariable}` and `{DisabledIfEnvironmentVariable}` annotations. The +value supplied via the `matches` attribute will be interpreted as a regular expression. + +[source,java,indent=0] +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_environment_variable] +---- + +[TIP] +==== +`{EnabledIfEnvironmentVariable}` and `{DisabledIfEnvironmentVariable}` are _repeatable +annotations_. Consequently, these annotations may be declared multiple times on a test +interface, test class, or test method. Specifically, these annotations will be found if +they are directly present, indirectly present, or meta-present on a given element. +==== + +[[custom]] +== Custom Conditions + +As an alternative to implementing an xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`], a +container or test may be enabled or disabled based on a _condition method_ configured via +the `{EnabledIf}` and `{DisabledIf}` annotations. A condition method must have a `boolean` +return type and may accept either no arguments or a single `ExtensionContext` argument. + +The following test class demonstrates how to configure a local method named +`customCondition` via `@EnabledIf` and `@DisabledIf`. + +[source,java,indent=0] +---- +include::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_custom] +---- + +Alternatively, the condition method can be located outside the test class. In this case, +it must be referenced by its _fully qualified name_ as demonstrated in the following +example. + +[source,java,indent=0] +---- +package example; + +include::example$java/example/ExternalCustomConditionDemo.java[tags=user_guide_external_custom_condition] +---- + +[NOTE] +==== +There are several cases where a condition method would need to be `static`: + +- when `@EnabledIf` or `@DisabledIf` is used at class level +- when `@EnabledIf` or `@DisabledIf` is used on a `@ParameterizedTest` or a + `@TestTemplate` method +- when the condition method is located in an external class + +In any other case, you can use either static methods or instance methods as condition +methods. +==== + +[TIP] +==== +It is often the case that you can use an existing static method in a utility class as a +custom condition. + +For example, `java.awt.GraphicsEnvironment` provides a `public static boolean isHeadless()` +method that can be used to determine if the current environment does not support a +graphical display. Thus, if you have a test that depends on graphical support you can +disable it when such support is unavailable as follows. + +[source,java,indent=0] +---- +@DisabledIf(value = "java.awt.GraphicsEnvironment#isHeadless", + disabledReason = "headless environment") +---- +==== diff --git a/documentation/modules/ROOT/pages/writing-tests/definitions.adoc b/documentation/modules/ROOT/pages/writing-tests/definitions.adoc new file mode 100644 index 000000000000..351fe22f151d --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/definitions.adoc @@ -0,0 +1,26 @@ += Definitions + +== Platform Concepts + +Container:: +a node in the test tree that contains other containers or tests as its children (e.g. a _test class_). + +Test:: +a node in the test tree that verifies expected behavior when executed (e.g. a `@Test` method). + +== Jupiter Concepts + +Lifecycle Method:: +any method that is directly annotated or meta-annotated with +`@BeforeAll`, `@AfterAll`, `@BeforeEach`, or `@AfterEach`. + +Test Class:: +any top-level class, `static` member class, or xref:writing-tests/nested-tests.adoc[`@Nested` class] that contains at least one _test method_, i.e. a _container_. +Test classes must not be `abstract` and must have a single constructor. +Java `record` classes are supported as well. + +Test Method:: +any instance method that is directly annotated or meta-annotated with +`@Test`, `@RepeatedTest`, `@ParameterizedTest`, `@TestFactory`, or `@TestTemplate`. +With the exception of `@Test`, these create a _container_ in the test tree that groups +_tests_ or, potentially (for `@TestFactory`), other _containers_. diff --git a/documentation/modules/ROOT/pages/writing-tests/dependency-injection-for-constructors-and-methods.adoc b/documentation/modules/ROOT/pages/writing-tests/dependency-injection-for-constructors-and-methods.adoc new file mode 100644 index 000000000000..cdb18e0091ea --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/dependency-injection-for-constructors-and-methods.adoc @@ -0,0 +1,90 @@ += Dependency Injection for Constructors and Methods + +In all prior JUnit versions, test constructors or methods were not allowed to have +parameters (at least not with the standard `Runner` implementations). As one of the major +changes in JUnit Jupiter, both test constructors and methods are now permitted to have +parameters. This allows for greater flexibility and enables _Dependency Injection_ for +constructors and methods. + +`{ParameterResolver}` defines the API for test extensions that wish to _dynamically_ +resolve parameters at runtime. If a _test class_ constructor, a _test method_, or a +_lifecycle method_ (see xref:writing-tests/definitions.adoc[]) accepts a parameter, the parameter +must be resolved at runtime by a registered `ParameterResolver`. + +There are currently three built-in resolvers that are registered automatically. + +* `{TestInfoParameterResolver}`: if a constructor or method parameter is of type + `{TestInfo}`, the `TestInfoParameterResolver` will supply an instance of `TestInfo` + corresponding to the current container or test as the value for the parameter. The + `TestInfo` can then be used to retrieve information about the current container or test + such as the display name, the test class, the test method, and associated tags. The + display name is either a technical name, such as the name of the test class or test + method, or a custom name configured via `@DisplayName`. ++ +`{TestInfo}` acts as a drop-in replacement for the `TestName` rule from JUnit 4. The +following demonstrates how to have `TestInfo` injected into a `@BeforeAll` method, test +class constructor, `@BeforeEach` method, and `@Test` method. + +[source,java,indent=0] +---- +include::example$java/example/TestInfoDemo.java[tags=user_guide] +---- + +* `{RepetitionExtension}`: if a method parameter in a `@RepeatedTest`, `@BeforeEach`, or + `@AfterEach` method is of type `{RepetitionInfo}`, the `RepetitionExtension` will supply + an instance of `RepetitionInfo`. `RepetitionInfo` can then be used to retrieve + information about the current repetition, the total number of repetitions, the number of + repetitions that have failed, and the failure threshold for the corresponding + `@RepeatedTest`. Note, however, that `RepetitionExtension` is not registered outside the + context of a `@RepeatedTest`. See xref:writing-tests/repeated-tests.adoc#examples[Repeated Test Examples]. + +* `{TestReporterParameterResolver}`: if a constructor or method parameter is of type + `{TestReporter}`, the `TestReporterParameterResolver` will supply an instance of + `TestReporter`. The `TestReporter` can be used to publish additional data about the + current test run or attach files to it. The data can be consumed in a + `{TestExecutionListener}` via the `reportingEntryPublished()` or `fileEntryPublished()` + method, respectively. This allows them to be viewed in IDEs or included in reports. ++ +In JUnit Jupiter you should use `TestReporter` where you used to print information to +`stdout` or `stderr` in JUnit 4. Some IDEs print report entries to `stdout` or display +them in the user interface for test results. + +[source,java,indent=0] +---- +include::example$java/example/TestReporterDemo.java[tags=user_guide] +---- + +NOTE: Other parameter resolvers must be explicitly enabled by registering appropriate +xref:extensions/overview.adoc[extensions] via `@ExtendWith`. + +Check out the `{RandomParametersExtension}` for an example of a custom +`{ParameterResolver}`. While not intended to be production-ready, it demonstrates the +simplicity and expressiveness of both the extension model and the parameter resolution +process. `MyRandomParametersTest` demonstrates how to inject random values into `@Test` +methods. + +[source,java,indent=0] +---- +@ExtendWith(RandomParametersExtension.class) +class MyRandomParametersTest { + + @Test + void injectsInteger(@Random int i, @Random int j) { + assertNotEquals(i, j); + } + + @Test + void injectsDouble(@Random double d) { + assertEquals(0.0, d, 1.0); + } + +} +---- + +For real-world use cases, check out the source code for the `{MockitoExtension}` and the +`{SpringExtension}`. + +When the type of the parameter to inject is the only condition for your +`{ParameterResolver}`, you can use the generic `{TypeBasedParameterResolver}` base class. +The `supportsParameters` method is implemented behind the scenes and supports +parameterized types. diff --git a/documentation/modules/ROOT/pages/writing-tests/disabling-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/disabling-tests.adoc new file mode 100644 index 000000000000..5630bf30eac8 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/disabling-tests.adoc @@ -0,0 +1,44 @@ += Disabling Tests + +Entire test classes or individual test methods may be _disabled_ via the `{Disabled}` +annotation, via one of the annotations discussed in +xref:writing-tests/conditional-test-execution.adoc[], or via a custom xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`]. + +When `@Disabled` is applied at the class level, all test methods within that class are +automatically disabled as well. + +If a test method is disabled via `@Disabled`, that prevents execution of the test method +and method-level lifecycle callbacks such as `@BeforeEach` methods, `@AfterEach` methods, +and corresponding extension APIs. However, that does not prevent the test class from being +instantiated, and it does not prevent the execution of class-level lifecycle callbacks +such as `@BeforeAll` methods, `@AfterAll` methods, and corresponding extension APIs. + +Here's a `@Disabled` test class. + +[source,java,indent=0] +---- +include::example$java/example/DisabledClassDemo.java[tags=user_guide] +---- + +And here's a test class that contains a `@Disabled` test method. + +[source,java,indent=0] +---- +include::example$java/example/DisabledTestsDemo.java[tags=user_guide] +---- + +[TIP] +==== +`@Disabled` may be declared without providing a _reason_; however, the JUnit team +recommends that developers provide a short explanation for why a test class or test +method has been disabled. Consequently, the above examples both show the use of a reason +-- for example, `@Disabled("Disabled until bug #42 has been resolved")`. Some development +teams even require the presence of issue tracking numbers in the _reason_ for automated +traceability, etc. +==== + +[NOTE] +==== +`@Disabled` is not `@Inherited`. Consequently, if you wish to disable a class whose +superclass is `@Disabled`, you must redeclare `@Disabled` on the subclass. +==== diff --git a/documentation/modules/ROOT/pages/writing-tests/display-names.adoc b/documentation/modules/ROOT/pages/writing-tests/display-names.adoc new file mode 100644 index 000000000000..86aef486627d --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/display-names.adoc @@ -0,0 +1,159 @@ += Display Names + +Test classes and test methods can declare custom display names via `@DisplayName` -- with +spaces, special characters, and even emojis -- that will be displayed in test reports and +by test runners and IDEs. + +[source,java,indent=0] +---- +include::example$java/example/DisplayNameDemo.java[tags=user_guide] +---- + +[NOTE] +==== +Control characters in text-based arguments in display names for parameterized tests are +escaped by default. See xref:writing-tests/parameterized-classes-and-tests.adoc#tests-display-names-quoted-text[Quoted Text-based Arguments] +for details. + +Any remaining ISO control characters in a display name will be replaced as follows. + +[cols="25%,15%,60%"] +|=== +| Original | Replacement | Description + +| ```\r``` +| `````` +| Textual representation of a carriage return + +| ```\n``` +| `````` +| Textual representation of a line feed + +| Other control character +| ```�``` +| Unicode replacement character (U+FFFD) +|=== +==== + +[[generator]] +== Display Name Generators + +JUnit Jupiter supports custom display name generators that can be configured via the +`@DisplayNameGeneration` annotation. + +Generators can be created by implementing the `DisplayNameGenerator` API. The following +table lists the default display name generators available in Jupiter. + +[cols="20,80"] +|=== +| DisplayNameGenerator | Behavior + +| `Standard` | Matches the standard display name generation behavior in place since JUnit Jupiter was introduced. +| `Simple` | Extends the functionality of `Standard` by removing trailing parentheses for methods with no parameters. +| `ReplaceUnderscores` | Replaces underscores with spaces. +| `IndicativeSentences` | Generates complete sentences by concatenating the names of the test and the enclosing classes. +|=== + +NOTE: Values provided via `@DisplayName` annotations always take precedence over display +names generated by a `DisplayNameGenerator`. + +====== +The following example demonstrates the use of the `ReplaceUnderscores` display name +generator. + +[source,java,indent=0] +---- +include::example$java/example/DisplayNameGeneratorDemo.java[tags=user_guide_replace_underscores] +---- + +Running the above test class results in the following display names. + +``` +A year is not supported ✔ +├─ if it is zero ✔ +└─ A negative value for year is not supported by the leap year computation. ✔ + ├─ For example, year -1 is not supported. ✔ + └─ For example, year -4 is not supported. ✔ +``` +====== + +====== +With the `IndicativeSentences` display name generator, you can customize the separator and +the underlying generator by using `@IndicativeSentencesGeneration` as shown in the +following example. + +[source,java,indent=0] +---- +include::example$java/example/DisplayNameGeneratorDemo.java[tags=user_guide_indicative_sentences] +---- + +Running the above test class results in the following display names. + +``` +A year is a leap year ✔ +├─ A year is a leap year -> if it is divisible by 4 but not by 100 ✔ +└─ A year is a leap year -> if it is one of the following years ✔ + ├─ Year 2016 is a leap year. ✔ + ├─ Year 2020 is a leap year. ✔ + └─ Year 2048 is a leap year. ✔ +``` +====== + +====== +With `IndicativeSentences`, you can optionally specify custom sentence fragments via the +`@SentenceFragment` annotation as demonstrated in the following example. + +[source,java,indent=0] +---- +include::example$java/example/DisplayNameGeneratorDemo.java[tags=user_guide_custom_sentence_fragments] +---- + +Running the above test class results in the following display names. + +``` +A year is a leap year ✔ +├─ A year is a leap year, if it is divisible by 4 but not by 100 ✔ +└─ A year is a leap year, if it is one of the following years ✔ + ├─ 2016 ✔ + ├─ 2020 ✔ + └─ 2048 ✔ +``` +====== + + +[[generator-default]] +== Setting the Default Display Name Generator + +You can use the `junit.jupiter.displayname.generator.default` +xref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the fully qualified +class name of the `DisplayNameGenerator` you would like to use by default. Just like for +display name generators configured via the `@DisplayNameGeneration` annotation, the +supplied class has to implement the `DisplayNameGenerator` interface. The default display +name generator will be used for all tests unless the `@DisplayNameGeneration` annotation +is present on an enclosing test class or test interface. Values provided via +`@DisplayName` annotations always take precedence over display names generated by a +`DisplayNameGenerator`. + +For example, to use the `ReplaceUnderscores` display name generator by default, you should +set the configuration parameter to the corresponding fully qualified class name (e.g., in +`src/test/resources/junit-platform.properties`): + +[source,properties,indent=0] +---- +junit.jupiter.displayname.generator.default = \ + org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores +---- + +Similarly, you can specify the fully qualified name of any custom class that implements +`DisplayNameGenerator`. + +[[generator-precedence-rules]] +In summary, the display name for a test class or method is determined according to the +following precedence rules: + +1. value of the `@DisplayName` annotation, if present +2. by calling the `DisplayNameGenerator` specified in the `@DisplayNameGeneration` + annotation, if present +3. by calling the default `DisplayNameGenerator` configured via the configuration + parameter, if present +4. by calling `org.junit.jupiter.api.DisplayNameGenerator.Standard` diff --git a/documentation/modules/ROOT/pages/writing-tests/dynamic-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/dynamic-tests.adoc new file mode 100644 index 000000000000..7878f90d3b2f --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/dynamic-tests.adoc @@ -0,0 +1,155 @@ += Dynamic Tests + +The standard `@Test` annotation in JUnit Jupiter described in +xref:writing-tests/annotations.adoc[] is very similar to the `@Test` annotation in JUnit 4. Both +describe methods that implement test cases. These test cases are static in the sense that +they are fully specified at compile time, and their behavior cannot be changed by +anything happening at runtime. _Assumptions provide a basic form of dynamic behavior but +are intentionally rather limited in their expressiveness._ + +In addition to these standard tests a completely new kind of test programming model has +been introduced in JUnit Jupiter. This new kind of test is a _dynamic test_ which is +generated at runtime by a factory method that is annotated with `@TestFactory`. + +In contrast to `@Test` methods, a `@TestFactory` method is not itself a test case but +rather a factory for test cases. Thus, a dynamic test is the product of a factory. +Technically speaking, a `@TestFactory` method must return a single `DynamicNode` or a +_stream_ of `DynamicNode` instances or any of its subclasses. In this context, a "stream" +is anything that JUnit can reliably convert into a `Stream`, such as `Stream`, +`Collection`, `Iterator`, `Iterable`, an array of objects, or any type that provides an +`iterator(): Iterator` method (such as, for example, a `kotlin.sequences.Sequence`). + +Instantiable subclasses of `DynamicNode` are `DynamicContainer` and `DynamicTest`. +`DynamicContainer` instances are composed of a _display name_ and a list of dynamic child +nodes, enabling the creation of arbitrarily nested hierarchies of dynamic nodes. +`DynamicTest` instances will be executed lazily, enabling dynamic and even +non-deterministic generation of test cases. + +Any `Stream` returned by a `@TestFactory` will be properly closed by calling +`stream.close()`, making it safe to use a resource such as `Files.lines()`. + +As with `@Test` methods, `@TestFactory` methods must not be `private` or `static` and may +optionally declare parameters to be resolved by `ParameterResolvers`. + +A `DynamicTest` is a test case generated at runtime. It is composed of a _display name_ +and an `Executable`. `Executable` is a `@FunctionalInterface` which means that the +implementations of dynamic tests can be provided as _lambda expressions_ or _method +references_. + +.Dynamic Test Lifecycle +WARNING: The execution lifecycle of a dynamic test is quite different than it is for a +standard `@Test` case. Specifically, there are no lifecycle callbacks for individual +dynamic tests. This means that `@BeforeEach` and `@AfterEach` methods and their +corresponding extension callbacks are executed for the `@TestFactory` method but not for +each _dynamic test_. In other words, if you access fields from the test instance within a +lambda expression for a dynamic test, those fields will not be reset by callback methods +or extensions between the execution of individual dynamic tests generated by the same +`@TestFactory` method. + +[[examples]] +== Dynamic Test Examples + +The following `DynamicTestsDemo` class demonstrates several examples of test factories +and dynamic tests. + +The first method returns an invalid return type and will cause a warning to be reported by +JUnit during test discovery. Such methods are not executed. + +The next six methods demonstrate the generation of a `Collection`, `Iterable`, `Iterator`, +array, or `Stream` of `DynamicTest` instances. Most of these examples do not really +exhibit dynamic behavior but merely demonstrate the supported return types in principle. +However, `dynamicTestsFromStream()` and `dynamicTestsFromIntStream()` demonstrate how to +generate dynamic tests for a given set of strings or a range of input numbers. + +The next method is truly dynamic in nature. `generateRandomNumberOfTests()` implements an +`Iterator` that generates random numbers, a display name generator, and a test executor +and then provides all three to `DynamicTest.stream()`. Although the non-deterministic +behavior of `generateRandomNumberOfTests()` is of course in conflict with test +repeatability and should thus be used with care, it serves to demonstrate the +expressiveness and power of dynamic tests. + +The next method is similar to `generateRandomNumberOfTests()` in terms of flexibility; +however, `dynamicTestsFromStreamFactoryMethod()` generates a stream of dynamic tests from +an existing `Stream` via the `DynamicTest.stream()` factory method. + +For demonstration purposes, the `dynamicNodeSingleTest()` method generates a single +`DynamicTest` instead of a stream, and the `dynamicNodeSingleContainer()` method generates +a nested hierarchy of dynamic tests utilizing `DynamicContainer`. + +[source,java] +---- +include::example$java/example/DynamicTestsDemo.java[tags=user_guide] +---- + +[[named-support]] +== Dynamic Tests and Named + +In some cases, it can be more natural to specify inputs together with a descriptive name +using the {Named} API and the corresponding `stream()` factory methods on `DynamicTest` as +shown in the first example below. The second example takes it one step further and allows +to provide the code block that should be executed by implementing the `Executable` +interface along with `Named` via the `NamedExecutable` base class. + +[source,java] +---- +include::example$java/example/DynamicTestsNamedDemo.java[tags=user_guide] +---- + +[[uri-test-source]] +== URI Test Sources for Dynamic Tests + +The JUnit Platform provides `TestSource`, a representation of the source of a test or +container used to navigate to its location by IDEs and build tools. + +The `TestSource` for a dynamic test or dynamic container can be constructed from a +`java.net.URI` which can be supplied via the `DynamicTest.dynamicTest(String, URI, +Executable)` or `DynamicContainer.dynamicContainer(String, URI, Stream)` factory method, +respectively. The `URI` will be converted to one of the following `TestSource` +implementations. + +`ClasspathResourceSource` :: + If the `URI` contains the `classpath` scheme -- for example, + `classpath:/test/foo.xml?line=20,column=2`. + +`DirectorySource` :: + If the `URI` represents a directory present in the file system. + +`FileSource` :: + If the `URI` represents a file present in the file system. + +`MethodSource` :: + If the `URI` contains the `method` scheme and the fully qualified method name (FQMN) -- + for example, `method:org.junit.Foo#bar(java.lang.String, java.lang.String[])`. Please + refer to the Javadoc for `{DiscoverySelectors}.{DiscoverySelectors_selectMethod}` for the + supported formats for a FQMN. + +`ClassSource` :: + If the `URI` contains the `class` scheme and the fully qualified class name -- + for example, `class:org.junit.Foo?line=42`. + +`UriSource` :: + If none of the above `TestSource` implementations are applicable. + +[[parallel-execution]] +== Parallel Execution + +Dynamic tests and containers support +xref:writing-tests/parallel-execution.adoc[parallel execution]. You can configure their +`ExecutionMode` by using the `dynamicTest(Consumer)` and `dynamicContainer(Consumer)` +factory methods as illustrated by the following example. + +[source,java,indent=0] +---- +include::example$java/example/DynamicTestsDemo.java[tags=execution_mode] +---- + +Executing the above test factory method results in the following test tree and execution +modes: + +* dynamicTestsWithConfiguredExecutionMode() -- `CONCURRENT` (from `@Execution` annotation) +** Container A -- `CONCURRENT` (from `@Execution` annotation) +*** not null -- `SAME_THREAD` (from `executionMode(...)` call) +*** properties -- `CONCURRENT` (from `@Execution` annotation) +**** length > 0 -- `CONCURRENT` (from `executionMode(...)` call) +**** not empty -- `SAME_THREAD` (from `childExecutionMode(...)` call) +** ... (same for "Container B" and "Container C") diff --git a/documentation/modules/ROOT/pages/writing-tests/exception-handling.adoc b/documentation/modules/ROOT/pages/writing-tests/exception-handling.adoc new file mode 100644 index 000000000000..2e1d6501b26a --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/exception-handling.adoc @@ -0,0 +1,119 @@ += Exception Handling + +JUnit Jupiter provides robust support for handling test exceptions. This includes the +built-in mechanisms for managing test failures due to exceptions, the role of exceptions +in implementing assertions and assumptions, and how to specifically assert non-throwing +conditions in code. + +[[uncaught]] +== Uncaught Exceptions + +In JUnit Jupiter, if an exception is thrown from a test method, a lifecycle method, or an +extension and not caught within that test method, lifecycle method, or extension, the +framework will mark the test or test class as failed. + +[TIP] +==== +Failed assumptions deviate from this general rule. + +In contrast to failed assertions, failed assumptions do not result in a test failure; +rather, a failed assumption results in a test being aborted. + +See xref:writing-tests/assumptions.adoc[] for further details and examples. +==== + +In the following example, the `failsDueToUncaughtException()` method throws an +`ArithmeticException`. Since the exception is not caught within the test method, JUnit +Jupiter will mark the test as failed. + +[source,java,indent=0] +---- +include::example$java/example/exception/UncaughtExceptionHandlingDemo.java[tags=user_guide] +---- + +NOTE: It's important to note that specifying a `throws` clause in the test method has +no effect on the outcome of the test. JUnit Jupiter does not interpret a `throws` clause +as an expectation or assertion about what exceptions the test method should throw. A test +fails only if an exception is thrown unexpectedly or if an assertion fails. + +[[failed-assertions]] +== Failed Assertions + +Assertions in JUnit Jupiter are implemented using exceptions. The framework provides a set +of assertion methods in the `org.junit.jupiter.api.Assertions` class, which throw +`AssertionError` when an assertion fails. This mechanism is a core aspect of how JUnit +handles assertion failures as exceptions. See the xref:writing-tests/assertions.adoc[] section for +further information about JUnit Jupiter's assertion support. + +NOTE: Third-party assertion libraries may choose to throw an `AssertionError` to signal a +failed assertion; however, they may also choose to throw different types of exceptions to +signal failures. See also: xref:writing-tests/assertions.adoc#third-party[Third-party Assertion Libraries]. + +TIP: JUnit Jupiter itself does not differentiate between failed assertions +(`AssertionError`) and other types of exceptions. All uncaught exceptions lead to a test +failure. However, Integrated Development Environments (IDEs) and other tools may +distinguish between these two types of failures by checking whether the thrown exception +is an instance of `AssertionError`. + +In the following example, the `failsDueToUncaughtAssertionError()` method throws an +`AssertionError`. Since the exception is not caught within the test method, JUnit Jupiter +will mark the test as failed. + +[source,java,indent=0] +---- +include::example$java/example/exception/FailedAssertionDemo.java[tags=user_guide] +---- + +[[expected]] +== Asserting Expected Exceptions + +JUnit Jupiter offers specialized assertions for testing that specific exceptions are +thrown under expected conditions. The `assertThrows()` and `assertThrowsExactly()` +assertions are critical tools for validating that your code responds correctly to error +conditions by throwing the appropriate exceptions. + +[[expected-assertThrows]] +=== Using `assertThrows()` + +The `assertThrows()` method is used to verify that a particular type of exception is +thrown during the execution of a provided executable block. It not only checks for the +type of the thrown exception but also its subclasses, making it suitable for more +generalized exception handling tests. The `assertThrows()` assertion method returns the +thrown exception object to allow performing additional assertions on it. + +[source,java,indent=0] +---- +include::example$java/example/exception/ExceptionAssertionDemo.java[tags=user_guide] +---- + +[[expected-assertThrowsExactly]] +=== Using `assertThrowsExactly()` + +The `assertThrowsExactly()` method is used when you need to assert that the exception +thrown is exactly of a specific type, not allowing for subclasses of the expected +exception type. This is useful when precise exception handling behavior needs to be +validated. Similar to `assertThrows()`, the `assertThrowsExactly()` assertion method also +returns the thrown exception object to allow performing additional assertions on it. + +[source,java,indent=0] +---- +include::example$java/example/exception/ExceptionAssertionExactDemo.java[tags=user_guide] +---- + +[[not-expected]] +== Asserting That no Exception is Expected + +Although any exception thrown from a test method will cause the test to fail, there are +certain use cases where it can be beneficial to explicitly assert that an exception is +_not_ thrown for a given code block within a test method. The `assertDoesNotThrow()` +assertion can be used when you want to verify that a particular piece of code does not +throw any exceptions. + +[source,java,indent=0] +---- +include::example$java/example/exception/AssertDoesNotThrowExceptionDemo.java[tags=user_guide] +---- + +NOTE: Third-party assertion libraries often provide similar support. For example, AssertJ +has `assertThatNoException().isThrownBy(() -> ...)`. See also: +xref:writing-tests/assertions.adoc#third-party[Third-party Assertion Libraries]. diff --git a/documentation/modules/ROOT/pages/writing-tests/intro.adoc b/documentation/modules/ROOT/pages/writing-tests/intro.adoc new file mode 100644 index 000000000000..6f7155423113 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/intro.adoc @@ -0,0 +1,11 @@ += Writing Tests + +The following example provides a glimpse at the minimum requirements for writing a test in +JUnit Jupiter. Subsequent sections of this chapter will provide further details on all +available features. + +[source,java,indent=0] +.A first test case +---- +include::example$java/example/MyFirstJUnitJupiterTests.java[tags=user_guide] +---- diff --git a/documentation/modules/ROOT/pages/writing-tests/nested-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/nested-tests.adoc new file mode 100644 index 000000000000..ce88abcd2185 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/nested-tests.adoc @@ -0,0 +1,71 @@ += Nested Tests + +`@Nested` tests give the test writer more capabilities to express the relationship among +several groups of tests. Such nested tests make use of Java's nested classes and +facilitate hierarchical thinking about the test structure. Here's an elaborate example, +both as source code and as a screenshot of the execution within an IDE. + +[source,java,indent=0] +.Nested test suite for testing a stack +---- +include::example$java/example/TestingAStackDemo.java[tags=user_guide] +---- + +When executing this example in an IDE, the test execution tree in the GUI will look +similar to the following image. + +image::writing-tests_nested_test_ide.png[caption='',title='Executing a nested test in an IDE'] + +In this example, preconditions from outer tests are used in inner tests by defining +hierarchical lifecycle methods for the setup code. For example, `createNewStack()` is a +`@BeforeEach` lifecycle method that is used in the test class in which it is defined and +in all levels in the nesting tree below the class in which it is defined. + +The fact that setup code from outer tests is run before inner tests are executed gives you +the ability to run all tests independently. You can even run inner tests alone without +running the outer tests, because the setup code from the outer tests is always executed. + +NOTE: _Only non-static nested classes_ (i.e. _inner classes_) can serve as `@Nested` test +classes. Nesting can be arbitrarily deep, and those inner classes are subject to full +lifecycle support, including `@BeforeAll` and `@AfterAll` methods on each level. + +[[interoperability]] +== Interoperability + +`@Nested` may be combined with +xref:writing-tests/parameterized-classes-and-tests.adoc[`@ParameterizedClass`] in which case the nested test +class is parameterized. + +The following example illustrates how to combine `@Nested` with `@ParameterizedClass` and +`@ParameterizedTest`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedClassDemo.java[tags=nested] +---- + +Executing the above test class yields the following output: + +.... +FruitTests ✔ +├─ [1] fruit = "apple" ✔ +│ └─ QuantityTests ✔ +│ ├─ [1] quantity = 23 ✔ +│ │ └─ test(Duration) ✔ +│ │ ├─ [1] duration = "PT1H" ✔ +│ │ └─ [2] duration = "PT2H" ✔ +│ └─ [2] quantity = 42 ✔ +│ └─ test(Duration) ✔ +│ ├─ [1] duration = "PT1H" ✔ +│ └─ [2] duration = "PT2H" ✔ +└─ [2] fruit = "banana" ✔ + └─ QuantityTests ✔ + ├─ [1] quantity = 23 ✔ + │ └─ test(Duration) ✔ + │ ├─ [1] duration = "PT1H" ✔ + │ └─ [2] duration = "PT2H" ✔ + └─ [2] quantity = 42 ✔ + └─ test(Duration) ✔ + ├─ [1] duration = "PT1H" ✔ + └─ [2] duration = "PT2H" ✔ +.... diff --git a/documentation/modules/ROOT/pages/writing-tests/parallel-execution.adoc b/documentation/modules/ROOT/pages/writing-tests/parallel-execution.adoc new file mode 100644 index 000000000000..60056b77c543 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/parallel-execution.adoc @@ -0,0 +1,351 @@ += Parallel Execution + +By default, JUnit Jupiter tests are run sequentially in a single thread; however, running +tests in parallel -- for example, to speed up execution -- is available as an opt-in +feature. To enable parallel execution, set the `junit.jupiter.execution.parallel.enabled` +configuration parameter to `true` -- for example, in `junit-platform.properties` (see +xref:running-tests/configuration-parameters.adoc[] for other options). + +Please note that enabling this property is only the first step required to execute tests +in parallel. If enabled, test classes and methods will still be executed sequentially by +default. Whether or not a node in the test tree is executed concurrently is controlled by +its execution mode. The following two modes are available. + +`SAME_THREAD`:: + Force execution in the same thread used by the parent. For example, when used on a test + method, the test method will be executed in the same thread as any `@BeforeAll` or + `@AfterAll` methods of the containing test class. + +`CONCURRENT`:: + Execute concurrently unless a resource lock forces execution in the same thread. + +By default, nodes in the test tree use the `SAME_THREAD` execution mode. You can change +the default by setting the `junit.jupiter.execution.parallel.mode.default` configuration +parameter. Alternatively, you can use the `{Execution}` annotation to change the +execution mode for the annotated element and its subelements (if any) which allows you to +activate parallel execution for individual test classes, one by one. + +[source,properties] +.Configuration parameters to execute all tests in parallel +---- +junit.jupiter.execution.parallel.enabled = true +junit.jupiter.execution.parallel.mode.default = concurrent +---- + +The default execution mode is applied to all nodes of the test tree with a few notable +exceptions, namely test classes that use the `Lifecycle.PER_CLASS` mode or a +`{MethodOrderer}`. In the former case, test authors have to ensure that the test class is +thread-safe; in the latter, concurrent execution might conflict with the configured +execution order. Thus, in both cases, test methods in such test classes are only executed +concurrently if the `@Execution(CONCURRENT)` annotation is present on the test class or +method. + +You can use the `@Execution` annotation to explicitly configure the execution mode for a +test class or method: + +[source,java] +---- +include::example$java/example/ExplicitExecutionModeDemo.java[tags=user_guide] +---- + +This allows test classes or methods to opt in or out of concurrent execution regardless of +the globally configured default. + +When parallel execution is enabled and a default `{ClassOrderer}` is registered (see +xref:writing-tests/test-execution-order.adoc#classes[Class Order] for details), top-level test classes will +initially be sorted accordingly and scheduled in that order. However, they are not +guaranteed to be started in exactly that order since the threads they are executed on are +not controlled directly by JUnit. + +All nodes of the test tree that are configured with the `CONCURRENT` execution mode will +be executed fully in parallel according to the provided +<> while observing the +declarative <> +mechanism. Please note that xref:running-tests/capturing-standard-output-error.adoc[] needs to be enabled +separately. + +In addition, you can configure the default execution mode for top-level classes by setting +the `junit.jupiter.execution.parallel.mode.classes.default` configuration parameter. By +combining both configuration parameters, you can configure classes to run in parallel but +their methods in the same thread: + +[source,properties] +.Configuration parameters to execute top-level classes in parallel but methods in same thread +---- +junit.jupiter.execution.parallel.enabled = true +junit.jupiter.execution.parallel.mode.default = same_thread +junit.jupiter.execution.parallel.mode.classes.default = concurrent +---- + +The opposite combination will run all methods within one class in parallel, but top-level +classes will run sequentially: + +[source,properties] +.Configuration parameters to execute top-level classes sequentially but their methods in parallel +---- +junit.jupiter.execution.parallel.enabled = true +junit.jupiter.execution.parallel.mode.default = concurrent +junit.jupiter.execution.parallel.mode.classes.default = same_thread +---- + +The following diagram illustrates how the execution of two top-level test classes `A` and +`B` with two test methods per class behaves for all four combinations of +`junit.jupiter.execution.parallel.mode.default` and +`junit.jupiter.execution.parallel.mode.classes.default` (see labels in first column). + +//// +Source: https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqFlE1u2zAQha9CEChio7IQKfVGXfUH_QEatICyKAIBwYQaW0QkUiDHhV3X2x4gvWFPUlKUbTmpEq2kN2-GHx403HKhS-QZn81mhSqlbWvYXDopY0I3LQgqVFcq1BIUuS_mnhIIP2jTALHvQYG1tL3ywgaJpLj7rAjND6hZsteoRvb39x9GlUEoLfvltMZL9_4M77EoSGrFJhYavAm-iA0-psH3Jia0lEymLANrk4idR_tjQintS2nEYOE4WLClwfP22H7b6QeP818MPWnvOcwJ_ldPAwutxMoYVPQ_XjHOKwa8YoT3tP0EUwww-_YHmEey52IV47EKH8dDhEAnBmmKR4mnvScdeNLnMJ8MU4yHKcQ45XiGgy4e8Qbdby1LtyNbby04VdhgwTP3qnBFBuqCR6EUdsSVtmFqwWtc0DcoS6mWXk_TebQv3YL5CK1Xk_ODuDSy_CIV5gRm2DiwuL5PKJdVd9DFUV9oRbn82aElc6_uogHxuzwP0DGBvbvCtcs17tO-6vZyy_yI2QIaWW8ydva1RcVyUPbsdahYNz1L5u2a7VjsSVnst5yRG-a6--sjU1rhqSNTVM1EJetykqqXyfSRueCF2rmwYUU63yjBMzIrjPiq9XfNewlLAw3PFlBbp2IpSZvLcHN1F1jEW1DXWu89u3-YPX1X + +--- +displayMode: compact +--- + +gantt + dateFormat X + axisFormat %s + tickInterval 1 + title ↓ threads | time → + + section (same_thread, same_thread) + A.test1() :ass1, 0, 1 + A.test2() :ass2, after ass1, 2 + B.test1() :bss1, after ass2, 3 + B.test2() :bss2, after bss1, 4 + + section (same_thread, concurrent) + A.test1() :asc1, 0, 1 + A.test2() :asc2, after asc1, 2 + B.test1() :bsc1, 0, 1 + B.test2() :bsc2, after bsc1, 2 + + section (concurrent, same_thread) + A.test1() :acs1, 0, 1 + A.test2() :acs2, 0, 1 + B.test1() :bcs1, after acs1, 2 + B.test2() :bcs2, after acs2, 2 + + section (concurrent, concurrent) + A.test1() :acc1, 0, 1 + A.test2() :acc2, 0, 1 + B.test1() :bcc1, 0, 1 + B.test2() :bcc2, 0, 1 + +//// +image::writing-tests_execution_mode.svg[caption='',title='Default execution mode configuration combinations'] + +If the `junit.jupiter.execution.parallel.mode.classes.default` configuration parameter is +not explicitly set, the value for `junit.jupiter.execution.parallel.mode.default` will be +used instead. + +[[config]] +== Configuration + +[[config-executor-service]] +=== Executor Service + +If parallel execution is enabled, a thread pool is used behind the scenes to execute tests +concurrently. You can configure which implementation of `HierarchicalTestExecutorService` +is used be setting the `junit.jupiter.execution.parallel.config.executor-service` +configuration parameter to one of the following options: + +`fork_join_pool` (default):: +Use an executor service that is backed by a `ForkJoinPool` from the JDK. This will cause +tests to be executed in a `ForkJoinWorkerThread`. In some cases, usages of +`ForkJoinPool` in test or production code or calls to blocking JDK APIs may cause the +number of concurrently executing tests to increase. To avoid this situation, please use +`worker_thread_pool`. + +`worker_thread_pool` (experimental):: +Use an executor service that is backed by a regular thread pool and does not create +additional threads if test or production code uses `ForkJoinPool` or calls a blocking +API in the JDK. + +WARNING: Using `worker_thread_pool` is currently an _experimental_ feature. You're invited +to give it a try and provide feedback to the JUnit team so they can improve and eventually +xref:api-evolution.adoc[promote] this feature. + +[[config-strategies]] +=== Strategies + +Properties such as the desired parallelism and the maximum pool size can be configured +using a `{ParallelExecutionConfigurationStrategy}`. The JUnit Platform provides two +implementations out of the box: `dynamic` and `fixed`. Alternatively, you may implement a +`custom` strategy. + +To select a strategy, set the `junit.jupiter.execution.parallel.config.strategy` +configuration parameter to one of the following options. + +`dynamic`:: + Computes the desired parallelism based on the number of available processors/cores + multiplied by the `junit.jupiter.execution.parallel.config.dynamic.factor` + configuration parameter (defaults to `1`). + The optional `junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor` + configuration parameter can be used to limit the maximum number of threads. + +`fixed`:: + Uses the mandatory `junit.jupiter.execution.parallel.config.fixed.parallelism` + configuration parameter as the desired parallelism. + The optional `junit.jupiter.execution.parallel.config.fixed.max-pool-size` + configuration parameter can be used to limit the maximum number of threads. + +`custom`:: + Allows you to specify a custom `{ParallelExecutionConfigurationStrategy}` + implementation via the mandatory `junit.jupiter.execution.parallel.config.custom.class` + configuration parameter to determine the desired configuration. + +If no configuration strategy is set, JUnit Jupiter uses the `dynamic` configuration +strategy with a factor of `1`. Consequently, the desired parallelism will be equal to the +number of available processors/cores. + +.Parallelism alone does not imply maximum number of concurrent threads +NOTE: By default, JUnit Jupiter does not guarantee that the number of threads used to +execute test will not exceed the configured parallelism. For example, when using one +of the synchronization mechanisms described in the next section, the executor service +implementation may spawn additional threads to ensure execution continues with sufficient +parallelism. If you require such guarantees, it is possible to limit the maximum number of +threads by configuring the maximum pool size of the `dynamic`, `fixed` and `custom` +strategies. + +[[config-properties]] +=== Relevant properties + +The following table lists relevant properties for configuring parallel execution. See +xref:running-tests/configuration-parameters.adoc[] for details on how to set such properties. + +==== General + +`junit.jupiter.execution.parallel.enabled=true|false`:: + Enable/disable parallel test execution (defaults to `false`). + +`junit.jupiter.execution.parallel.mode.default=concurrent|same_thread`:: + Default execution mode of nodes in the test tree (defaults to `same_thread`). + +`junit.jupiter.execution.parallel.mode.classes.default=concurrent|same_thread`:: + Default execution mode of top-level classes (defaults to `same_thread`). + +`junit.jupiter.execution.parallel.config.executor-service=fork_join_pool|worker_thread_pool`:: + Type of `HierarchicalTestExecutorService` to use for parallel execution (defaults to + `fork_join_pool`). + +`junit.jupiter.execution.parallel.config.strategy=dynamic|fixed|custom`:: + Execution strategy for desired parallelism, maximum pool size, etc. (defaults to `dynamic`). + +==== Dynamic strategy + +`junit.jupiter.execution.parallel.config.dynamic.factor=decimal`:: + Factor to be multiplied by the number of available processors/cores to determine the + desired parallelism for the ```dynamic``` configuration strategy. + Must be a positive decimal number (defaults to `1.0`). + +`junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor=decimal`:: + Factor to be multiplied by the number of available processors/cores and the value of + `junit.jupiter.execution.parallel.config.dynamic.factor` to determine the desired + parallelism for the ```dynamic``` configuration strategy. + Must be a positive decimal number greater than or equal to `1.0` (defaults to 256 plus + the value of `junit.jupiter.execution.parallel.config.dynamic.factor` multiplied by the + number of available processors/cores) + +`junit.jupiter.execution.parallel.config.dynamic.saturate=true|false`:: + Enable/disable saturation of the underlying `ForkJoinPool` for the ```dynamic``` + configuration strategy (defaults to `true`). Only used if + `junit.jupiter.execution.parallel.config.executor-service` is set to `fork_join_pool`. + +==== Fixed strategy + +`junit.jupiter.execution.parallel.config.fixed.parallelism=integer`:: + Desired parallelism for the ```fixed``` configuration strategy (no default value). Must + be a positive integer. + +`junit.jupiter.execution.parallel.config.fixed.max-pool-size=integer`:: + Desired maximum pool size of the underlying fork-join pool for the ```fixed``` + configuration strategy. Must be a positive integer greater than or equal to + `junit.jupiter.execution.parallel.config.fixed.parallelism` (defaults to 256 plus the + value of `junit.jupiter.execution.parallel.config.fixed.parallelism`). + +`junit.jupiter.execution.parallel.config.fixed.saturate=true|false`:: + Enable/disable saturation of the underlying `ForkJoinPool` for the ```fixed``` + configuration strategy (defaults to `true`). Only used if + `junit.jupiter.execution.parallel.config.executor-service` is set to `fork_join_pool`. + +==== Custom strategy + +`junit.jupiter.execution.parallel.config.custom.class=classname`:: + Fully qualified class name of the `ParallelExecutionConfigurationStrategy` to be used + for the ```custom``` configuration strategy (no default value). + +[[synchronization]] +== Synchronization + +In addition to controlling the execution mode using the `{Execution}` annotation, JUnit +Jupiter provides another annotation-based declarative synchronization mechanism. The +`{ResourceLock}` annotation allows you to declare that a test class or method uses a +specific shared resource that requires synchronized access to ensure reliable test +execution. The shared resource is identified by a unique name which is a `String`. The +name can be user-defined or one of the predefined constants in `{Resources}`: +`SYSTEM_PROPERTIES`, `SYSTEM_OUT`, `SYSTEM_ERR`, `LOCALE`, or `TIME_ZONE`. + +In addition to declaring these shared resources statically, the `{ResourceLock}` +annotation has a `providers` attribute that allows registering implementations of the +`{ResourceLocksProvider}` interface that can add shared resources dynamically at runtime. +Note that resources declared statically with `{ResourceLock}` annotation are combined with +resources added dynamically by `{ResourceLocksProvider}` implementations. + +If the tests in the following example were run in parallel _without_ the use of +`{ResourceLock}`, they would be _flaky_. Sometimes they would pass, and at other times they +would fail due to the inherent race condition of writing and then reading the same JVM +System Property. + +When access to shared resources is declared using the `{ResourceLock}` annotation, the +JUnit Jupiter engine uses this information to ensure that no conflicting tests are run in +parallel. This guarantee extends to lifecycle methods of a test class or method. For +example, if a test method is annotated with a `{ResourceLock}` annotation, the "lock" will +be acquired before any `@BeforeEach` methods are executed and released after all +`@AfterEach` methods have been executed. + +[NOTE] +.Running tests in isolation +==== +If most of your test classes can be run in parallel without any synchronization but you +have some test classes that need to run in isolation, you can mark the latter with the +`{Isolated}` annotation. Tests in such classes are executed sequentially without any other +tests running at the same time. +==== + +In addition to the `String` that uniquely identifies the shared resource, you may specify +an access mode. Two tests that require `READ` access to a shared resource may run in +parallel with each other but not while any other test that requires `READ_WRITE` access +to the same shared resource is running. + +[source,java] +.Declaring shared resources "statically" with `{ResourceLock}` annotation +---- +include::example$java/example/sharedresources/StaticSharedResourcesDemo.java[tags=user_guide] +---- + +[source,java] +.Adding shared resources "dynamically" with `{ResourceLocksProvider}` implementation +---- +include::example$java/example/sharedresources/DynamicSharedResourcesDemo.java[tags=user_guide] +---- + +Also, "static" shared resources can be declared for _direct_ child nodes via the `target` +attribute in the `{ResourceLock}` annotation, the attribute accepts a value from +the `{ResourceLockTarget}` enum. + +Specifying `target = CHILDREN` in a class-level `{ResourceLock}` annotation +has the same semantics as adding an annotation with the same `value` and `mode` +to each test method and nested test class declared in this class. + +This may improve parallelization when a test class declares a `READ` lock, +but only a few methods hold a `READ_WRITE` lock. + +Tests in the following example would run in the `SAME_THREAD` if the `{ResourceLock}` +didn't have `target = CHILDREN`. This is because the test class declares a `READ` +shared resource, but one test method holds a `READ_WRITE` lock, +which would force the `SAME_THREAD` execution mode for all the test methods. + +[source,java] +.Declaring shared resources for child nodes with `target` attribute +---- +include::example$java/example/sharedresources/ChildrenSharedResourcesDemo.java[tags=user_guide] +---- diff --git a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc new file mode 100644 index 000000000000..8f3844fcce9c --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc @@ -0,0 +1,1314 @@ += Parameterized Classes and Tests + +_Parameterized tests_ make it possible to run a test method multiple times with different +arguments. They are declared just like regular `@Test` methods but use the +`{ParameterizedTest}` annotation instead. + +_Parameterized classes_ make it possible to run _all_ tests in a test class, including +xref:writing-tests/nested-tests.adoc[], multiple times with different arguments. They are declared just +like regular test classes and may contain any supported test method type (including +`@ParameterizedTest`) but annotated with the `{ParameterizedClass}` annotation. + +WARNING: _Parameterized classes_ are currently an _experimental_ feature. You're invited +to give it a try and provide feedback to the JUnit team so they can improve and eventually +xref:api-evolution.adoc[promote] this feature. + +Regardless of whether you are parameterizing a test method or a test class, you must +declare at least one <> that will +provide the arguments for each invocation and then +<> the arguments in the +parameterized method or class, respectively. + +The following example demonstrates a parameterized test that uses the `@ValueSource` +annotation to specify a `String` array as the source of arguments. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=first_example] +---- + +When executing the above parameterized test method, each invocation will be reported +separately. For instance, the `ConsoleLauncher` will print output similar to the +following. + +.... +palindromes(String) ✔ +├─ [1] candidate = "racecar" ✔ +├─ [2] candidate = "radar" ✔ +└─ [3] candidate = "able was I ere I saw elba" ✔ +.... + +The same `@ValueSource` annotation can be used to specify the source of arguments for a +`@ParameterizedClass`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedClassDemo.java[tags=first_example] +---- + +When executing the above parameterized test class, each invocation will be reported +separately. For instance, the `ConsoleLauncher` will print output similar to the +following. + +.... +PalindromeTests ✔ +├─ [1] candidate = "racecar" ✔ +│ ├─ palindrome() ✔ +│ └─ reversePalindrome() ✔ +├─ [2] candidate = "radar" ✔ +│ ├─ palindrome() ✔ +│ └─ reversePalindrome() ✔ +└─ [3] candidate = "able was I ere I saw elba" ✔ + ├─ palindrome() ✔ + └─ reversePalindrome() ✔ +.... + +[[tests-setup]] +== Required Setup + +In order to use parameterized classes or tests you need to add a dependency on the +`junit-jupiter-params` artifact. Please refer to xref:appendix.adoc#dependency-metadata[Dependency Metadata] for details. + +[[tests-consuming-arguments]] +== Consuming Arguments + +[[tests-consuming-arguments-methods]] +=== Parameterized Tests + +Parameterized test methods _consume_ arguments directly from the configured source (see +<>) following a one-to-one correlation between +argument source index and method parameter index (see examples in +<>). However, a parameterized test +method may also choose to _aggregate_ arguments from the source into a single object +passed to the method (see <>). +Additional arguments may also be provided by a `ParameterResolver` (e.g., to obtain an +instance of `TestInfo`, `TestReporter`, etc.). Specifically, a parameterized test method +must declare formal parameters according to the following rules. + +* Zero or more _indexed parameters_ must be declared first. +* Zero or more _aggregators_ must be declared next. +* Zero or more arguments supplied by a `ParameterResolver` must be declared last. + +In this context, an _indexed parameter_ is an argument for a given index in the +`{Arguments}` provided by an `{ArgumentsProvider}` that is passed as an argument to the +parameterized method at the same index in the method's formal parameter list. An +_aggregator_ is any parameter of type `{ArgumentsAccessor}` or any parameter annotated +with `{AggregateWith}`. + +[[tests-consuming-arguments-classes]] +=== Parameterized Classes + +Parameterized classes _consume_ arguments directly from the configured source (see +<>); either via their unique constructor or via +field injection. If a `{Parameter}`-annotated field is declared in the parameterized class +or one of its superclasses, field injection will be used. Otherwise, constructor injection +will be used. + +[[tests-consuming-arguments-constructor-injection]] +==== Constructor Injection + +WARNING: Constructor injection can only be used with the (default) `PER_METHOD` +xref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] mode. Please use +<> +with the `PER_CLASS` mode instead. + +For constructor injection, the same rules apply as defined for +<> +above. In the following example, two arguments are injected into the constructor of the +test class. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedClassDemo.java[tags=constructor_injection] +---- + +You may use _records_ to implement parameterized classes that avoid the boilerplate code +of declaring a test class constructor. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedRecordDemo.java[tags=example] +---- + +[[tests-consuming-arguments-field-injection]] +==== Field Injection + +For field injection, the following rules apply for fields annotated with `@Parameter`. + +* Zero or more _indexed parameters_ may be declared; each must have a unique index + specified in its `@Parameter(index)` annotation. The index may be omitted if there is + only one indexed parameter. If there are at least two indexed parameter declarations, + there must be declarations for all indexes from 0 to the largest declared index. +* Zero or more _aggregators_ may be declared; each without specifying an index in its + `@Parameter` annotation. +* Zero or more other fields may be declared as usual as long as they're not annotated with + `@Parameter`. + +In this context, an _indexed parameter_ is an argument for a given index in the +`{Arguments}` provided by an `{ArgumentsProvider}` that is injected into a field annotated +with `@Parameter(index)`. An _aggregator_ is any `@Parameter`-annotated field of type +{ArgumentsAccessor} or any field annotated with {AggregateWith}. + +The following example demonstrates how to use field injection to consume multiple +arguments in a parameterized class. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedClassDemo.java[tags=field_injection] +---- + +If field injection is used, no constructor parameters will be resolved with arguments from +the source. Other xref:writing-tests/dependency-injection-for-constructors-and-methods.adoc[`ParameterResolver` extensions] +may resolve constructor parameters as usual, though. + +[[tests-consuming-arguments-lifecycle-method]] +==== Lifecycle Methods + +`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` can also +be used to consume arguments if their `injectArguments` attribute is set to `true` (the +default). If so, their method signatures must follow the same rules apply as defined for +<> and +additionally use the same parameter types as the _indexed parameters_ of the parameterized +test class. Please refer to the Javadoc of `{BeforeParameterizedClassInvocation}` and +`{AfterParameterizedClassInvocation}` for details and to the +<> section for an +example. + +[NOTE] +.AutoCloseable arguments +==== +Arguments that implement `java.lang.AutoCloseable` (or `java.io.Closeable` which extends +`java.lang.AutoCloseable`) will be automatically closed after the parameterized class or +test invocation. + +To prevent this from happening, set the `autoCloseArguments` attribute in +`@ParameterizedTest` to `false`. Specifically, if an argument that implements +`AutoCloseable` is reused for multiple invocations of the same parameterized class or test +method, you must specify the `autoCloseArguments = false` on the `{ParameterizedClass}` or +`{ParameterizedTest}` annotation to ensure that the argument is not closed between +invocations. +==== + +[[tests-consuming-arguments-other-extensions]] +=== Other Extensions + +Other extensions can access the parameters and resolved arguments of a parameterized test +or class by retrieving a `{ParameterInfo}` object from the `{ExtensionContext_Store}`. +Please refer to the Javadoc of `{ParameterInfo}` for details. + +[[tests-sources]] +== Sources of Arguments + +Out of the box, JUnit Jupiter provides quite a few _source_ annotations. Each of the +following subsections provides a brief overview and an example for each of them. Please +refer to the Javadoc in the `{params-provider-package}` package for additional +information. + +TIP: All source annotations in this section are applicable to both `{ParameterizedClass}` +and `{ParameterizedTest}`. For the sake of brevity, the examples in this section will only +show how to use them with `{ParameterizedTest}` methods. + +[[tests-sources-ValueSource]] +=== @ValueSource + +`@ValueSource` is one of the simplest possible sources. It lets you specify a single +array of literal values and can only be used for providing a single argument per +parameterized test invocation. + +The following types of literal values are supported by `@ValueSource`. + +- `short` +- `byte` +- `int` +- `long` +- `float` +- `double` +- `char` +- `boolean` +- `java.lang.String` +- `java.lang.Class` + +For example, the following `@ParameterizedTest` method will be invoked three times, with +the values `1`, `2`, and `3` respectively. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ValueSource_example] +---- + +[[tests-sources-null-and-empty]] +=== Null and Empty Sources + +In order to check corner cases and verify proper behavior of our software when it is +supplied _bad input_, it can be useful to have `null` and _empty_ values supplied to our +parameterized tests. The following annotations serve as sources of `null` and empty values +for parameterized tests that accept a single argument. + +* `{NullSource}`: provides a single `null` argument to the annotated `@ParameterizedClass` + or `@ParameterizedTest`. + - `@NullSource` cannot be used for a parameter that has a primitive type. +* `{EmptySource}`: provides a single _empty_ argument to the annotated + `@ParameterizedClass` or `@ParameterizedTest` for parameters of the following types: + `java.lang.String`, `java.util.Collection` (and concrete subtypes with a `public` no-arg + constructor), `java.util.List`, `java.util.Set`, `java.util.SortedSet`, + `java.util.NavigableSet`, `java.util.Map` (and concrete subtypes with a `public` no-arg + constructor), `java.util.SortedMap`, `java.util.NavigableMap`, primitive arrays (e.g., + `int[]`, `char[][]`, etc.), object arrays (e.g., `String[]`, `Integer[][]`, etc.). +* `{NullAndEmptySource}`: a _composed annotation_ that combines the functionality of + `@NullSource` and `@EmptySource`. + +If you need to supply multiple varying types of _blank_ strings to a parameterized +class or test, you can achieve that using +<> -- for example, +`@ValueSource(strings = {"{nbsp}", "{nbsp}{nbsp}{nbsp}", "\t", "\n"})`. + +You can also combine `@NullSource`, `@EmptySource`, and `@ValueSource` to test a wider +range of `null`, _empty_, and _blank_ input. The following example demonstrates how to +achieve this for strings. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example1] +---- + +Making use of the composed `@NullAndEmptySource` annotation simplifies the above as +follows. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example2] +---- + +NOTE: Both variants of the `nullEmptyAndBlankStrings(String)` parameterized test method +result in six invocations: 1 for `null`, 1 for the empty string, and 4 for the explicit +blank strings supplied via `@ValueSource`. + +[[tests-sources-EnumSource]] +=== @EnumSource + +`@EnumSource` provides a convenient way to use `Enum` constants. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_example] +---- + +The annotation's `value` attribute is optional. When omitted, the declared type of the +first parameter is used. The test will fail if it does not reference an enum type. +Thus, the `value` attribute is required in the above example because the method parameter +is declared as `TemporalUnit`, i.e. the interface implemented by `ChronoUnit`, which isn't +an enum type. Changing the method parameter type to `ChronoUnit` allows you to omit the +explicit enum type from the annotation as follows. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_example_autodetection] +---- + +The annotation provides an optional `names` attribute that lets you specify which +constants shall be used, like in the following example. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_include_example] +---- + +In addition to `names`, you can use the `from` and `to` attributes to specify a range of +constants. The range starts from the constant specified in the `from` attribute and +includes all subsequent constants up to and including the one specified in the `to` +attribute, based on the natural order of the enum constants. + +If `from` and `to` attributes are omitted, they default to the first and last constants +in the enum type, respectively. If all `names`, `from`, and `to` attributes are omitted, +all constants will be used. The following example demonstrates how to specify a range of +constants. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_range_example] +---- + +The `@EnumSource` annotation also provides an optional `mode` attribute that enables +fine-grained control over which constants are passed to the test method. For example, you +can exclude names from the enum constant pool or specify regular expressions as in the +following examples. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_exclude_example] +---- + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_regex_example] +---- + +You can also combine `mode` with the `from`, `to` and `names` attributes to define a +range of constants while excluding specific values from that range as shown below. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_range_exclude_example] +---- + +[[tests-sources-MethodSource]] +=== @MethodSource + +`{MethodSource}` allows you to refer to one or more _factory_ methods of the test class +or external classes. + +Factory methods within the test class must be `static` unless the test class is annotated +with `@TestInstance(Lifecycle.PER_CLASS)`; whereas, factory methods in external classes +must always be `static`. + +Each factory method must generate a _stream_ of _arguments_, and each set of arguments +within the stream will be provided as the physical arguments for individual invocations +of the annotated `@ParameterizedClass` or `@ParameterizedTest`. Generally speaking this +translates to a `Stream` of `Arguments` (i.e., `Stream`); however, the actual +concrete return type can take on many forms. In this context, a "stream" is anything that +JUnit can reliably convert into a `Stream`, such as `Stream`, `DoubleStream`, +`LongStream`, `IntStream`, `Collection`, `Iterator`, `Iterable`, an array of objects or +primitives, or any type that provides an `iterator(): Iterator` method (such as, for +example, a `kotlin.sequences.Sequence`). The "arguments" within the stream can be supplied +as an instance of `Arguments`, an array of objects (e.g., `Object[]`), or a single value +if the parameterized class or test method accepts a single argument. + +If the return type is `Stream` or one of the primitive streams, +JUnit will properly close it by calling `BaseStream.close()`, +making it safe to use a resource such as `Files.lines()`. + +If you only need a single parameter, you can return a `Stream` of instances of the +parameter type as demonstrated in the following example. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSource_example] +---- + +For a `@ParameterizedClass`, providing a factory method name via `@MethodSource` is +mandatory. For a `@ParameterizedTest`, if you do not explicitly provide a factory method +name, JUnit Jupiter will search for a _factory_ method with the same name as the current +`@ParameterizedTest` method by convention. This is demonstrated in the following example. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSource_without_value_example] +---- + +Streams for primitive types (`DoubleStream`, `IntStream`, and `LongStream`) are also +supported as demonstrated by the following example. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=primitive_MethodSource_example] +---- + +If a parameterized class or test method declares multiple parameters, you need to return a +collection, stream, or array of `Arguments` instances or object arrays as shown below (see +the Javadoc for `{MethodSource}` for further details on supported return types). Note that +`arguments(Object...)` is a static factory method defined in the `Arguments` interface. In +addition, `Arguments.of(Object...)` may be used as an alternative to +`arguments(Object...)`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_MethodSource_example] +---- + +An external, `static` _factory_ method can be referenced by providing its _fully qualified +method name_ as demonstrated in the following example. + +[source,java,indent=0] +---- +package example; + +include::example$java/example/ExternalMethodSourceDemo.java[tags=external_MethodSource_example] +---- + +Factory methods can declare parameters, which will be provided by registered +implementations of the `ParameterResolver` extension API. In the following example, the +factory method is referenced by its name since there is only one such method in the test +class. If there are several local methods with the same name, parameters can also be +provided to differentiate them – for example, `@MethodSource("factoryMethod()")` or +`@MethodSource("factoryMethod(java.lang.String)")`. Alternatively, the factory method +can be referenced by its fully qualified method name, e.g. +`@MethodSource("example.MyTests#factoryMethod(java.lang.String)")`. + +[source,java,indent=0] +---- +include::example$java/example/MethodSourceParameterResolutionDemo.java[tags=parameter_resolution_MethodSource_example] +---- + +[[tests-sources-FieldSource]] +=== @FieldSource + +`{FieldSource}` allows you to refer to one or more fields of the test class or external +classes. + +Fields within the test class must be `static` unless the test class is annotated with +`@TestInstance(Lifecycle.PER_CLASS)`; whereas, fields in external classes must always be +`static`. + +Each field must be able to supply a _stream_ of arguments, and each set of "arguments" +within the "stream" will be provided as the physical arguments for individual invocations +of the annotated `@ParameterizedClass` or `@ParameterizedTest`. + +In this context, a "stream" is anything that JUnit can reliably convert to a `Stream`; +however, the actual concrete field type can take on many forms. Generally speaking this +translates to a `Collection`, an `Iterable`, a `Supplier` of a stream (`Stream`, +`DoubleStream`, `LongStream`, or `IntStream`), a `Supplier` of an `Iterator`, an array of +objects or primitives, or any type that provides an `iterator(): Iterator` method (such +as, for example, a `kotlin.sequences.Sequence`). Each set of "arguments" within the +"stream" can be supplied as an instance of `Arguments`, an array of objects (for example, +`Object[]`, `String[]`, etc.), or a single value if the parameterized class or test method accepts +a single argument. + +[WARNING] +==== +In contrast to the supported return types for +<> factory +methods, the value of a `@FieldSource` field cannot be an instance of `Stream`, +`DoubleStream`, `LongStream`, `IntStream`, or `Iterator`, since the values of such types +are _consumed_ the first time they are processed. However, if you wish to use one of +these types, you can wrap it in a `Supplier` — for example, `Supplier`. +==== + +If the `Supplier` return type is `Stream` or one of the primitive streams, +JUnit will properly close it by calling `BaseStream.close()`, +making it safe to use a resource such as `Files.lines()`. + +Please note that a one-dimensional array of objects supplied as a set of "arguments" will +be handled differently than other types of arguments. Specifically, all the elements of a +one-dimensional array of objects will be passed as individual physical arguments to the +`@ParameterizedClass` or `@ParameterizedTest`. See the Javadoc for `{FieldSource}` for +further details. + +For a `@ParameterizedClass`, providing a field name via `@FieldSource` is mandatory. For a +`@ParameterizedTest`, if you do not explicitly provide a field name, JUnit Jupiter will +search in the test class for a field that has the same name as the current +`@ParameterizedTest` method by convention. This is demonstrated in the following example. +This parameterized test method will be invoked twice: with the values `"apple"` and +`"banana"`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=default_field_FieldSource_example] +---- + +The following example demonstrates how to provide a single explicit field name via +`@FieldSource`. This parameterized test method will be invoked twice: with the values +`"apple"` and `"banana"`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=explicit_field_FieldSource_example] +---- + +The following example demonstrates how to provide multiple explicit field names via +`@FieldSource`. This example uses the `listOfFruits` field from the previous example as +well as the `additionalFruits` field. Consequently, this parameterized test method will +be invoked four times: with the values `"apple"`, `"banana"`, `"cherry"`, and +`"dewberry"`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=multiple_fields_FieldSource_example] +---- + +It is also possible to provide a `Stream`, `DoubleStream`, `IntStream`, `LongStream`, or +`Iterator` as the source of arguments via a `@FieldSource` field as long as the stream or +iterator is wrapped in a `java.util.function.Supplier`. The following example demonstrates +how to provide a `Supplier` of a `Stream` of named arguments. This parameterized test +method will be invoked twice: with the values `"apple"` and `"banana"` and with display +names `"Apple"` and `"Banana"`, respectively. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments_FieldSource_example] +---- + +[NOTE] +==== +Note that `arguments(Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. + +Similarly, `named(String, Object)` is a static factory method defined in the +`org.junit.jupiter.api.Named` interface. +==== + +If a parameterized class or test method declares multiple parameters, the corresponding +`@FieldSource` field must be able to provide a collection, stream supplier, or array of +`Arguments` instances or object arrays as shown below (see the Javadoc for `{FieldSource}` +for further details on supported types). + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_FieldSource_example] +---- + +[NOTE] +==== +Note that `arguments(Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. +==== + +An external, `static` `@FieldSource` field can be referenced by providing its +_fully qualified field name_ as demonstrated in the following example. + +[source,java,indent=0] +---- +include::example$java/example/ExternalFieldSourceDemo.java[tags=external_field_FieldSource_example] +---- + +[[tests-sources-CsvSource]] +=== @CsvSource + +`@CsvSource` allows you to express argument lists as comma-separated values (i.e., CSV +`String` literals). Each string provided via the `value` attribute in `@CsvSource` +represents a CSV record and results in one invocation of the parameterized class or +test. The first record may optionally be used to supply CSV headers (see the Javadoc for +the `useHeadersInDisplayName` attribute for details and an example). + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=CsvSource_example] +---- + +The default delimiter is a comma (`,`), but you can use another character by setting the +`delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a +`String` delimiter instead of a single character. However, both delimiter attributes +cannot be set simultaneously. + +By default, `@CsvSource` uses a single quote (`'`) as its quote character, but this can be +changed via the `quoteCharacter` attribute. See the `'lemon, lime'` value in the example +above and in the table below. An empty, quoted value (`''`) results in an empty `String` +unless the `emptyValue` attribute is set; whereas, an entirely _empty_ value is +interpreted as a `null` reference. By specifying one or more `nullValues`, a custom value +can be interpreted as a `null` reference (see the `NIL` example in the table below). An +`ArgumentConversionException` is thrown if the target type of a `null` reference is a +primitive type. + +NOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless +of any custom values configured via the `nullValues` attribute. + +Except within a quoted string, leading and trailing whitespace in a CSV column is trimmed +by default. This behavior can be changed by setting the +`ignoreLeadingAndTrailingWhitespace` attribute to `true`. + +[cols="50,50"] +|=== +| Example Input | Resulting Argument List + +| `@CsvSource({ "apple, banana" })` | `"apple"`, `"banana"` +| `@CsvSource({ "apple, 'lemon, lime'" })` | `"apple"`, `"lemon, lime"` +| `@CsvSource({ "apple, ''" })` | `"apple"`, `""` +| `@CsvSource({ "apple, " })` | `"apple"`, `null` +| `@CsvSource(value = { "apple, banana, NIL" }, nullValues = "NIL")` | `"apple"`, `"banana"`, `null` +| `@CsvSource(value = { " apple , banana" }, ignoreLeadingAndTrailingWhitespace = false)` | `" apple "`, `" banana"` +|=== + +If the programming language you are using supports Java _text blocks_ or equivalent +multi-line string literals, you can alternatively use the `textBlock` attribute of +`@CsvSource`. Each record within a text block represents a CSV record and results in one +invocation of the parameterized class or test. The first record may optionally be used to +supply CSV headers by setting the `useHeadersInDisplayName` attribute to `true` as in the +example below. + +Using a text block, the previous example can be implemented as follows. + +[source,java,indent=0] +---- +@ParameterizedTest +@CsvSource(useHeadersInDisplayName = true, textBlock = """ + FRUIT, RANK + apple, 1 + banana, 2 + 'lemon, lime', 0xF1 + strawberry, 700_000 + """) +void testWithCsvSource(String fruit, int rank) { + // ... +} +---- + +The generated display names for the previous example include the CSV header names. + +---- +[1] FRUIT = "apple", RANK = "1" +[2] FRUIT = "banana", RANK = "2" +[3] FRUIT = "lemon, lime", RANK = "0xF1" +[4] FRUIT = "strawberry", RANK = "700_000" +---- + +In contrast to CSV records supplied via the `value` attribute, a text block can contain +comments. Any line beginning with the value of the `commentCharacter` attribute (`+++#+++` +by default) will be treated as a comment and ignored. Note that there is one exception +to this rule: if the comment character appears within a quoted field, it loses +its special meaning. + +The comment character must be the first character on the line without any leading +whitespace. It is therefore recommended that the closing text block delimiter (`"""`) +be placed either at the end of the last line of input or on the following line, +left aligned with the rest of the input (as can be seen in the example below which +demonstrates formatting similar to a table). + +[source,java,indent=0] +---- +@ParameterizedTest +@CsvSource(delimiter = '|', quoteCharacter = '"', textBlock = """ + #----------------------------- + # FRUIT | RANK + #----------------------------- + apple | 1 + #----------------------------- + banana | 2 + #----------------------------- + "lemon lime" | 0xF1 + #----------------------------- + strawberry | 700_000 + #----------------------------- + """) +void testWithCsvSource(String fruit, int rank) { + // ... +} +---- + +[NOTE] +==== +Java's https://docs.oracle.com/en/java/javase/17/text-blocks/index.html[text block] +feature automatically removes _incidental whitespace_ when the code is compiled. +However other JVM languages such as Groovy and Kotlin do not. Thus, if you are using a +programming language other than Java and your text block contains comments or new lines +within quoted strings, you will need to ensure that there is no leading whitespace within +your text block. +==== + +[[tests-sources-CsvFileSource]] +=== @CsvFileSource + +`@CsvFileSource` lets you use comma-separated value (CSV) files from the classpath or the +local file system. Each record from a CSV file results in one invocation of the +parameterized class or test. The first record may optionally be used to supply CSV +headers. You can instruct JUnit to ignore the headers via the `numLinesToSkip` attribute. +If you would like for the headers to be used in the display names, you can set the +`useHeadersInDisplayName` attribute to `true`. The examples below demonstrate the use of +`numLinesToSkip` and `useHeadersInDisplayName`. + +The default delimiter is a comma (`,`), but you can use another character by setting the +`delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a +`String` delimiter instead of a single character. However, both delimiter attributes +cannot be set simultaneously. + +.Comments in CSV files +NOTE: Any line beginning with the value of the `commentCharacter` attribute (`+++#+++` +by default) will be interpreted as a comment and will be ignored. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=CsvFileSource_example] +---- + +[source,csv,indent=0] +.two-column.csv +---- +include::example$resources/two-column.csv[] +---- + +The following listing shows the generated display names for the first two parameterized +test methods above. + +---- +[1] country = "Sweden", reference = "1" +[2] country = "Poland", reference = "2" +[3] country = "United States of America", reference = "3" +[4] country = "France", reference = "700_000" +---- + +The following listing shows the generated display names for the last parameterized test +method above that uses CSV header names. + +---- +[1] COUNTRY = "Sweden", REFERENCE = "1" +[2] COUNTRY = "Poland", REFERENCE = "2" +[3] COUNTRY = "United States of America", REFERENCE = "3" +[4] COUNTRY = "France", REFERENCE = "700_000" +---- + +In contrast to the default syntax used in `@CsvSource`, `@CsvFileSource` uses a double +quote (`+++"+++`) as the quote character by default, but this can be changed via the +`quoteCharacter` attribute. See the `"United States of America"` value in the example +above. An empty, quoted value (`+++""+++`) results in an empty `String` unless the +`emptyValue` attribute is set; whereas, an entirely _empty_ value is interpreted as a +`null` reference. By specifying one or more `nullValues`, a custom value can be +interpreted as a `null` reference. An `ArgumentConversionException` is thrown if the +target type of a `null` reference is a primitive type. + +NOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless +of any custom values configured via the `nullValues` attribute. + +Except within a quoted string, leading and trailing whitespace in a CSV column is trimmed +by default. This behavior can be changed by setting the +`ignoreLeadingAndTrailingWhitespace` attribute to `true`. + +[[tests-sources-ArgumentsSource]] +=== @ArgumentsSource + +`@ArgumentsSource` can be used to specify a custom, reusable `ArgumentsProvider`. Note +that an implementation of `ArgumentsProvider` must be declared as either a top-level +class or as a `static` nested class. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsSource_example] +---- + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsProvider_example] +---- + +If you wish to implement a custom `ArgumentsProvider` that also consumes an annotation +(like built-in providers such as `{ValueArgumentsProvider}` or `{CsvArgumentsProvider}`), +you have the possibility to extend the `{AnnotationBasedArgumentsProvider}` class. + +Moreover, `ArgumentsProvider` implementations may declare constructor parameters in case +they need to be resolved by a registered `ParameterResolver` as demonstrated in the +following example. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsProviderWithConstructorInjection_example] +---- + +[[repeatable-sources]] +=== Multiple sources using repeatable annotations + +Repeatable annotations provide a convenient way to specify multiple sources from +different providers. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=repeatable_annotations] +---- + +Following the above parameterized test, a test case will run for each argument: + +---- +[1] foo +[2] bar +---- + +The following annotations are repeatable: + +* `@ValueSource` +* `@EnumSource` +* `@MethodSource` +* `@FieldSource` +* `@CsvSource` +* `@CsvFileSource` +* `@ArgumentsSource` + +[[tests-argument-count-validation]] +== Argument Count Validation + +By default, when an arguments source provides more arguments than the test method needs, +those additional arguments are ignored and the test executes as usual. +This can lead to bugs where arguments are never passed to the parameterized class or +method. + +To prevent this, you can set argument count validation to 'strict'. +Then, any additional arguments will cause an error instead. + +To change this behavior for all tests, set the +`junit.jupiter.params.argumentCountValidation` +xref:running-tests/configuration-parameters.adoc[configuration parameter] to `strict`. +To change this behavior for a single parameterized class or test method, +use the `argumentCountValidation` attribute of the `@ParameterizedClass` or +`@ParameterizedTest` annotation: + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=argument_count_validation] +---- + +[[tests-argument-conversion]] +== Argument Conversion + +[[tests-argument-conversion-widening]] +=== Widening Conversion + +JUnit Jupiter supports +https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2[Widening Primitive +Conversion] for arguments supplied to a `@ParameterizedClass` or `@ParameterizedTest`. +For example, a parameterized class or test method annotated with +`@ValueSource(ints = { 1, 2, 3 })` can be declared to accept not only an argument of type +`int` but also an argument of type `long`, `float`, or `double`. + +[[tests-argument-conversion-implicit]] +=== Implicit Conversion + +To support use cases like `@CsvSource`, JUnit Jupiter provides a number of built-in +implicit type converters. The conversion process depends on the declared type of each +method parameter. + +For example, if a `@ParameterizedClass` or `@ParameterizedTest` declares a parameter +of type `TimeUnit` and the actual type supplied by the declared source is a `String`, the +string will be automatically converted into the corresponding `TimeUnit` enum constant. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=implicit_conversion_example] +---- + +`String` instances are implicitly converted to the following target types. + +NOTE: Decimal, hexadecimal, and octal `String` literals will be converted to their +integral types: `byte`, `short`, `int`, `long`, and their boxed counterparts. + +[[tests-argument-conversion-implicit-table]] +[cols="10,90"] +|=== +| Target Type | Example + +| `boolean`/`Boolean` | `"true"` -> `true` _(only accepts values 'true' or 'false', case-insensitive)_ +| `byte`/`Byte` | `"15"`, `"0xF"`, or `"017"` -> `(byte) 15` +| `char`/`Character` | `"o"` -> `'o'` +| `short`/`Short` | `"15"`, `"0xF"`, or `"017"` -> `(short) 15` +| `int`/`Integer` | `"15"`, `"0xF"`, or `"017"` -> `15` +| `long`/`Long` | `"15"`, `"0xF"`, or `"017"` -> `15L` +| `float`/`Float` | `"1.0"` -> `1.0f` +| `double`/`Double` | `"1.0"` -> `1.0d` +| `Enum` subclass | `"SECONDS"` -> `TimeUnit.SECONDS` +| `java.io.File` | `"/path/to/file"` -> `new File("/path/to/file")` +| `java.lang.Class` | `"java.lang.Integer"` -> `java.lang.Integer.class` _(use `$` for nested classes, e.g. `"java.lang.Thread$State"`)_ +| `java.lang.Class` | `"byte"` -> `byte.class` _(primitive types are supported)_ +| `java.lang.Class` | `"char[]"` -> `char[].class` _(array types are supported)_ +| `java.math.BigDecimal` | `"123.456e789"` -> `new BigDecimal("123.456e789")` +| `java.math.BigInteger` | `"1234567890123456789"` -> `new BigInteger("1234567890123456789")` +| `java.net.URI` | `"https://junit.org/"` -> `URI.create("https://junit.org/")` +| `java.net.URL` | `"https://junit.org/"` -> `URI.create("https://junit.org/").toURL()` +| `java.nio.charset.Charset` | `"UTF-8"` -> `Charset.forName("UTF-8")` +| `java.nio.file.Path` | `"/path/to/file"` -> `Paths.get("/path/to/file")` +| `java.time.Duration` | `"PT3S"` -> `Duration.ofSeconds(3)` +| `java.time.Instant` | `"1970-01-01T00:00:00Z"` -> `Instant.ofEpochMilli(0)` +| `java.time.LocalDateTime` | `"2017-03-14T12:34:56.789"` -> `LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000)` +| `java.time.LocalDate` | `"2017-03-14"` -> `LocalDate.of(2017, 3, 14)` +| `java.time.LocalTime` | `"12:34:56.789"` -> `LocalTime.of(12, 34, 56, 789_000_000)` +| `java.time.MonthDay` | `"--03-14"` -> `MonthDay.of(3, 14)` +| `java.time.OffsetDateTime` | `"2017-03-14T12:34:56.789Z"` -> `OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)` +| `java.time.OffsetTime` | `"12:34:56.789Z"` -> `OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC)` +| `java.time.Period` | `"P2M6D"` -> `Period.of(0, 2, 6)` +| `java.time.YearMonth` | `"2017-03"` -> `YearMonth.of(2017, 3)` +| `java.time.Year` | `"2017"` -> `Year.of(2017)` +| `java.time.ZonedDateTime` | `"2017-03-14T12:34:56.789Z"` -> `ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)` +| `java.time.ZoneId` | `"Europe/Berlin"` -> `ZoneId.of("Europe/Berlin")` +| `java.time.ZoneOffset` | `"+02:30"` -> `ZoneOffset.ofHoursMinutes(2, 30)` +| `java.util.Currency` | `"JPY"` -> `Currency.getInstance("JPY")` +| `java.util.Locale` | `"en-US"` -> `Locale.forLanguageTag("en-US")` +| `java.util.UUID` | `"d043e930-7b3b-48e3-bdbe-5a3ccfb833db"` -> `UUID.fromString("d043e930-7b3b-48e3-bdbe-5a3ccfb833db")` +|=== + +[[tests-argument-conversion-implicit-fallback]] +==== Fallback String-to-Object Conversion + +In addition to implicit conversion from strings to the target types listed in the above +table, JUnit Jupiter also provides a fallback mechanism for automatic conversion from a +`String` to a given target type if the target type declares exactly one suitable _factory +method_ or a _factory constructor_ as defined below. + +- __factory method__: a non-private, `static` method declared in the target type that + accepts either a single `String` argument or a single `CharSequence` argument and + returns an instance of the target type. The name of the method can be arbitrary and need + not follow any particular convention. +- __factory constructor__: a non-private constructor in the target type that accepts a + either a single `String` argument or a single `CharSequence` argument. Note that the + target type must be declared as either a top-level class or as a `static` nested class. + +NOTE: If multiple _factory methods_ are discovered, they will be ignored. If a _factory +method_ and a _factory constructor_ are discovered, the factory method will be used +instead of the constructor. + +For example, in the following `@ParameterizedTest` method, the `Book` argument will be +created by invoking the `Book.fromTitle(String)` factory method and passing `"42 Cats"` +as the title of the book. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example] +---- + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example_Book] +---- + +[[tests-argument-conversion-explicit]] +=== Explicit Conversion + +Instead of relying on implicit argument conversion, you may explicitly specify an +`ArgumentConverter` to use for a certain parameter using the `@ConvertWith` annotation +like in the following example. Note that an implementation of `ArgumentConverter` must be +declared as either a top-level class or as a `static` nested class. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example] +---- + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_ToStringArgumentConverter] +---- + +If the converter is only meant to convert one type to another, you can extend +`TypedArgumentConverter` to avoid boilerplate type checks. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_TypedArgumentConverter] +---- + +Explicit argument converters are meant to be implemented by test and extension authors. +Thus, `junit-jupiter-params` only provides a single explicit argument converter that may +also serve as a reference implementation: `JavaTimeArgumentConverter`. It is used via the +composed annotation `JavaTimeConversionPattern`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=explicit_java_time_converter] +---- + +If you wish to implement a custom `ArgumentConverter` that also consumes an annotation +(like `JavaTimeArgumentConverter`), you have the possibility to extend the +`{AnnotationBasedArgumentConverter}` class. + +[[tests-argument-aggregation]] +== Argument Aggregation + +By default, each _argument_ provided to a `@ParameterizedClass` or `@ParameterizedTest` +corresponds to a single method parameter. Consequently, argument sources which are +expected to supply a large number of arguments can lead to large constructor or method +signatures, respectively. + +In such cases, an `{ArgumentsAccessor}` can be used instead of multiple parameters. Using +this API, you can access the provided arguments through a single argument passed to your +test method. In addition, type conversion is supported as discussed in +<>. + +Besides, you can retrieve the current test invocation index with +`ArgumentsAccessor.getInvocationIndex()`. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAccessor_example] +---- + +_An instance of `ArgumentsAccessor` is automatically injected into any parameter of type +`ArgumentsAccessor`._ + +[[tests-argument-aggregation-custom]] +=== Custom Aggregators + +Apart from direct access to the arguments of a `@ParameterizedClass` or +`@ParameterizedTest` using an `ArgumentsAccessor`, JUnit Jupiter also supports the usage +of custom, reusable _aggregators_. + +To use a custom aggregator, implement the `{ArgumentsAggregator}` interface and register +it via the `@AggregateWith` annotation on a compatible parameter of the +`@ParameterizedClass` or `@ParameterizedTest`. The result of the aggregation will then be +provided as an argument for the corresponding parameter when the parameterized test is +invoked. Note that an implementation of `ArgumentsAggregator` must be declared as either a +top-level class or as a `static` nested class. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example] +---- + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example_PersonAggregator] +---- + +If you find yourself repeatedly declaring `@AggregateWith(MyTypeAggregator.class)` for +multiple parameterized classes or methods across your codebase, you may wish to create a +custom _composed annotation_ such as `@CsvToMyType` that is meta-annotated with +`@AggregateWith(MyTypeAggregator.class)`. The following example demonstrates this in +action with a custom `@CsvToPerson` annotation. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example] +---- + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example_CsvToPerson] +---- + + +[[tests-display-names]] +== Customizing Display Names + +By default, the display name of a parameterized class or test invocation contains the +invocation index and a comma-separated list of the `String` representations of all +arguments for that specific invocation. If parameter names are present in the bytecode, +each argument will be preceded by its parameter name and an equals sign (unless the +argument is only available via an `ArgumentsAccessor` or `ArgumentAggregator`) – for +example, `firstName = "Jane"`. + +[TIP] +==== +To ensure that parameter names are present in the bytecode, test code must be compiled +with the `-parameters` compiler flag for Java or with the `-java-parameters` compiler flag +for Kotlin. +==== + +However, you can customize invocation display names via the `name` attribute of the +`@ParameterizedClass` or `@ParameterizedTest` annotation as in the following example. + +====== +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=custom_display_names] +---- + +When executing the above method using the `ConsoleLauncher` you will see output similar to +the following. + +.... +Display name of container ✔ +├─ 1 ==> the rank of "apple" is "1" ✔ +├─ 2 ==> the rank of "banana" is "2" ✔ +└─ 3 ==> the rank of "lemon, lime" is "3" ✔ +.... +====== + +[NOTE] +==== +Please note that `name` is a `MessageFormat` pattern. Thus, a single quote (`'`) needs to +be represented as a doubled single quote (`''`) in order to be displayed. +==== + +The following placeholders are supported within custom display names. + +[cols="20,80"] +|=== +| Placeholder | Description + +| `\{displayName}` | the display name of the method +| `\{index}` | the current invocation index (1-based) +| `\{arguments}` | the complete, comma-separated arguments list +| `\{argumentsWithNames}` | the complete, comma-separated arguments list with parameter names +| `\{argumentSetName}` | the name of the argument set +| `\{argumentSetNameOrArgumentsWithNames}` | `\{argumentSetName}` or `\{argumentsWithNames}`, depending on how the arguments are supplied +| `\{0}`, `\{1}`, ... | an individual argument +|=== + +NOTE: When including arguments in display names, their string representations are truncated +if they exceed the configured maximum length. The limit is configurable via the +`junit.jupiter.params.displayname.argument.maxlength` configuration parameter and defaults +to 512 characters. + +When using `@MethodSource`, `@FieldSource`, or `@ArgumentsSource`, you can provide custom +names for individual arguments or custom names for entire sets of arguments. + +Use the `{Named}` API to provide a custom name for an individual argument, and the custom +name will be used if the argument is included in the invocation display name, like in the +example below. + +====== +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments] +---- + +When executing the above method using the `ConsoleLauncher` you will see output similar to +the following. + +.... +A parameterized test with named arguments ✔ +├─ 1: An important file ✔ +└─ 2: Another file ✔ +.... +====== + +[NOTE] +==== +Note that `arguments(Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. + +Similarly, `named(String, Object)` is a static factory method defined in the +`org.junit.jupiter.api.Named` interface. +==== + +Use the `ArgumentSet` API to provide a custom name for the entire set of arguments, and +the custom name will be used as the display name, like in the example below. + +====== +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=named_argument_set] +---- + +When executing the above method using the `ConsoleLauncher` you will see output similar to +the following. + +.... +A parameterized test with named argument sets ✔ +├─ [1] Important files ✔ +└─ [2] Other files ✔ +.... +====== + +[NOTE] +==== +Note that `argumentSet(String, Object...)` is a static factory method defined in the +`org.junit.jupiter.params.provider.Arguments` interface. +==== + +[[tests-display-names-quoted-text]] +=== Quoted Text-based Arguments + +As of JUnit Jupiter 6.0, text-based arguments in display names for parameterized tests are +quoted by default. In this context, any `CharSequence` (such as a `String`) or `Character` +is considered text. A `CharSequence` is wrapped in double quotes (`"`), and a `Character` +is wrapped in single quotes (`'`). + +Special characters will be escaped in the quoted text. For example, carriage returns and +line feeds will be escaped as `\\r` and `\\n`, respectively. + +[TIP] +==== +This feature can be disabled by setting the `quoteTextArguments` attributes in +`@ParameterizedClass` and `@ParameterizedTest` to `false`. +==== + +For example, given a string argument `"line 1\nline 2"`, the physical representation in +the display name will be `"\"line 1\\nline 2\""` which is printed as `"line 1\nline 2"`. +Similarly, given a string argument `"\t"`, the physical representation in the display name +will be `"\"\\t\""` which is printed as `"\t"` instead of a blank string or invisible tab +character. The same applies for a character argument `'\t'`, whose physical representation +in the display name would be `"'\\t'"` which is printed as `'\t'`. + +For a concrete example, if you run the first `nullEmptyAndBlankStrings(String text)` +parameterized test method from the +<> section above, the following +display names are generated. + +---- +[1] text = null +[2] text = "" +[3] text = " " +[4] text = " " +[5] text = "\t" +[6] text = "\n" +---- + +If you run the first `testWithCsvSource(String fruit, int rank)` parameterized test method +from the <> section above, the +following display names are generated. + +---- +[1] fruit = "apple", rank = "1" +[2] fruit = "banana", rank = "2" +[3] fruit = "lemon, lime", rank = "0xF1" +[4] fruit = "strawberry", rank = "700_000" +---- + +[NOTE] +==== +The original source arguments are quoted when generating a display name, and this occurs +before any implicit or explicit argument conversion is performed. + +For example, if a parameterized test accepts `3.14` as a `float` argument that was +converted from `"3.14"` as an input string, `"3.14"` will be present in the display name +instead of `3.14`. You can see the effect of this with the `rank` values in the above +example. +==== + +[[tests-display-names-default-pattern]] +=== Default Display Name Pattern + +If you'd like to set a default name pattern for all parameterized classes and tests in +your project, you can declare the `junit.jupiter.params.displayname.default` configuration +parameter in the `junit-platform.properties` file as demonstrated in the following example (see +xref:running-tests/configuration-parameters.adoc[] for other options). + +[source,properties,indent=0] +---- +junit.jupiter.params.displayname.default = {index} +---- + +[[tests-display-names-precedence-rules]] +=== Precedence Rules + +The display name for a parameterized class or test is determined according to the +following precedence rules: + +1. `name` attribute in `@ParameterizedClass` or `@ParameterizedTest`, if present +2. value of the `junit.jupiter.params.displayname.default` configuration parameter, if present +3. `DEFAULT_DISPLAY_NAME` constant defined in + `org.junit.jupiter.params.ParameterizedInvocationConstants` + +[[tests-lifecycle-interop]] +== Lifecycle and Interoperability + +[[tests-lifecycle-interop-methods]] +=== Parameterized Tests + +Each invocation of a parameterized test has the same lifecycle as a regular `@Test` +method. For example, `@BeforeEach` methods will be executed before each invocation. +Similar to xref:writing-tests/dynamic-tests.adoc[], invocations will appear one by one in the +test tree of an IDE. You may at will mix regular `@Test` methods and `@ParameterizedTest` +methods within the same test class. + +You may use `ParameterResolver` extensions with `@ParameterizedTest` methods. However, +method parameters that are resolved by argument sources need to come first in the +parameter list. Since a test class may contain regular tests as well as parameterized +tests with different parameter lists, values from argument sources are not resolved for +lifecycle methods (e.g. `@BeforeEach`) and test class constructors. + +[source,java,indent=0] +---- +include::example$java/example/ParameterizedTestDemo.java[tags=ParameterResolver_example] +---- + +[[tests-lifecycle-interop-classes]] +=== Parameterized Classes + +Each invocation of a parameterized class has the same lifecycle as a regular test class. +For example, `@BeforeAll` methods will be executed _once_ before all invocations and +`@BeforeEach` methods will be executed before each _test method_ invocation. Similar to +xref:writing-tests/dynamic-tests.adoc[], invocations will appear one by one in the test tree of an +IDE. + +You may use `ParameterResolver` extensions with `@ParameterizedClass` constructors. +However, if constructor injection is used, constructor parameters that are resolved by +argument sources need to come first in the parameter list. Values from argument sources +are not resolved for regular lifecycle methods (e.g. `@BeforeEach`). + +In addition to regular lifecycle methods, parameterized classes may declare +`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` lifecycle +methods that are called once before/after each invocation of the parameterized class. +These methods must be `static` unless the parameterized class is configured to use +`@TestInstance(Lifecycle.PER_CLASS)` (see xref:writing-tests/test-instance-lifecycle.adoc[]). + +These lifecycle methods may optionally declare parameters that are resolved depending on +the setting of the `injectArguments` annotation attribute. If it is set to `false`, the +parameters must be resolved by other registered {ParameterResolver} extensions. If the +attribute is set to `true` (the default), the method may declare parameters that match the +arguments of the parameterized class (see the Javadoc of +`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` for +details). This may, for example, be used to initialize the used arguments as demonstrated +by the following example. + +[source,java,indent=0] +.Using parameterized class lifecycle methods +---- +include::example$java/example/ParameterizedLifecycleDemo.java[tags=example] +---- +<1> Initialization of the argument _before_ each invocation of the parameterized class +<2> Usage of the previously initialized argument in a test method +<3> Validation and cleanup of the argument _after_ each invocation of the parameterized + class diff --git a/documentation/modules/ROOT/pages/writing-tests/repeated-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/repeated-tests.adoc new file mode 100644 index 000000000000..d81d9b40dd13 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/repeated-tests.adoc @@ -0,0 +1,174 @@ += Repeated Tests + +JUnit Jupiter provides the ability to repeat a test a specified number of times by +annotating a method with `@RepeatedTest` and specifying the total number of repetitions +desired. Each invocation of a repeated test behaves like the execution of a regular +`@Test` method with full support for the same lifecycle callbacks and extensions. + +The following example demonstrates how to declare a test named `repeatedTest()` that +will be automatically repeated 10 times. + +[source,java] +---- +@RepeatedTest(10) +void repeatedTest() { + // ... +} +---- + +`@RepeatedTest` can be configured with a failure threshold which signifies the number of +failures after which remaining repetitions will be automatically skipped. Set the +`failureThreshold` attribute to a positive number less than the total number of +repetitions in order to skip the invocations of remaining repetitions after the specified +number of failures has been encountered. + +For example, if you are using `@RepeatedTest` to repeatedly invoke a test that you suspect +to be _flaky_, a single failure is sufficient to demonstrate that the test is flaky, and +there is no need to invoke the remaining repetitions. To support that specific use case, +set `failureThreshold = 1`. You can alternatively set the threshold to a number greater +than 1 depending on your use case. + +By default, the `failureThreshold` attribute is set to `Integer.MAX_VALUE`, signaling that +no failure threshold will be applied, which effectively means that the specified number of +repetitions will be invoked regardless of whether any repetitions fail. + +WARNING: If the repetitions of a `@RepeatedTest` method are executed in parallel, no +guarantees can be made regarding the failure threshold. It is therefore recommended that a +`@RepeatedTest` method be annotated with `@Execution(SAME_THREAD)` when parallel execution +is configured. See xref:writing-tests/parallel-execution.adoc[] for further details. + +In addition to specifying the number of repetitions and failure threshold, a custom +display name can be configured for each repetition via the `name` attribute of the +`@RepeatedTest` annotation. Furthermore, the display name can be a pattern composed of a +combination of static text and dynamic placeholders. The following placeholders are +currently supported. + +- `+{displayName}+`: display name of the `@RepeatedTest` method +- `+{currentRepetition}+`: the current repetition count +- `+{totalRepetitions}+`: the total number of repetitions + +The default display name for a given repetition is generated based on the following +pattern: `"repetition +{currentRepetition}+ of +{totalRepetitions}+"`.Thus, the display +names for individual repetitions of the previous `repeatedTest()` example would be: +`repetition 1 of 10`, `repetition 2 of 10`, etc.If you would like the display name of +the `@RepeatedTest` method included in the name of each repetition, you can define your +own custom pattern or use the predefined `RepeatedTest.LONG_DISPLAY_NAME` pattern.The +latter is equal to `"+{displayName}+ :: repetition +{currentRepetition}+ of ++{totalRepetitions}+"` which results in display names for individual repetitions like +`repeatedTest() :: repetition 1 of 10`, `repeatedTest() :: repetition 2 of 10`, etc. + +In order to retrieve information about the current repetition, the total number of +repetitions, the number of repetitions that have failed, and the failure threshold, a +developer can choose to have an instance of `{RepetitionInfo}` injected into a +`@RepeatedTest`, `@BeforeEach`, or `@AfterEach` method. + +[[examples]] +== Repeated Test Examples + +The `RepeatedTestsDemo` class at the end of this section demonstrates several examples of +repeated tests. + +The `repeatedTest()` method is identical to the example from the previous section; whereas, +`repeatedTestWithRepetitionInfo()` demonstrates how to have an instance of +`RepetitionInfo` injected into a test to access the total number of repetitions for the +current repeated test. + +`repeatedTestWithFailureThreshold()` demonstrates how to set a failure threshold and +simulates an unexpected failure for every second repetition.The resulting behavior can be +viewed in the `ConsoleLauncher` output at the end of this section. + +The next two methods demonstrate how to include a custom `@DisplayName` for the +`@RepeatedTest` method in the display name of each repetition. `customDisplayName()` +combines a custom display name with a custom pattern and then uses `TestInfo` to verify +the format of the generated display name. `Repeat!` is the `+{displayName}+` which comes +from the `@DisplayName` declaration, and `1/1` comes from +`+{currentRepetition}+/+{totalRepetitions}+`.In contrast, +`customDisplayNameWithLongPattern()` uses the aforementioned predefined +`RepeatedTest.LONG_DISPLAY_NAME` pattern. + +`repeatedTestInGerman()` demonstrates the ability to translate display names of repeated +tests into foreign languages -- in this case German, resulting in names for individual +repetitions such as: `Wiederholung 1 von 5`, `Wiederholung 2 von 5`, etc. + +Since the `beforeEach()` method is annotated with `@BeforeEach` it will get executed +before each repetition of each repeated test. By having the `TestInfo` and +`RepetitionInfo` injected into the method, we see that it's possible to obtain +information about the currently executing repeated test. Executing `RepeatedTestsDemo` +with the `INFO` log level enabled results in the following output. + +.... +INFO: About to execute repetition 1 of 10 for repeatedTest +INFO: About to execute repetition 2 of 10 for repeatedTest +INFO: About to execute repetition 3 of 10 for repeatedTest +INFO: About to execute repetition 4 of 10 for repeatedTest +INFO: About to execute repetition 5 of 10 for repeatedTest +INFO: About to execute repetition 6 of 10 for repeatedTest +INFO: About to execute repetition 7 of 10 for repeatedTest +INFO: About to execute repetition 8 of 10 for repeatedTest +INFO: About to execute repetition 9 of 10 for repeatedTest +INFO: About to execute repetition 10 of 10 for repeatedTest +INFO: About to execute repetition 1 of 5 for repeatedTestWithRepetitionInfo +INFO: About to execute repetition 2 of 5 for repeatedTestWithRepetitionInfo +INFO: About to execute repetition 3 of 5 for repeatedTestWithRepetitionInfo +INFO: About to execute repetition 4 of 5 for repeatedTestWithRepetitionInfo +INFO: About to execute repetition 5 of 5 for repeatedTestWithRepetitionInfo +INFO: About to execute repetition 1 of 8 for repeatedTestWithFailureThreshold +INFO: About to execute repetition 2 of 8 for repeatedTestWithFailureThreshold +INFO: About to execute repetition 3 of 8 for repeatedTestWithFailureThreshold +INFO: About to execute repetition 4 of 8 for repeatedTestWithFailureThreshold +INFO: About to execute repetition 1 of 1 for customDisplayName +INFO: About to execute repetition 1 of 1 for customDisplayNameWithLongPattern +INFO: About to execute repetition 1 of 5 for repeatedTestInGerman +INFO: About to execute repetition 2 of 5 for repeatedTestInGerman +INFO: About to execute repetition 3 of 5 for repeatedTestInGerman +INFO: About to execute repetition 4 of 5 for repeatedTestInGerman +INFO: About to execute repetition 5 of 5 for repeatedTestInGerman +.... + +[source,java] +---- +include::example$java/example/RepeatedTestsDemo.java[tags=user_guide] +---- + +When using the `ConsoleLauncher` with the unicode theme enabled, execution of +`RepeatedTestsDemo` results in the following output to the console. + +.... +├─ RepeatedTestsDemo ✔ +│ ├─ repeatedTest() ✔ +│ │ ├─ repetition 1 of 10 ✔ +│ │ ├─ repetition 2 of 10 ✔ +│ │ ├─ repetition 3 of 10 ✔ +│ │ ├─ repetition 4 of 10 ✔ +│ │ ├─ repetition 5 of 10 ✔ +│ │ ├─ repetition 6 of 10 ✔ +│ │ ├─ repetition 7 of 10 ✔ +│ │ ├─ repetition 8 of 10 ✔ +│ │ ├─ repetition 9 of 10 ✔ +│ │ └─ repetition 10 of 10 ✔ +│ ├─ repeatedTestWithRepetitionInfo(RepetitionInfo) ✔ +│ │ ├─ repetition 1 of 5 ✔ +│ │ ├─ repetition 2 of 5 ✔ +│ │ ├─ repetition 3 of 5 ✔ +│ │ ├─ repetition 4 of 5 ✔ +│ │ └─ repetition 5 of 5 ✔ +│ ├─ repeatedTestWithFailureThreshold(RepetitionInfo) ✔ +│ │ ├─ repetition 1 of 8 ✔ +│ │ ├─ repetition 2 of 8 ✘ Boom! +│ │ ├─ repetition 3 of 8 ✔ +│ │ ├─ repetition 4 of 8 ✘ Boom! +│ │ ├─ repetition 5 of 8 ↷ Failure threshold [2] exceeded +│ │ ├─ repetition 6 of 8 ↷ Failure threshold [2] exceeded +│ │ ├─ repetition 7 of 8 ↷ Failure threshold [2] exceeded +│ │ └─ repetition 8 of 8 ↷ Failure threshold [2] exceeded +│ ├─ Repeat! ✔ +│ │ └─ Repeat! 1/1 ✔ +│ ├─ Details... ✔ +│ │ └─ Details... :: repetition 1 of 1 ✔ +│ └─ repeatedTestInGerman() ✔ +│ ├─ Wiederholung 1 von 5 ✔ +│ ├─ Wiederholung 2 von 5 ✔ +│ ├─ Wiederholung 3 von 5 ✔ +│ ├─ Wiederholung 4 von 5 ✔ +│ └─ Wiederholung 5 von 5 ✔ +.... diff --git a/documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc b/documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc new file mode 100644 index 000000000000..ad5bb6c07300 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc @@ -0,0 +1,14 @@ += Tagging and Filtering + +Test classes and methods can be tagged via the `@Tag` annotation. Those tags can later be +used to filter xref:running-tests/intro.adoc[test discovery and execution]. Please refer to the +xref:running-tests/tags.adoc[] section for more information about tag support in the JUnit +Platform. + +[source,java,indent=0] +---- +include::example$java/example/TaggingDemo.java[tags=user_guide] +---- + +TIP: See xref:writing-tests/annotations.adoc#annotations[Meta-Annotations and Composed Annotations] for examples demonstrating how to create +custom annotations for tags. diff --git a/documentation/modules/ROOT/pages/writing-tests/test-classes-and-methods.adoc b/documentation/modules/ROOT/pages/writing-tests/test-classes-and-methods.adoc new file mode 100644 index 000000000000..0dd9e086fc48 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/test-classes-and-methods.adoc @@ -0,0 +1,73 @@ += Test Classes and Methods + +Test methods and lifecycle methods may be declared locally within the current test class, +inherited from superclasses, or inherited from interfaces (see +xref:writing-tests/test-interfaces-and-default-methods.adoc[]). In addition, test methods and +lifecycle methods must not be `abstract` and must not return a value (except `@TestFactory` +methods which are required to return a value). + +[NOTE] +.Class and method visibility +==== +Test classes, test methods, and lifecycle methods are not required to be `public`, but +they must _not_ be `private`. + +It is generally recommended to omit the `public` modifier for test classes, test methods, +and lifecycle methods unless there is a technical reason for doing so – for example, when +a test class is extended by a test class in another package. Another technical reason for +making classes and methods `public` is to simplify testing on the module path when using +the Java Module System. +==== + +[NOTE] +.Field and method inheritance +==== +Fields in test classes are inherited. For example, a `@TempDir` field from a superclass +will always be applied in a subclass. + +Test methods and lifecycle methods are inherited unless they are overridden according to +the visibility rules of the Java language. For example, a `@Test` method from a superclass +will always be applied in a subclass unless the subclass explicitly overrides the method. +Similarly, if a package-private `@Test` method is declared in a superclass that resides in +a different package than the subclass, that `@Test` method will always be applied in the +subclass since the subclass cannot override a package-private method from a superclass in +a different package. + +See also: xref:extensions/supported-utilities-in-extensions.adoc#search-semantics[Field and Method Search Semantics] +==== + +The following test class demonstrates the use of `@Test` methods and all supported +lifecycle methods. For further information on runtime semantics, see +xref:writing-tests/test-execution-order.adoc[] and +xref:extensions/relative-execution-order-of-user-code-and-extensions.adoc#wrapping-behavior[Wrapping Behavior of Callbacks]. + +[source,java,indent=0] +.A standard Java test class +---- +include::example$java/example/StandardTests.java[tags=user_guide] +---- + +It is also possible to use Java `record` classes as test classes as illustrated by the +following example. + +[source,java,indent=0] +.A test class written as a Java record +---- +include::example$java/example/MyFirstJUnitJupiterRecordTests.java[tags=user_guide] +---- + +Test and lifecycle methods may be written in Kotlin and may optionally use the `suspend` +keyword for testing code using coroutines. + +[source,kotlin] +.A test class written in Kotlin +---- +include::example$kotlin/example/KotlinCoroutinesDemo.kt[tags=user_guide] +---- + +NOTE: Using suspending functions as test or lifecycle methods requires +https://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-stdlib[`kotlin-stdlib`], +https://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-reflect[`kotlin-reflect`], +and +https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core[`kotlinx-coroutines-core`] +to be present on the classpath or module path. diff --git a/documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc b/documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc new file mode 100644 index 000000000000..20bae4e323fb --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc @@ -0,0 +1,138 @@ += Test Execution Order + +By default, test classes and methods will be ordered using an algorithm that is +deterministic but intentionally nonobvious. This ensures that subsequent runs of a test +suite execute test classes and test methods in the same order, thereby allowing for +repeatable builds. + +NOTE: See xref:writing-tests/definitions.adoc[] for a definition of _test method_ and _test class_. + +[[methods]] +== Method Order + +Although true _unit tests_ typically should not rely on the order in which they are +executed, there are times when it is necessary to enforce a specific test method execution +order -- for example, when writing _integration tests_ or _functional tests_ where the +sequence of the tests is important, especially in conjunction with +`@TestInstance(Lifecycle.PER_CLASS)`. + +To control the order in which test methods are executed, annotate your test class or test +interface with `{TestMethodOrder}` and specify the desired `{MethodOrderer}` +implementation. You can implement your own custom `MethodOrderer` or use one of the +following built-in `MethodOrderer` implementations. + +* `{MethodOrderer_DisplayName}`: sorts test methods _alphanumerically_ based on their + display names (see xref:writing-tests/display-names.adoc#generator-precedence-rules[display name + generation precedence rules]) +* `{MethodOrderer_MethodName}`: sorts test methods _alphanumerically_ based on their names + and formal parameter lists +* `{MethodOrderer_OrderAnnotation}`: sorts test methods _numerically_ based on values + specified via the `{Order}` annotation +* `{MethodOrderer_Random}`: orders test methods _pseudo-randomly_ and supports + configuration of a custom _seed_ + +The `MethodOrderer` configured on a test class is inherited by the `@Nested` test classes +it contains, recursively. If you want to avoid that a `@Nested` test class uses the same +`MethodOrderer` as its enclosing class, you can specify `{MethodOrderer_Default}` together +with `{TestMethodOrder}`. + +NOTE: See also: xref:extensions/relative-execution-order-of-user-code-and-extensions.adoc#wrapping-behavior[Wrapping Behavior of Callbacks] + +The following example demonstrates how to guarantee that test methods are executed in the +order specified via the `@Order` annotation. + +[source,java,indent=0] +---- +include::example$java/example/OrderedTestsDemo.java[tags=user_guide] +---- + +[[methods-default]] +=== Setting the Default Method Orderer + +You can use the `junit.jupiter.testmethod.order.default` xref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the fully qualified class name of the +`{MethodOrderer}` you would like to use by default. Just like for the orderer configured +via the `{TestMethodOrder}` annotation, the supplied class has to implement the +`MethodOrderer` interface. The default orderer will be used for all tests unless the +`@TestMethodOrder` annotation is present on an enclosing test class or test interface. + +For example, to use the `{MethodOrderer_OrderAnnotation}` method orderer by default, you +should set the configuration parameter to the corresponding fully qualified class name +(e.g., in `src/test/resources/junit-platform.properties`): + +[source,properties,indent=0] +---- +junit.jupiter.testmethod.order.default = \ + org.junit.jupiter.api.MethodOrderer$OrderAnnotation +---- + +Similarly, you can specify the fully qualified name of any custom class that implements +`MethodOrderer`. + +[[classes]] +== Class Order + +Although test classes typically should not rely on the order in which they are executed, +there are times when it is desirable to enforce a specific test class execution order. You +may wish to execute test classes in a random order to ensure there are no accidental +dependencies between test classes, or you may wish to order test classes to optimize build +time as outlined in the following scenarios. + +* Run previously failing tests and faster tests first: "fail fast" mode +* With parallel execution enabled, schedule longer tests first: "shortest test plan + execution duration" mode +* Various other use cases + +To configure test class execution order _globally_ for the entire test suite, use the +`junit.jupiter.testclass.order.default` xref:running-tests/configuration-parameters.adoc[configuration +parameter] to specify the fully qualified class name of the `{ClassOrderer}` you would +like to use. The supplied class must implement the `ClassOrderer` interface. + +You can implement your own custom `ClassOrderer` or use one of the following built-in +`ClassOrderer` implementations. + +* `{ClassOrderer_ClassName}`: sorts test classes _alphanumerically_ based on their fully + qualified class names +* `{ClassOrderer_DisplayName}`: sorts test classes _alphanumerically_ based on their + display names (see xref:writing-tests/display-names.adoc#generator-precedence-rules[display name + generation precedence rules]) +* `{ClassOrderer_OrderAnnotation}`: sorts test classes _numerically_ based on values + specified via the `{Order}` annotation +* `{ClassOrderer_Random}`: orders test classes _pseudo-randomly_ and supports + configuration of a custom _seed_ + +For example, for the `@Order` annotation to be honored on _test classes_, you should +configure the `{ClassOrderer_OrderAnnotation}` class orderer using the configuration +parameter with the corresponding fully qualified class name (e.g., in +`src/test/resources/junit-platform.properties`): + +[source,properties,indent=0] +---- +junit.jupiter.testclass.order.default = \ + org.junit.jupiter.api.ClassOrderer$OrderAnnotation +---- + +The configured `ClassOrderer` will be applied to all top-level test classes (including +`static` nested test classes) and `@Nested` test classes. + +NOTE: Top-level test classes will be ordered relative to each other; whereas, `@Nested` +test classes will be ordered relative to other `@Nested` test classes sharing the same +_enclosing class_. + +To configure test class execution order _locally_ for `@Nested` test classes, declare the +`{TestClassOrder}` annotation on the enclosing class for the `@Nested` test classes you +want to order, and supply a class reference to the `ClassOrderer` implementation you would +like to use directly in the `@TestClassOrder` annotation. The configured `ClassOrderer` +will be applied recursively to `@Nested` test classes and their `@Nested` test classes. +If you want to avoid that a `@Nested` test class uses the same `ClassOrderer` as its +enclosing class, you can specify `{ClassOrderer_Default}` together with `@TestClassOrder`. +Note that a local `@TestClassOrder` declaration always overrides an inherited +`@TestClassOrder` declaration or a `ClassOrderer` configured globally via the +`junit.jupiter.testclass.order.default` configuration parameter. + +The following example demonstrates how to guarantee that `@Nested` test classes are +executed in the order specified via the `@Order` annotation. + +[source,java,indent=0] +---- +include::example$java/example/OrderedNestedTestClassesDemo.java[tags=user_guide] +---- diff --git a/documentation/modules/ROOT/pages/writing-tests/test-instance-lifecycle.adoc b/documentation/modules/ROOT/pages/writing-tests/test-instance-lifecycle.adoc new file mode 100644 index 000000000000..aa12cdb745b7 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/test-instance-lifecycle.adoc @@ -0,0 +1,62 @@ += Test Instance Lifecycle + +In order to allow individual test methods to be executed in isolation and to avoid +unexpected side effects due to mutable test instance state, JUnit creates a new instance +of each test class before executing each _test method_ (see +xref:writing-tests/definitions.adoc[]). This "per-method" test instance lifecycle is the default +behavior in JUnit Jupiter and is analogous to all previous versions of JUnit. + +NOTE: Please note that the test class will still be instantiated if a given _test method_ +is _disabled_ via a xref:writing-tests/conditional-test-execution.adoc[condition] (e.g., `@Disabled`, +`@DisabledOnOs`, etc.) even when the "per-method" test instance lifecycle mode is active. + +If you would prefer that JUnit Jupiter execute all test methods on the same test +instance, annotate your test class with `@TestInstance(Lifecycle.PER_CLASS)`. When using +this mode, a new test instance will be created once per test class. Thus, if your test +methods rely on state stored in instance variables, you may need to reset that state in +`@BeforeEach` or `@AfterEach` methods. + +The "per-class" mode has some additional benefits over the default "per-method" mode. +Specifically, with the "per-class" mode it becomes possible to declare `@BeforeAll` and +`@AfterAll` on non-static methods as well as on interface `default` methods. + +If you are authoring tests using the Kotlin programming language, you may also find it +easier to implement non-static `@BeforeAll` and `@AfterAll` lifecycle methods as well as +`@MethodSource` factory methods by switching to the "per-class" test instance lifecycle +mode. + +[[default]] +== Changing the Default Test Instance Lifecycle + +If a test class or test interface is not annotated with `@TestInstance`, JUnit Jupiter +will use a _default_ lifecycle mode. The standard _default_ mode is `PER_METHOD`; +however, it is possible to change the _default_ for the execution of an entire test plan. +To change the default test instance lifecycle mode, set the +`junit.jupiter.testinstance.lifecycle.default` _configuration parameter_ to the name of +an enum constant defined in `TestInstance.Lifecycle`, ignoring case. This can be supplied +as a JVM system property, as a _configuration parameter_ in the +`LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform +configuration file (see xref:running-tests/configuration-parameters.adoc[] for details). + +For example, to set the default test instance lifecycle mode to `Lifecycle.PER_CLASS`, +you can start your JVM with the following system property. + +`-Djunit.jupiter.testinstance.lifecycle.default=per_class` + +Note, however, that setting the default test instance lifecycle mode via the JUnit +Platform configuration file is a more robust solution since the configuration file can be +checked into a version control system along with your project and can therefore be used +within IDEs and your build software. + +To set the default test instance lifecycle mode to `Lifecycle.PER_CLASS` via the JUnit +Platform configuration file, create a file named `junit-platform.properties` in the root +of the class path (e.g., `src/test/resources`) with the following content. + +`junit.jupiter.testinstance.lifecycle.default = per_class` + +WARNING: Changing the _default_ test instance lifecycle mode can lead to unpredictable +results and fragile builds if not applied consistently. For example, if the build +configures "per-class" semantics as the default but tests in the IDE are executed using +"per-method" semantics, that can make it difficult to debug errors that occur on the +build server. It is therefore recommended to change the default in the JUnit Platform +configuration file instead of via a JVM system property. diff --git a/documentation/modules/ROOT/pages/writing-tests/test-interfaces-and-default-methods.adoc b/documentation/modules/ROOT/pages/writing-tests/test-interfaces-and-default-methods.adoc new file mode 100644 index 000000000000..d5425fe0767f --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/test-interfaces-and-default-methods.adoc @@ -0,0 +1,77 @@ += Test Interfaces and Default Methods + +JUnit Jupiter allows `@Test`, `@RepeatedTest`, `@ParameterizedTest`, `@TestFactory`, +`@TestTemplate`, `@BeforeEach`, and `@AfterEach` to be declared on interface `default` +methods. `@BeforeAll` and `@AfterAll` can either be declared on `static` methods in a +test interface or on interface `default` methods _if_ the test interface or test class is +annotated with `@TestInstance(Lifecycle.PER_CLASS)` (see +xref:writing-tests/test-instance-lifecycle.adoc[]). Here are some examples. + +[source,java] +---- +include::example$java/example/testinterface/TestLifecycleLogger.java[tags=user_guide] +---- + +[source,java] +---- +include::example$java/example/testinterface/TestInterfaceDynamicTestsDemo.java[tags=user_guide] +---- + +`@ExtendWith` and `@Tag` can be declared on a test interface so that classes that +implement the interface automatically inherit its tags and extensions. See +xref:extensions/test-lifecycle-callbacks.adoc#before-after-execution[Before and After Test Execution Callbacks] for the source code of the +xref:extensions/test-lifecycle-callbacks.adoc#timing-extension[TimingExtension]. + +[source,java] +---- +include::example$java/example/testinterface/TimeExecutionLogger.java[tags=user_guide] +---- + +In your test class you can then implement these test interfaces to have them applied. + +[source,java] +---- +include::example$java/example/testinterface/TestInterfaceDemo.java[tags=user_guide] +---- + +Running the `TestInterfaceDemo` results in output similar to the following: + +.... +INFO example.TestLifecycleLogger - Before all tests +INFO example.TestLifecycleLogger - About to execute [dynamicTestsForPalindromes()] +INFO example.TimingExtension - Method [dynamicTestsForPalindromes] took 19 ms. +INFO example.TestLifecycleLogger - Finished executing [dynamicTestsForPalindromes()] +INFO example.TestLifecycleLogger - About to execute [isEqualValue()] +INFO example.TimingExtension - Method [isEqualValue] took 1 ms. +INFO example.TestLifecycleLogger - Finished executing [isEqualValue()] +INFO example.TestLifecycleLogger - After all tests +.... + +Another possible application of this feature is to write tests for interface contracts. +For example, you can write tests for how implementations of `Object.equals` or +`Comparable.compareTo` should behave as follows. + +[source,java] +---- +include::example$java/example/defaultmethods/Testable.java[tags=user_guide] +---- + +[source,java] +---- +include::example$java/example/defaultmethods/EqualsContract.java[tags=user_guide] +---- + +[source,java] +---- +include::example$java/example/defaultmethods/ComparableContract.java[tags=user_guide] +---- + +In your test class you can then implement both contract interfaces thereby inheriting the +corresponding tests. Of course you'll have to implement the abstract methods. + +[source,java] +---- +include::example$java/example/defaultmethods/StringTests.java[tags=user_guide] +---- + +NOTE: The above tests are merely meant as examples and therefore not complete. diff --git a/documentation/modules/ROOT/pages/writing-tests/test-templates.adoc b/documentation/modules/ROOT/pages/writing-tests/test-templates.adoc new file mode 100644 index 000000000000..c1a9426b6e1f --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/test-templates.adoc @@ -0,0 +1,13 @@ += Test Templates + +A `{TestTemplate}` method is not a regular test case but rather a template for a test +case. As such, it is designed to be invoked multiple times depending on the number of +invocation contexts returned by the registered providers. Thus, it must be used in +conjunction with a registered `{TestTemplateInvocationContextProvider}` extension. Each +invocation of a test template method behaves like the execution of a regular `@Test` +method with full support for the same lifecycle callbacks and extensions. Please refer to +xref:extensions/providing-invocation-contexts-for-test-templates.adoc[] for usage examples. + +NOTE: xref:writing-tests/repeated-tests.adoc[] and +xref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Tests] are built-in specializations of +test templates. diff --git a/documentation/modules/ROOT/pages/writing-tests/timeouts.adoc b/documentation/modules/ROOT/pages/writing-tests/timeouts.adoc new file mode 100644 index 000000000000..531573c1bc78 --- /dev/null +++ b/documentation/modules/ROOT/pages/writing-tests/timeouts.adoc @@ -0,0 +1,159 @@ += Timeouts + +The `@Timeout` annotation allows one to declare that a test, test factory, test template, +or lifecycle method should fail if its execution time exceeds a given duration. The time +unit for the duration defaults to seconds but is configurable. + +The following example shows how `@Timeout` is applied to lifecycle and test methods. + +[source,java] +---- +include::example$java/example/TimeoutDemo.java[tags=user_guide] +---- + +To apply the same timeout to all test methods within a test class and all of its `@Nested` +classes, you can declare the `@Timeout` annotation at the class level. It will then be +applied to all test, test factory, and test template methods within that class and its +`@Nested` classes unless overridden by a `@Timeout` annotation on a specific method or +`@Nested` class. Please note that `@Timeout` annotations declared at the class level are +not applied to lifecycle methods. + +Declaring `@Timeout` on a `@TestFactory` method checks that the factory method returns +within the specified duration but does not verify the execution time of each individual +`DynamicTest` generated by the factory. Please use +`assertTimeout()` or `assertTimeoutPreemptively()` for that purpose. + +If `@Timeout` is present on a `@TestTemplate` method — for example, a `@RepeatedTest` or +`@ParameterizedTest` — each invocation will have the given timeout applied to it. + +[[thread-mode]] +== Thread mode + +The timeout can be applied using one of the following three thread modes: `SAME_THREAD`, +`SEPARATE_THREAD`, or `INFERRED`. + +When `SAME_THREAD` is used, the execution of the annotated method proceeds in the main +thread of the test. If the timeout is exceeded, the main thread is interrupted from +another thread. This is done to ensure interoperability with frameworks such as Spring +that make use of mechanisms that are sensitive to the currently running thread — for +example, `ThreadLocal` transaction management. + +On the contrary when `SEPARATE_THREAD` is used, like the `assertTimeoutPreemptively()` +assertion, the execution of the annotated method proceeds in a separate thread, this +can lead to undesirable side effects, see xref:writing-tests/assertions.adoc#preemptive-timeouts[Preemptive Timeouts with `assertTimeoutPreemptively()`]. + +When `INFERRED` (default) thread mode is used, the thread mode is resolved via the +`junit.jupiter.execution.timeout.thread.mode.default` configuration parameter. If the +provided configuration parameter is invalid or not present then `SAME_THREAD` is used as +fallback. + +[[default-timeouts]] +== Default Timeouts + +The following xref:running-tests/configuration-parameters.adoc[configuration parameters] can be used to +specify default timeouts for all methods of a certain category unless they or an enclosing +test class is annotated with `@Timeout`: + +`junit.jupiter.execution.timeout.default`:: + Default timeout for all testable and lifecycle methods +`junit.jupiter.execution.timeout.testable.method.default`:: + Default timeout for all testable methods +`junit.jupiter.execution.timeout.test.method.default`:: + Default timeout for `@Test` methods +`junit.jupiter.execution.timeout.testtemplate.method.default`:: + Default timeout for `@TestTemplate` methods +`junit.jupiter.execution.timeout.testfactory.method.default`:: + Default timeout for `@TestFactory` methods +`junit.jupiter.execution.timeout.lifecycle.method.default`:: + Default timeout for all lifecycle methods +`junit.jupiter.execution.timeout.beforeall.method.default`:: + Default timeout for `@BeforeAll` methods +`junit.jupiter.execution.timeout.beforeeach.method.default`:: + Default timeout for `@BeforeEach` methods +`junit.jupiter.execution.timeout.aftereach.method.default`:: + Default timeout for `@AfterEach` methods +`junit.jupiter.execution.timeout.afterall.method.default`:: + Default timeout for `@AfterAll` methods + +More specific configuration parameters override less specific ones. For example, +`junit.jupiter.execution.timeout.test.method.default` overrides +`junit.jupiter.execution.timeout.testable.method.default` which overrides +`junit.jupiter.execution.timeout.default`. + +The values of such configuration parameters must be in the following, case-insensitive +format: ` [ns|μs|ms|s|m|h|d]`. The space between the number and the unit may be +omitted. Specifying no unit is equivalent to using seconds. + +.Example timeout configuration parameter values +[cols="20,80"] +|=== +| Parameter value | Equivalent annotation + +| `42` | `@Timeout(42)` +| `42 ns` | `@Timeout(value = 42, unit = NANOSECONDS)` +| `42 μs` | `@Timeout(value = 42, unit = MICROSECONDS)` +| `42 ms` | `@Timeout(value = 42, unit = MILLISECONDS)` +| `42 s` | `@Timeout(value = 42, unit = SECONDS)` +| `42 m` | `@Timeout(value = 42, unit = MINUTES)` +| `42 h` | `@Timeout(value = 42, unit = HOURS)` +| `42 d` | `@Timeout(value = 42, unit = DAYS)` +|=== + + +[[polling]] +== Using @Timeout for Polling Tests + +When dealing with asynchronous code, it is common to write tests that poll while waiting +for something to happen before performing any assertions. In some cases you can rewrite +the logic to use a `CountDownLatch` or another synchronization mechanism, but sometimes +that is not possible — for example, if the subject under test sends a message to a channel +in an external message broker and assertions cannot be performed until the message has +been successfully sent through the channel. Asynchronous tests like these require some +form of timeout to ensure they don't hang the test suite by executing indefinitely, as +would be the case if an asynchronous message never gets successfully delivered. + +By configuring a timeout for an asynchronous test that polls, you can ensure that the test +does not execute indefinitely. The following example demonstrates how to achieve this with +JUnit Jupiter's `@Timeout` annotation. This technique can be used to implement "poll +until" logic very easily. + +[source,java] +---- +include::example$java/example/PollingTimeoutDemo.java[tags=user_guide,indent=0] +---- + +NOTE: If you need more control over polling intervals and greater flexibility with +asynchronous tests, consider using a dedicated library such as +link:https://github.com/awaitility/awaitility[Awaitility]. + + +[[debugging]] +== Debugging Timeouts + +Registered xref:extensions/pre-interrupt-callback.adoc[] extensions are called prior to invoking +`Thread.interrupt()` on the thread that is executing the timed out method. This allows to +inspect the application state and output additional information that might be helpful for +diagnosing the cause of a timeout. + + +[[debugging-thread-dump]] +=== Thread Dump on Timeout + +JUnit registers a default implementation of the xref:extensions/pre-interrupt-callback.adoc[] +extension point that dumps the stacks of all threads to `System.out` if enabled by setting +the `junit.jupiter.execution.timeout.threaddump.enabled` +xref:running-tests/configuration-parameters.adoc[configuration parameter] to `true`. + + +[[mode]] +== Disable @Timeout Globally + +When stepping through your code in a debug session, a fixed timeout limit may influence +the result of the test, e.g. mark the test as failed although all assertions were met. + +JUnit Jupiter supports the `junit.jupiter.execution.timeout.mode` configuration parameter +to configure when timeouts are applied. There are three modes: `enabled`, `disabled`, +and `disabled_on_debug`. The default mode is `enabled`. +A VM runtime is considered to run in debug mode when one of its input parameters starts +with `-agentlib:jdwp` or `-Xrunjdwp`. +This heuristic is queried by the `disabled_on_debug` mode. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0.adoc b/documentation/modules/ROOT/partials/release-notes/release-notes-6.0.0.adoc similarity index 97% rename from documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0.adoc rename to documentation/modules/ROOT/partials/release-notes/release-notes-6.0.0.adoc index 49435a023433..a4271f992d78 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0.adoc +++ b/documentation/modules/ROOT/partials/release-notes/release-notes-6.0.0.adoc @@ -1,4 +1,4 @@ -[[release-notes-6.0.0]] +[[v6.0.0]] == 6.0.0 *Date of Release:* September 30, 2025 diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.1.adoc b/documentation/modules/ROOT/partials/release-notes/release-notes-6.0.1.adoc similarity index 82% rename from documentation/src/docs/asciidoc/release-notes/release-notes-6.0.1.adoc rename to documentation/modules/ROOT/partials/release-notes/release-notes-6.0.1.adoc index 1972f8a9971e..b48b6a74c4f1 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.1.adoc +++ b/documentation/modules/ROOT/partials/release-notes/release-notes-6.0.1.adoc @@ -1,4 +1,4 @@ -[[release-notes-6.0.1]] +[[v6.0.1]] == 6.0.1 *Date of Release:* October 31, 2025 @@ -10,25 +10,25 @@ link:{junit-framework-repo}+/milestone/110?closed=1+[6.0.1] milestone page in th repository on GitHub. -[[release-notes-6.0.1-junit-platform]] +[[v6.0.1-junit-platform]] === JUnit Platform -[[release-notes-6.0.1-junit-platform-bug-fixes]] +[[v6.0.1-junit-platform-bug-fixes]] ==== Bug Fixes * The `jdk.jfr` package is now an optional import when using the `junit-platform-launcher` as an OSGi bundle. -[[release-notes-6.0.1-junit-platform-new-features-and-improvements]] +[[v6.0.1-junit-platform-new-features-and-improvements]] ==== New Features and Improvements * Legacy documentation regarding Java 8 compatibility has been removed from the User Guide. -[[release-notes-6.0.1-junit-jupiter]] +[[v6.0.1-junit-jupiter]] === JUnit Jupiter -[[release-notes-6.0.1-junit-jupiter-bug-fixes]] +[[v6.0.1-junit-jupiter-bug-fixes]] ==== Bug Fixes * A regression introduced in version 6.0.0 caused an exception when using `@CsvSource` or @@ -44,13 +44,13 @@ repository on GitHub. * Fix support for test methods with the same signature as package-private methods declared in super classes in different packages. -[[release-notes-6.0.1-junit-jupiter-deprecations-and-breaking-changes]] +[[v6.0.1-junit-jupiter-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * The `org.junit.jupiter.migrationsupport` module descriptor has been marked as deprecated for removal. -[[release-notes-6.0.1-junit-jupiter-new-features-and-improvements]] +[[v6.0.1-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements * The `@CsvSource` and `@CsvFileSource` annotations now allow specifying a custom comment @@ -61,10 +61,10 @@ repository on GitHub. without having to cast class literals to `Class<@Nullable T>`. -[[release-notes-6.0.1-junit-vintage]] +[[v6.0.1-junit-vintage]] === JUnit Vintage -[[release-notes-6.0.1-junit-vintage-new-features-and-improvements]] +[[v6.0.1-junit-vintage-new-features-and-improvements]] ==== New Features and Improvements * Allow disabling the reporting of discovery issues by the JUnit Vintage engine (including diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.2.adoc b/documentation/modules/ROOT/partials/release-notes/release-notes-6.0.2.adoc similarity index 53% rename from documentation/src/docs/asciidoc/release-notes/release-notes-6.0.2.adoc rename to documentation/modules/ROOT/partials/release-notes/release-notes-6.0.2.adoc index 382420cc04bc..74a9291a1a3f 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.2.adoc +++ b/documentation/modules/ROOT/partials/release-notes/release-notes-6.0.2.adoc @@ -1,4 +1,4 @@ -[[release-notes-6.0.2]] +[[v6.0.2]] == 6.0.2 *Date of Release:* ❓ @@ -10,59 +10,59 @@ link:{junit-framework-repo}+/milestone/113?closed=1+[6.0.2] milestone page in th repository on GitHub. -[[release-notes-6.0.2-junit-platform]] +[[v6.0.2-junit-platform]] === JUnit Platform -[[release-notes-6.0.2-junit-platform-bug-fixes]] +[[v6.0.2-junit-platform-bug-fixes]] ==== Bug Fixes * Make `ConsoleLauncher` compatible with JDK 26 by avoiding final field mutations. -[[release-notes-6.0.2-junit-platform-deprecations-and-breaking-changes]] +[[v6.0.2-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-6.0.2-junit-platform-new-features-and-improvements]] +[[v6.0.2-junit-platform-new-features-and-improvements]] ==== New Features and Improvements * ❓ -[[release-notes-6.0.2-junit-jupiter]] +[[v6.0.2-junit-jupiter]] === JUnit Jupiter -[[release-notes-6.0.2-junit-jupiter-bug-fixes]] +[[v6.0.2-junit-jupiter-bug-fixes]] ==== Bug Fixes * Allow using `@ResourceLock` on classes annotated with `@ClassTemplate` (or `@ParameterizedClass`). -[[release-notes-6.0.2-junit-jupiter-deprecations-and-breaking-changes]] +[[v6.0.2-junit-jupiter-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-6.0.2-junit-jupiter-new-features-and-improvements]] +[[v6.0.2-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements * ❓ -[[release-notes-6.0.2-junit-vintage]] +[[v6.0.2-junit-vintage]] === JUnit Vintage -[[release-notes-6.0.2-junit-vintage-bug-fixes]] +[[v6.0.2-junit-vintage-bug-fixes]] ==== Bug Fixes * ❓ -[[release-notes-6.0.2-junit-vintage-deprecations-and-breaking-changes]] +[[v6.0.2-junit-vintage-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-6.0.2-junit-vintage-new-features-and-improvements]] +[[v6.0.2-junit-vintage-new-features-and-improvements]] ==== New Features and Improvements * ❓ diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc b/documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-M1.adoc similarity index 84% rename from documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc rename to documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-M1.adoc index 7f489ef6713d..98a7fd44094c 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc +++ b/documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-M1.adoc @@ -1,4 +1,4 @@ -[[release-notes-6.1.0-M1]] +[[v6.1.0-M1]] == 6.1.0-M1 *Date of Release:* November 17, 2025 @@ -14,17 +14,17 @@ link:{junit-framework-repo}+/milestone/104?closed=1+[6.1.0-M1] milestone page in repository on GitHub. -[[release-notes-6.1.0-M1-junit-platform]] +[[v6.1.0-M1-junit-platform]] === JUnit Platform -[[release-notes-6.1.0-M1-junit-platform-deprecations-and-breaking-changes]] +[[v6.1.0-M1-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * Deprecate constructors for `ForkJoinPoolHierarchicalTestExecutorService` in favor of the new `ParallelHierarchicalTestExecutorServiceFactory` that also supports `WorkerThreadPoolHierarchicalTestExecutorService`. -[[release-notes-6.1.0-M1-junit-platform-new-features-and-improvements]] +[[v6.1.0-M1-junit-platform-new-features-and-improvements]] ==== New Features and Improvements * Support for creating a `ModuleSelector` from a `java.lang.Module` and using @@ -42,15 +42,15 @@ repository on GitHub. `false`. -[[release-notes-6.1.0-M1-junit-jupiter]] +[[v6.1.0-M1-junit-jupiter]] === JUnit Jupiter -[[release-notes-6.1.0-M1-junit-jupiter-new-features-and-improvements]] +[[v6.1.0-M1-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements * Introduce new module `org.junit.start` for writing and running tests. It simplifies using JUnit in compact source files together with a single `module import` statement. - Find an example at the <<../user-guide/#running-tests-source-launcher, User Guide>>. + Find an example at the xref:running-tests/source-launcher.adoc[User Guide]. * Introduce new `dynamicTest(Consumer)` factory method for dynamic tests. It allows configuring the `ExecutionMode` of the dynamic test in addition to its display name, test source URI, and executable. @@ -65,10 +65,10 @@ repository on GitHub. `junit.jupiter.execution.parallel.config.executor-service` configuration parameter to in order to add support for `WorkerThreadPoolHierarchicalTestExecutorService`. Please refer to the - <<../user-guide/index.adoc#writing-tests-parallel-execution-config-executor-service, User Guide>> + xref:writing-tests/parallel-execution.adoc#config-executor-service[User Guide] for details. -[[release-notes-6.1.0-M1-junit-vintage]] +[[v6.1.0-M1-junit-vintage]] === JUnit Vintage No changes. diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc b/documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-M2.adoc similarity index 52% rename from documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc rename to documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-M2.adoc index 9a9ee9e870cc..f55ef4a580f6 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc +++ b/documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-M2.adoc @@ -1,4 +1,4 @@ -[[release-notes-6.1.0-M2]] +[[v6.1.0-M2]] == 6.1.0-M2 *Date of Release:* ❓ @@ -9,59 +9,59 @@ For a complete list of all _closed_ issues and pull requests for this release, c link:{junit-framework-repo}+/milestone/112?closed=1+[6.1.0-M2] milestone page in the JUnit repository on GitHub. -[[release-notes-6.1.0-M2-junit-platform]] +[[v6.1.0-M2-junit-platform]] === JUnit Platform -[[release-notes-6.1.0-M2-junit-platform-bug-fixes]] +[[v6.1.0-M2-junit-platform-bug-fixes]] ==== Bug Fixes * Clarify `TestDescriptor` implementation requirements. -[[release-notes-6.1.0-M2-junit-platform-deprecations-and-breaking-changes]] +[[v6.1.0-M2-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-6.1.0-M2-junit-platform-new-features-and-improvements]] +[[v6.1.0-M2-junit-platform-new-features-and-improvements]] ==== New Features and Improvements * ❓ -[[release-notes-6.1.0-M2-junit-jupiter]] +[[v6.1.0-M2-junit-jupiter]] === JUnit Jupiter -[[release-notes-6.1.0-M2-junit-jupiter-bug-fixes]] +[[v6.1.0-M2-junit-jupiter-bug-fixes]] ==== Bug Fixes * ❓ -[[release-notes-6.1.0-M2-junit-jupiter-deprecations-and-breaking-changes]] +[[v6.1.0-M2-junit-jupiter-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-6.1.0-M2-junit-jupiter-new-features-and-improvements]] +[[v6.1.0-M2-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements * `JAVA_27` has been added to the `JRE` enum for use with `JRE`-based execution conditions. * https://www.junit-pioneer.org/[JUnit Pioneer]'s `DefaultLocaleExtension` and `DefaultTimeZoneExtension` are now part of the JUnit Jupiter. Find examples in the - <<../user-guide/index.adoc#writing-tests-built-in-extensions-DefaultLocaleAndTimeZone, User Guide>>. + xref:writing-tests/built-in-extensions.adoc#DefaultLocaleAndTimeZone[User Guide]. -[[release-notes-6.1.0-M2-junit-vintage]] +[[v6.1.0-M2-junit-vintage]] === JUnit Vintage -[[release-notes-6.1.0-M2-junit-vintage-bug-fixes]] +[[v6.1.0-M2-junit-vintage-bug-fixes]] ==== Bug Fixes * ❓ -[[release-notes-6.1.0-M2-junit-vintage-deprecations-and-breaking-changes]] +[[v6.1.0-M2-junit-vintage-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-6.1.0-M2-junit-vintage-new-features-and-improvements]] +[[v6.1.0-M2-junit-vintage-new-features-and-improvements]] ==== New Features and Improvements * ❓ diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc b/documentation/modules/ROOT/partials/release-notes/release-notes-TEMPLATE.adoc similarity index 65% rename from documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc rename to documentation/modules/ROOT/partials/release-notes/release-notes-TEMPLATE.adoc index 5bd352c3c008..6bcfc9452fc5 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-TEMPLATE.adoc +++ b/documentation/modules/ROOT/partials/release-notes/release-notes-TEMPLATE.adoc @@ -10,10 +10,10 @@ // which you can determine via https://github.com/junit-team/junit-framework/milestones/. // If a GitHub milestone does not yet exist for the given VERSION, you will need to // create a new GitHub milestone or ask a member of the JUnit team to create it for you. -// 5) 'include:' this new file in index.adoc. +// 5) 'include:' this new file in ../../pages/release-notes.adoc. // 6) Delete this entire comment block. // -[[release-notes-VERSION]] +[[vVERSION]] == VERSION *Date of Release:* ❓ @@ -25,58 +25,58 @@ link:{junit-framework-repo}+/milestone/MILESTONE_NUMBER?closed=1+[VERSION] miles repository on GitHub. -[[release-notes-VERSION-junit-platform]] +[[vVERSION-junit-platform]] === JUnit Platform -[[release-notes-VERSION-junit-platform-bug-fixes]] +[[vVERSION-junit-platform-bug-fixes]] ==== Bug Fixes * ❓ -[[release-notes-VERSION-junit-platform-deprecations-and-breaking-changes]] +[[vVERSION-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-VERSION-junit-platform-new-features-and-improvements]] +[[vVERSION-junit-platform-new-features-and-improvements]] ==== New Features and Improvements * ❓ -[[release-notes-VERSION-junit-jupiter]] +[[vVERSION-junit-jupiter]] === JUnit Jupiter -[[release-notes-VERSION-junit-jupiter-bug-fixes]] +[[vVERSION-junit-jupiter-bug-fixes]] ==== Bug Fixes * ❓ -[[release-notes-VERSION-junit-jupiter-deprecations-and-breaking-changes]] +[[vVERSION-junit-jupiter-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-VERSION-junit-jupiter-new-features-and-improvements]] +[[vVERSION-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements * ❓ -[[release-notes-VERSION-junit-vintage]] +[[vVERSION-junit-vintage]] === JUnit Vintage -[[release-notes-VERSION-junit-vintage-bug-fixes]] +[[vVERSION-junit-vintage-bug-fixes]] ==== Bug Fixes * ❓ -[[release-notes-VERSION-junit-vintage-deprecations-and-breaking-changes]] +[[vVERSION-junit-vintage-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes * ❓ -[[release-notes-VERSION-junit-vintage-new-features-and-improvements]] +[[vVERSION-junit-vintage-new-features-and-improvements]] ==== New Features and Improvements * ❓ diff --git a/documentation/package.json b/documentation/package.json new file mode 100644 index 000000000000..9960fe42e116 --- /dev/null +++ b/documentation/package.json @@ -0,0 +1,12 @@ +{ + "devDependencies": { + "@antora/cli": "3.1.14", + "@antora/site-generator": "3.1.14" + }, + "dependencies": { + "@antora/collector-extension": "1.0.2", + "@antora/lunr-extension": "1.0.0-alpha.10", + "@springio/antora-extensions": "1.14.7", + "highlight.js": "11.11.1" + } +} diff --git a/documentation/src/docs/asciidoc/docinfos/docinfo-footer.html b/documentation/src/docs/asciidoc/docinfos/docinfo-footer.html deleted file mode 100644 index 221366510a23..000000000000 --- a/documentation/src/docs/asciidoc/docinfos/docinfo-footer.html +++ /dev/null @@ -1,33 +0,0 @@ - - diff --git a/documentation/src/docs/asciidoc/docinfos/docinfo.html b/documentation/src/docs/asciidoc/docinfos/docinfo.html deleted file mode 100644 index 15ca19338a7e..000000000000 --- a/documentation/src/docs/asciidoc/docinfos/docinfo.html +++ /dev/null @@ -1,23 +0,0 @@ - - diff --git a/documentation/src/docs/asciidoc/link-attributes.adoc b/documentation/src/docs/asciidoc/link-attributes.adoc deleted file mode 100644 index 649b7d2e847d..000000000000 --- a/documentation/src/docs/asciidoc/link-attributes.adoc +++ /dev/null @@ -1,272 +0,0 @@ -:javadoc-root: link:../api -ifdef::backend-pdf[] -:javadoc-root: https://docs.junit.org/{docs-version}/api -endif::[] -// Snapshot Repository -:snapshot-repo: https://central.sonatype.com/service/rest/repository/browse/maven-snapshots -// Base Links -:junit-team: https://github.com/junit-team -:junit-framework-repo: {junit-team}/junit-framework -:current-branch: {junit-framework-repo}/tree/{release-branch} -// Platform Commons -:junit-platform-support-package: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/package-summary.html[org.junit.platform.commons.support] -:AnnotationSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/AnnotationSupport.html[AnnotationSupport] -:ClassSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ClassSupport.html[ClassSupport] -:ConversionSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/conversion/ConversionSupport.html[ConversionSupport] -:ModifierSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ModifierSupport.html[ModifierSupport] -:ReflectionSupport: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ReflectionSupport.html[ReflectionSupport] -:Testable: {javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/annotation/Testable.html[@Testable] -// Platform Console Launcher -:junit-platform-console: {javadoc-root}/org.junit.platform.console/org/junit/platform/console/package-summary.html[junit-platform-console] -:ConsoleLauncher: {javadoc-root}/org.junit.platform.console/org/junit/platform/console/ConsoleLauncher.html[ConsoleLauncher] -// Platform Engine -:junit-platform-engine: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/package-summary.html[junit-platform-engine] -:junit-platform-engine-support-discovery: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/discovery/package-summary.html[org.junit.platform.engine.support.discovery] -:CancellationToken: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/CancellationToken.html[CancellationToken] -:ClasspathResourceSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathResourceSelector.html[ClasspathResourceSelector] -:ClasspathRootSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathRootSelector.html[ClasspathRootSelector] -:ClassSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClassSelector.html[ClassSelector] -:DiscoveryIssue: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/DiscoveryIssue.html[DiscoveryIssue] -:DiscoveryIssueReporter: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/discovery/DiscoveryIssueReporter.html[DiscoveryIssueReporter] -:DirectorySelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DirectorySelector.html[DirectorySelector] -:DiscoverySelectors: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html[DiscoverySelectors] -:DiscoverySelectors_selectClasspathResource: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathResource(java.lang.String)[selectClasspathResource] -:DiscoverySelectors_selectClasspathRoots: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathRoots(java.util.Set)[selectClasspathRoots] -:DiscoverySelectors_selectClass: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClass(java.lang.String)[selectClass] -:DiscoverySelectors_selectDirectory: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectDirectory(java.lang.String)[selectDirectory] -:DiscoverySelectors_selectFile: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectFile(java.lang.String)[selectFile] -:DiscoverySelectors_selectIteration: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectIteration(org.junit.platform.engine.DiscoverySelector,int\...)[selectIteration] -:DiscoverySelectors_selectMethod: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectMethod(java.lang.String)[selectMethod] -:DiscoverySelectors_selectModule: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectModule(java.lang.String)[selectModule] -:DiscoverySelectors_selectNestedClass: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectNestedClass(java.util.List,java.lang.Class)[selectNestedClass] -:DiscoverySelectors_selectNestedMethod: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectNestedMethod(java.util.List,java.lang.Class,java.lang.String)[selectNestedMethod] -:DiscoverySelectors_selectPackage: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectPackage(java.lang.String)[selectPackage] -:DiscoverySelectors_selectUniqueId: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUniqueId(java.lang.String)[selectUniqueId] -:DiscoverySelectors_selectUri: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUri(java.lang.String)[selectUri] -:EngineDiscoveryListener: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/EngineDiscoveryListener.html[EngineDiscoveryListener] -:EngineDiscoveryRequest: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/EngineDiscoveryRequest.html[EngineDiscoveryRequest] -:FileSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/FileSelector.html[FileSelector] -:HierarchicalTestEngine: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.html[HierarchicalTestEngine] -:IterationSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/IterationSelector.html[IterationSelector] -:MethodSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/MethodSelector.html[MethodSelector] -:ModuleSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ModuleSelector.html[ModuleSelector] -:NamespacedHierarchicalStore: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.html[NamespacedHierarchicalStore] -:NestedClassSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedClassSelector.html[NestedClassSelector] -:NestedMethodSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedMethodSelector.html[NestedMethodSelector] -:OutputDirectoryCreator: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/OutputDirectoryCreator.html[OutputDirectoryCreator] -:PackageSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/PackageSelector.html[PackageSelector] -:ParallelExecutionConfigurationStrategy: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.html[ParallelExecutionConfigurationStrategy] -:UniqueIdSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UniqueIdSelector.html[UniqueIdSelector] -:UriSelector: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UriSelector.html[UriSelector] -:TestEngine: {javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/TestEngine.html[TestEngine] -// Platform Launcher API -:junit-platform-launcher: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/package-summary.html[junit-platform-launcher] -:DiscoveryIssueException: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/DiscoveryIssueException.html[DiscoveryIssueException] -:Launcher: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/Launcher.html[Launcher] -:LauncherConfig: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherConfig.html[LauncherConfig] -:LauncherDiscoveryListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryListener.html[LauncherDiscoveryListener] -:LauncherDiscoveryRequest: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryRequest.html[LauncherDiscoveryRequest] -:LauncherDiscoveryRequestBuilder: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.html[LauncherDiscoveryRequestBuilder] -:LauncherExecutionRequest: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherExecutionRequest.html[LauncherExecutionRequest] -:LauncherExecutionRequestBuilder: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherExecutionRequestBuilder.html[LauncherExecutionRequestBuilder] -:LauncherFactory: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherFactory.html[LauncherFactory] -:LauncherInterceptor: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherInterceptor.html[LauncherInterceptor] -:LauncherSession: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSession.html[LauncherSession] -:LauncherSessionListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSessionListener.html[LauncherSessionListener] -:LoggingListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/LoggingListener.html[LoggingListener] -:PostDiscoveryFilter: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/PostDiscoveryFilter.html[PostDiscoveryFilter] -:SummaryGeneratingListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/SummaryGeneratingListener.html[SummaryGeneratingListener] -:TestExecutionListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestExecutionListener.html[TestExecutionListener] -:TestPlan: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestPlan.html[TestPlan] -:UniqueIdTrackingListener: {javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.html[UniqueIdTrackingListener] -// Platform Reporting -:LegacyXmlReportGeneratingListener: {javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.html[LegacyXmlReportGeneratingListener] -:OpenTestReportGeneratingListener: {javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.html[OpenTestReportGeneratingListener] -// Platform Suite -:suite-api-package: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/package-summary.html[org.junit.platform.suite.api] -:junit-platform-suite-engine: {javadoc-root}/org.junit.platform.suite.engine/org/junit/platform/suite/engine/package-summary.html[junit-platform-suite-engine] -:Select: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/Select.html[@Select] -:SelectClasspathResource: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasspathResource.html[@SelectClasspathResource] -:SelectClasses: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasses.html[@SelectClasses] -:SelectDirectories: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectDirectories.html[@SelectDirectories] -:SelectFile: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectFile.html[@SelectFile] -:SelectMethod: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectMethod.html[@SelectMethod] -:SelectModules: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectModules.html[@SelectModules] -:SelectPackages: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectPackages.html[@SelectPackages] -:SelectUris: {javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectUris.html[@SelectUris] -// Platform Test Kit -:testkit-engine-package: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/package-summary.html[org.junit.platform.testkit.engine] -:EngineExecutionResults: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EngineExecutionResults.html[EngineExecutionResults] -:EngineTestKit: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EngineTestKit.html[EngineTestKit] -:Event: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Event.html[Event] -:EventConditions: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventConditions.html[EventConditions] -:Events: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Events.html[Events] -:EventStatistics: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventStatistics.html[EventStatistics] -:EventType: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventType.html[EventType] -:Executions: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Executions.html[Executions] -:TerminationInfo: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/TerminationInfo.html[TerminationInfo] -:TestExecutionResultConditions: {javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/TestExecutionResultConditions.html[TestExecutionResultConditions] -// Jupiter Core API -:api-package: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/package-summary.html[org.junit.jupiter.api] -:Assertions: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html[org.junit.jupiter.api.Assertions] -:Assumptions: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assumptions.html[org.junit.jupiter.api.Assumptions] -:AutoClose: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/AutoClose.html[@AutoClose] -:ClassOrderer_ClassName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.ClassName.html[ClassOrderer.ClassName] -:ClassOrderer_Default: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Default.html[ClassOrderer.Default] -:ClassOrderer_DisplayName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.DisplayName.html[ClassOrderer.DisplayName] -:ClassOrderer_OrderAnnotation: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.OrderAnnotation.html[ClassOrderer.OrderAnnotation] -:ClassOrderer_Random: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Random.html[ClassOrderer.Random] -:ClassOrderer: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.html[ClassOrderer] -:ClassTemplate: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassTemplate.html[@ClassTemplate] -:Disabled: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Disabled.html[@Disabled] -:MethodOrderer_Default: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Default.html[MethodOrderer.Default] -:MethodOrderer_DisplayName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.DisplayName.html[MethodOrderer.DisplayName] -:MethodOrderer_MethodName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.MethodName.html[MethodOrderer.MethodName] -:MethodOrderer_OrderAnnotation: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.OrderAnnotation.html[MethodOrderer.OrderAnnotation] -:MethodOrderer_Random: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Random.html[MethodOrderer.Random] -:MethodOrderer: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.html[MethodOrderer] -:Named: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Named.html[Named] -:Order: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Order.html[@Order] -:RepetitionInfo: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/RepetitionInfo.html[RepetitionInfo] -:TestInfo: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestInfo.html[TestInfo] -:TestClassOrder: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestClassOrder.html[@TestClassOrder] -:TestMethodOrder: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestMethodOrder.html[@TestMethodOrder] -:TestReporter: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[TestReporter] -:TestTemplate: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestTemplate.html[@TestTemplate] -// @DefaultLocale and @DefaultTimeZone -:DefaultLocale: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/DefaultLocale.html[@DefaultLocale] -:DefaultTimeZone: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/DefaultTimeZone.html[@DefaultTimeZone] -:LocaleProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/LocaleProvider.html[LocaleProvider] -:TimeZoneProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/TimeZoneProvider.html[TimeZoneProvider] -:ReadsDefaultLocale: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ReadsDefaultLocale.html[@ReadsDefaultLocale] -:ReadsDefaultTimeZone: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ReadsDefaultTimeZone.html[@ReadsDefaultTimeZone] -:WritesDefaultLocale: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/WritesDefaultLocale.html[@WritesDefaultLocale] -:WritesDefaultTimeZone: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/WritesDefaultTimeZone.html[@WritesDefaultTimeZone] -// Jupiter Parallel API -:Execution: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Execution.html[@Execution] -:Isolated: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Isolated.html[@Isolated] -:ResourceLock: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLock.html[@ResourceLock] -:ResourceLockTarget: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLockTarget.html[ResourceLockTarget] -:ResourceLocksProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLocksProvider.html[ResourceLocksProvider] -:Resources: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Resources.html[Resources] -// Jupiter Extension APIs -:extension-api-package: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/package-summary.html[org.junit.jupiter.api.extension] -:AfterAllCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterAllCallback.html[AfterAllCallback] -:AfterClassTemplateInvocationCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterClassTemplateInvocationCallback.html[AfterClassTemplateInvocationCallback] -:AfterEachCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterEachCallback.html[AfterEachCallback] -:AfterTestExecutionCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterTestExecutionCallback.html[AfterTestExecutionCallback] -:ParameterContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterContext.html[ParameterContext] -:BeforeAllCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeAllCallback.html[BeforeAllCallback] -:BeforeClassTemplateInvocationCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeClassTemplateInvocationCallback.html[BeforeClassTemplateInvocationCallback] -:BeforeEachCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeEachCallback.html[BeforeEachCallback] -:BeforeTestExecutionCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.html[BeforeTestExecutionCallback] -:ClassTemplateInvocationContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ClassTemplateInvocationContext.html[ClassTemplateInvocationContext] -:ClassTemplateInvocationContextProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ClassTemplateInvocationContextProvider.html[ClassTemplateInvocationContextProvider] -:ExecutableInvoker: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutableInvoker.html[ExecutableInvoker] -:ExecutionCondition: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutionCondition.html[ExecutionCondition] -:ExtendWith: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtendWith.html[@ExtendWith] -:ExtensionContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.html[ExtensionContext] -:ExtensionContext_Store: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.Store.html[Store] -:ExtensionContext_StoreScope: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.StoreScope.html[StoreScope] -:InvocationInterceptor: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/InvocationInterceptor.html[InvocationInterceptor] -:LifecycleMethodExecutionExceptionHandler: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.html[LifecycleMethodExecutionExceptionHandler] -:ParameterResolver: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterResolver.html[ParameterResolver] -:RegisterExtension: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/RegisterExtension.html[@RegisterExtension] -:TestExecutionExceptionHandler: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.html[TestExecutionExceptionHandler] -:TestInstanceFactory: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstanceFactory.html[TestInstanceFactory] -:TestInstancePostProcessor: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePostProcessor.html[TestInstancePostProcessor] -:TestInstancePreConstructCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.html[TestInstancePreConstructCallback] -:TestInstancePreDestroyCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.html[TestInstancePreDestroyCallback] -:TestTemplateInvocationContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContext.html[TestTemplateInvocationContext] -:TestTemplateInvocationContextProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.html[TestTemplateInvocationContextProvider] -:TestWatcher: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestWatcher.html[TestWatcher] -:PreInterruptCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/PreInterruptCallback.html[PreInterruptCallback] -// Jupiter Conditions -:DisabledForJreRange: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledForJreRange.html[@DisabledForJreRange] -:DisabledIf: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIf.html[@DisabledIf] -:DisabledIfEnvironmentVariable: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.html[@DisabledIfEnvironmentVariable] -:DisabledIfSystemProperty: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfSystemProperty.html[@DisabledIfSystemProperty] -:DisabledInNativeImage: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledInNativeImage.html[@DisabledInNativeImage] -:DisabledOnJre: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnJre.html[@DisabledOnJre] -:DisabledOnOs: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnOs.html[@DisabledOnOs] -:EnabledForJreRange: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledForJreRange.html[@EnabledForJreRange] -:EnabledIf: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIf.html[@EnabledIf] -:EnabledIfEnvironmentVariable: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.html[@EnabledIfEnvironmentVariable] -:EnabledIfSystemProperty: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfSystemProperty.html[@EnabledIfSystemProperty] -:EnabledInNativeImage: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledInNativeImage.html[@EnabledInNativeImage] -:EnabledOnJre: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnJre.html[@EnabledOnJre] -:EnabledOnOs: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnOs.html[@EnabledOnOs] -:JRE: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/JRE.html[JRE] -// Jupiter I/O -:TempDir: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDir.html[@TempDir] -// Jupiter Params -:params-provider-package: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/package-summary.html[org.junit.jupiter.params.provider] -:AfterParameterizedClassInvocation: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/AfterParameterizedClassInvocation.html[@AfterParameterizedClassInvocation] -:AnnotationBasedArgumentConverter: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.html[AnnotationBasedArgumentConverter] -:AnnotationBasedArgumentsProvider: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.html[AnnotationBasedArgumentsProvider] -:AggregateWith: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/AggregateWith.html[@AggregateWith] -:Arguments: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/Arguments.html[Arguments] -:ArgumentsProvider: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/ArgumentsProvider.html[ArgumentsProvider] -:ArgumentsAccessor: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAccessor.html[ArgumentsAccessor] -:ArgumentsAggregator: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAggregator.html[ArgumentsAggregator] -:BeforeParameterizedClassInvocation: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/BeforeParameterizedClassInvocation.html[@BeforeParameterizedClassInvocation] -:CsvArgumentsProvider: {junit-framework-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java[CsvArgumentsProvider] -:EmptySource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/EmptySource.html[@EmptySource] -:FieldSource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/FieldSource.html[@FieldSource] -:MethodSource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/MethodSource.html[@MethodSource] -:NullAndEmptySource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullAndEmptySource.html[@NullAndEmptySource] -:NullSource: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullSource.html[@NullSource] -:Parameter: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/Parameter.html[@Parameter] -:ParameterizedClass: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedClass.html[@ParameterizedClass] -:ParameterizedTest: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedTest.html[@ParameterizedTest] -:ParameterInfo: {javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/support/ParameterInfo.html[ParameterInfo] -:ValueArgumentsProvider: {junit-framework-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java[ValueArgumentsProvider] -// Jupiter Engine -:junit-jupiter-engine: {javadoc-root}/org.junit.jupiter.engine/org/junit/jupiter/engine/package-summary.html[junit-jupiter-engine] -// Jupiter Extension Implementations -:AutoCloseExtension: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java[AutoCloseExtension] -:DisabledCondition: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java[DisabledCondition] -:RepetitionExtension: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java[RepetitionExtension] -:TempDirectory: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java[TempDirectory] -:TestInfoParameterResolver: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java[TestInfoParameterResolver] -:TestReporterParameterResolver: {current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java[TestReporterParameterResolver] -:TypeBasedParameterResolver: {current-branch}/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java[TypeBasedParameterResolver] -// Jupiter Examples -:CustomAnnotationParameterResolver: {current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java[CustomAnnotationParameterResolver] -:CustomTypeParameterResolver: {current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java[CustomTypeParameterResolver] -:MapOfListsTypeBasedParameterResolver: {current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java[MapOfListsTypeBasedParameterResolver] -// Jupiter Migration Support -:EnableJUnit4MigrationSupport: {javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.html[@EnableJUnit4MigrationSupport] -:EnableRuleMigrationSupport: {javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.html[@EnableRuleMigrationSupport] -// JUnit Start -:JUnit: {javadoc-root}/org.junit.start/org/junit/start/JUnit.html[JUnit] -// Vintage -:junit-vintage-engine: {javadoc-root}/org.junit.vintage.engine/org/junit/vintage/engine/package-summary.html[junit-vintage-engine] -// Examples Repository -:junit-examples-repo: {junit-team}/junit-examples -:junit-jupiter-starter-ant: {junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-ant[junit-jupiter-starter-ant] -:junit-jupiter-starter-gradle-groovy: {junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle-groovy[junit-jupiter-starter-gradle-groovy] -:junit-jupiter-starter-gradle-kotlin: {junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle-kotlin[junit-jupiter-starter-gradle-kotlin] -:junit-jupiter-starter-gradle: {junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle[junit-jupiter-starter-gradle] -:junit-jupiter-starter-maven: {junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-maven[junit-jupiter-starter-maven] -:RandomParametersExtension: {junit-examples-repo}/tree/{release-branch}/junit-jupiter-extensions/src/main/java/com/example/random/RandomParametersExtension.java[RandomParametersExtension] -// Third-party Links -:API: https://apiguardian-team.github.io/apiguardian/docs/current/api/[@API] -:API_Guardian: https://github.com/apiguardian-team/apiguardian[@API Guardian] -:AssertJ: https://assertj.github.io/doc/[AssertJ] -:Checkstyle: https://checkstyle.sourceforge.io[Checkstyle] -:DiscussionsQA: https://github.com/junit-team/junit-framework/discussions/categories/q-a[Q&A category on GitHub Discussions] -:Hamcrest: https://hamcrest.org/JavaHamcrest/[Hamcrest] -:Jimfs: https://google.github.io/jimfs/[Jimfs] -:Log4j: https://logging.apache.org/log4j/2.x/[Log4j] -:Log4j_JDK_Logging_Adapter: https://logging.apache.org/log4j/2.x/log4j-jul/index.html[Log4j JDK Logging Adapter] -:Logback: https://logback.qos.ch/[Logback] -:LogManager: https://docs.oracle.com/en/java/javase/17/docs/api/java.logging/java/util/logging/LogManager.html[LogManager] -:Maven_Central: https://central.sonatype.com/[Maven Central] -:MockitoExtension: https://github.com/mockito/mockito/blob/release/2.x/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java[MockitoExtension] -:ServiceLoader: {jdk-javadoc-base-url}/java.base/java/util/ServiceLoader.html[ServiceLoader] -:SpringExtension: https://github.com/spring-projects/spring-framework/tree/HEAD/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java[SpringExtension] -:StackOverflow: https://stackoverflow.com/questions/tagged/junit5[Stack Overflow] -:Truth: https://truth.dev/[Truth] -:OpenTestReporting: https://github.com/ota4j-team/open-test-reporting[Open Test Reporting] -:OpenTestReportingCliTool: https://github.com/ota4j-team/open-test-reporting#cli-tool-for-validation-and-format-conversion[Open Test Reporting CLI Tool] diff --git a/documentation/src/docs/asciidoc/release-notes/index.adoc b/documentation/src/docs/asciidoc/release-notes/index.adoc deleted file mode 100644 index c7f8737025c6..000000000000 --- a/documentation/src/docs/asciidoc/release-notes/index.adoc +++ /dev/null @@ -1,28 +0,0 @@ -[[release-notes]] -= JUnit Release Notes -Stefan Bechtold; Sam Brannen; Johannes Link; Matthias Merdes; Marc Philipp; Juliette de Rancourt; Christian Stein -// -:basedir: {includedir}/release-notes -:docinfodir: {includedir}/docinfos -:docinfo2: -:numbered!: -:last-update-label!: -// - -This document contains the change log for all JUnit releases since 6.0 GA. - -Please refer to the <<../user-guide/index.adoc#user-guide,User Guide>> for comprehensive -reference documentation for programmers writing tests, extension authors, and engine -authors as well as build tool and IDE vendors. - -include::{includedir}/link-attributes.adoc[] - -include::{basedir}/release-notes-6.1.0-M2.adoc[] - -include::{basedir}/release-notes-6.1.0-M1.adoc[] - -include::{basedir}/release-notes-6.0.2.adoc[] - -include::{basedir}/release-notes-6.0.1.adoc[] - -include::{basedir}/release-notes-6.0.0.adoc[] diff --git a/documentation/src/docs/asciidoc/resources/fonts/Symbola.ttf b/documentation/src/docs/asciidoc/resources/fonts/Symbola.ttf deleted file mode 100644 index 055f02558b9a..000000000000 Binary files a/documentation/src/docs/asciidoc/resources/fonts/Symbola.ttf and /dev/null differ diff --git a/documentation/src/docs/asciidoc/resources/themes/junit-pdf-theme.yml b/documentation/src/docs/asciidoc/resources/themes/junit-pdf-theme.yml deleted file mode 100644 index 1cba8c427dee..000000000000 --- a/documentation/src/docs/asciidoc/resources/themes/junit-pdf-theme.yml +++ /dev/null @@ -1,6 +0,0 @@ -extends: default -font: - catalog: - merge: true - Symbola: Symbola.ttf - fallbacks: [Symbola] diff --git a/documentation/src/docs/asciidoc/resources/themes/rouge_junit.rb b/documentation/src/docs/asciidoc/resources/themes/rouge_junit.rb deleted file mode 100644 index 003a88b51955..000000000000 --- a/documentation/src/docs/asciidoc/resources/themes/rouge_junit.rb +++ /dev/null @@ -1,148 +0,0 @@ -require 'rouge' unless defined? ::Rouge.version - -module Rouge; module Themes - - class JUnit < CSSTheme - name 'junit' - - # This is an extension of the official "github" theme to remove accessibility issues in code blocks - # Primer primitives (https://github.com/primer/primitives/tree/main/src/tokens) - P_RED_0 = {:light => '#ffebe9', :dark => '#ffdcd7'} - P_RED_3 = {:dark => '#ff7b72'} - P_RED_5 = {:light => '#cf222e'} - P_RED_7 = {:light => '#82071e', :dark => '#8e1519'} - P_RED_8 = {:dark => '#67060c'} - P_ORANGE_2 = {:dark => '#ffa657'} - P_ORANGE_6 = {:light => '#953800'} - P_GREEN_0 = {:light => '#dafbe1', :dark => '#aff5b4'} - P_GREEN_1 = {:dark => '#7ee787'} - P_GREEN_6 = {:light => '#116329'} - P_GREEN_8 = {:dark => '#033a16'} - P_BLUE_1 = {:dark => '#a5d6ff'} - P_BLUE_2 = {:dark => '#79c0ff'} - P_BLUE_5 = {:dark => '#1f6feb'} - P_BLUE_6 = {:light => '#0550ae'} - P_BLUE_7 = {:light => '#055099'} - P_BLUE_8 = {:light => '#0a3069'} - P_PURPLE_2 = {:dark => '#d2a8ff'} - P_PURPLE_5 = {:light => '#8250df'} - P_GRAY_0 = {:light => '#f6f8fa', :dark => '#f0f6fc'} - P_GRAY_1 = {:dark => '#c9d1d9'} - P_GRAY_3 = {:dark => '#8b949e'} - P_GRAY_5 = {:light => '#34383d'} # '#6e7781' - P_GRAY_8 = {:dark => '#161b22'} - P_GRAY_9 = {:light => '#24292f'} - - extend HasModes - - def self.light! - mode :dark # indicate that there is a dark variant - mode! :light - end - - def self.dark! - mode :light # indicate that there is a light variant - mode! :dark - end - - def self.make_dark! - palette :comment => P_GRAY_3[@mode] - palette :constant => P_BLUE_2[@mode] - palette :entity => P_PURPLE_2[@mode] - palette :heading => P_BLUE_5[@mode] - palette :keyword => P_RED_3[@mode] - palette :string => P_BLUE_1[@mode] - palette :tag => P_GREEN_1[@mode] - palette :variable => P_ORANGE_2[@mode] - - palette :fgDefault => P_GRAY_1[@mode] - palette :bgDefault => P_GRAY_8[@mode] - - palette :fgInserted => P_GREEN_0[@mode] - palette :bgInserted => P_GREEN_8[@mode] - - palette :fgDeleted => P_RED_0[@mode] - palette :bgDeleted => P_RED_8[@mode] - - palette :fgError => P_GRAY_0[@mode] - palette :bgError => P_RED_7[@mode] - end - - def self.make_light! - palette :comment => P_GREEN_6[@mode] - palette :constant => P_BLUE_6[@mode] - palette :entity => P_PURPLE_5[@mode] - palette :heading => P_BLUE_6[@mode] - palette :keyword => P_RED_5[@mode] - palette :string => P_BLUE_8[@mode] - palette :tag => P_BLUE_7[@mode] - palette :variable => P_ORANGE_6[@mode] - - palette :fgDefault => P_GRAY_9[@mode] - palette :bgDefault => P_GRAY_0[@mode] - - palette :fgInserted => P_GREEN_6[@mode] - palette :bgInserted => P_GREEN_0[@mode] - - palette :fgDeleted => P_RED_7[@mode] - palette :bgDeleted => P_RED_0[@mode] - - palette :fgError => P_GRAY_0[@mode] - palette :bgError => P_RED_7[@mode] - end - - light! - - style Text, :fg => :fgDefault, :bg => :bgDefault - - style Keyword, :fg => :keyword - - style Generic::Error, :fg => :fgError - - style Generic::Deleted, :fg => :fgDeleted, :bg => :bgDeleted - - style Name::Builtin, - Name::Class, - Name::Constant, - Name::Namespace, :fg => :variable - - style Literal::String::Regex, - Name::Attribute, - Name::Tag, :fg => :tag - - style Generic::Inserted, :fg => :fgInserted, :bg => :bgInserted - - style Keyword::Constant, - Literal, - Literal::String::Backtick, - Name::Builtin::Pseudo, - Name::Exception, - Name::Label, - Name::Property, - Name::Variable, - Operator, :fg => :constant - - style Generic::Heading, - Generic::Subheading, :fg => :heading, :bold => true - - style Literal::String, :fg => :string - - style Name::Decorator, - Name::Function, :fg => :entity - - style Error, :fg => :fgError, :bg => :bgError - - style Comment, - Generic::Lineno, - Generic::Traceback, :fg => :comment - - style Name::Entity, - Literal::String::Interpol, :fg => :fgDefault - - style Generic::Emph, :fg => :fgDefault, :italic => true - - style Generic::Strong, :fg => :fgDefault, :bold => true - - end -end; end - diff --git a/documentation/src/docs/asciidoc/tocbot-3.0.2/styles.css b/documentation/src/docs/asciidoc/tocbot-3.0.2/styles.css deleted file mode 100644 index 3fd131d89c5d..000000000000 --- a/documentation/src/docs/asciidoc/tocbot-3.0.2/styles.css +++ /dev/null @@ -1 +0,0 @@ -.transition--300{transition:all 300ms ease-in-out}.toc{height:100%;width:280px;transform:translateX(0)}.content h1:first-child,.content h2:first-child{padding-top:0;margin-top:0}.title{font-size:3em}.content{margin-bottom:95vh}.content ul,.content ol{list-style:inherit}.content a{color:#0977c3;text-decoration:none;border-bottom:1px solid #EEE;transition:all 300ms ease}.content a.no-decoration{border-bottom:0}.content a:hover{border-bottom:1px solid #0977c3}.content a:hover.no-decoration{border-bottom:0}a.toc-link{text-decoration:none}.try-it-container{transform:translateY(84%)}.try-it-container.is-open{transform:translateY(0%)}.page-content{display:block !important}.hljs{display:block;background:white;padding:0.5em;color:#333333;overflow-x:auto}.hljs-comment,.hljs-meta{color:#969896}.hljs-string,.hljs-variable,.hljs-template-variable,.hljs-strong,.hljs-emphasis,.hljs-quote{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#a71d5d}.hljs-literal,.hljs-symbol,.hljs-bullet,.hljs-attribute{color:#0086b3}.hljs-section,.hljs-name{color:#63a35c}.hljs-tag{color:#333333}.hljs-title,.hljs-attr,.hljs-selector-id,.hljs-selector-class,.hljs-selector-attr,.hljs-selector-pseudo{color:#795da3}.hljs-addition{color:#55a532;background-color:#eaffea}.hljs-deletion{color:#bd2c00;background-color:#ffecec}.hljs-link{text-decoration:underline}.toc-icon{position:fixed;top:0;right:0}#toc:checked ~ .toc{box-shadow:0 0 5px #c8c8c8;transform:translateX(0)}.toc{background-color:rgba(255,255,255,0.9);transform:translateX(-100%)}.toc.toc-right{transform:translateX(100%);right:0}@media (min-width: 52em){.toc{transform:translateX(0)}.toc.toc-right{transform:translateX(0);right:calc((100% - 48rem - 4rem) / 2)}.toc-icon{display:none}.try-it-container{display:block}.content{margin-left:280px}.toc-right ~ .content{margin-left:0;margin-right:280px}}*{box-sizing:border-box}body{font-size:1.2rem;font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif}h1,h2,h3,h4,h5,h6{padding-top:0.5em}p{margin-top:0.25rem}pre{display:block;background:#f7f7f7;border-radius:2px;border:1px solid #e0e0e0;padding:2px;line-height:1.2;margin-bottom:10px;overflow:auto;white-space:pre-wrap}code{display:inline;font-size:.8em;max-width:100%} diff --git a/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.css b/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.css deleted file mode 100644 index 6265223f15ad..000000000000 --- a/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.css +++ /dev/null @@ -1 +0,0 @@ -.toc{overflow-y:auto}.toc>ul{overflow:hidden;position:relative}.toc>ul li{list-style:none}.toc-list{margin:0;padding-left:10px}a.toc-link{color:currentColor;height:100%}.is-collapsible{max-height:1000px;overflow:hidden;transition:all 300ms ease-in-out}.is-collapsed{max-height:0}.is-position-fixed{position:fixed !important;top:0}.is-active-link{font-weight:700}.toc-link::before{background-color:#EEE;content:' ';display:inline-block;height:inherit;left:0;margin-top:-1px;position:absolute;width:2px}.is-active-link::before{background-color:#54BC4B} diff --git a/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.js b/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.js deleted file mode 100644 index 52fe18378f4f..000000000000 --- a/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.js +++ /dev/null @@ -1,136 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // identity function for calling harmony imports with the correct context -/******/ __webpack_require__.i = function(value) { return value; }; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 5); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/* unknown exports provided */ -/* all exports used */ -/*!***********************************!*\ - !*** (webpack)/buildin/global.js ***! - \***********************************/ -/***/ (function(module, exports) { - -eval("var g;\r\n\r\n// This works in non-strict mode\r\ng = (function() {\r\n\treturn this;\r\n})();\r\n\r\ntry {\r\n\t// This works if eval is allowed (see CSP)\r\n\tg = g || Function(\"return this\")() || (1,eval)(\"this\");\r\n} catch(e) {\r\n\t// This works if the window reference is available\r\n\tif(typeof window === \"object\")\r\n\t\tg = window;\r\n}\r\n\r\n// g can still be undefined, but nothing to do about it...\r\n// We return undefined, instead of nothing here, so it's\r\n// easier to handle this case. if(!global) { ...}\r\n\r\nmodule.exports = g;\r\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8od2VicGFjaykvYnVpbGRpbi9nbG9iYWwuanM/MzY5OCJdLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgZztcclxuXHJcbi8vIFRoaXMgd29ya3MgaW4gbm9uLXN0cmljdCBtb2RlXHJcbmcgPSAoZnVuY3Rpb24oKSB7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn0pKCk7XHJcblxyXG50cnkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgZXZhbCBpcyBhbGxvd2VkIChzZWUgQ1NQKVxyXG5cdGcgPSBnIHx8IEZ1bmN0aW9uKFwicmV0dXJuIHRoaXNcIikoKSB8fCAoMSxldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2goZSkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXHJcblx0aWYodHlwZW9mIHdpbmRvdyA9PT0gXCJvYmplY3RcIilcclxuXHRcdGcgPSB3aW5kb3c7XHJcbn1cclxuXHJcbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cclxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3NcclxuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBnO1xyXG5cblxuXG4vLy8vLy8vLy8vLy8vLy8vLy9cbi8vIFdFQlBBQ0sgRk9PVEVSXG4vLyAod2VicGFjaykvYnVpbGRpbi9nbG9iYWwuanNcbi8vIG1vZHVsZSBpZCA9IDBcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9"); - -/***/ }), -/* 1 */ -/* unknown exports provided */ -/* all exports used */ -/*!**********************************!*\ - !*** ./~/zenscroll/zenscroll.js ***! - \**********************************/ -/***/ (function(module, exports, __webpack_require__) { - -eval("var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/**\n * Zenscroll 4.0.0\n * https://github.com/zengabor/zenscroll/\n *\n * Copyright 2015–2017 Gabor Lenard\n *\n * This is free and unencumbered software released into the public domain.\n * \n * Anyone is free to copy, modify, publish, use, compile, sell, or\n * distribute this software, either in source code form or as a compiled\n * binary, for any purpose, commercial or non-commercial, and by any\n * means.\n * \n * In jurisdictions that recognize copyright laws, the author or authors\n * of this software dedicate any and all copyright interest in the\n * software to the public domain. We make this dedication for the benefit\n * of the public at large and to the detriment of our heirs and\n * successors. We intend this dedication to be an overt act of\n * relinquishment in perpetuity of all present and future rights to this\n * software under copyright law.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n * \n * For more information, please refer to \n * \n */\n\n/*jshint devel:true, asi:true */\n\n/*global define, module */\n\n\n(function (root, factory) {\n\tif (true) {\n\t\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory()),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?\n\t\t\t\t(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))\n\t} else if (typeof module === \"object\" && module.exports) {\n\t\tmodule.exports = factory()\n\t} else {\n\t\t(function install() {\n\t\t\t// To make sure Zenscroll can be referenced from the header, before `body` is available\n\t\t\tif (document && document.body) {\n\t\t\t\troot.zenscroll = factory()\n\t\t\t} else {\n\t\t\t\t// retry 9ms later\n\t\t\t\tsetTimeout(install, 9)\n\t\t\t}\n\t\t})()\n\t}\n}(this, function () {\n\t\"use strict\"\n\n\n\t// Detect if the browser already supports native smooth scrolling (e.g., Firefox 36+ and Chrome 49+) and it is enabled:\n\tvar isNativeSmoothScrollEnabledOn = function (elem) {\n\t\treturn (\"getComputedStyle\" in window) &&\n\t\t\twindow.getComputedStyle(elem)[\"scroll-behavior\"] === \"smooth\"\n\t}\n\n\n\t// Exit if it’s not a browser environment:\n\tif (typeof window === \"undefined\" || !(\"document\" in window)) {\n\t\treturn {}\n\t}\n\n\n\tvar makeScroller = function (container, defaultDuration, edgeOffset) {\n\n\t\t// Use defaults if not provided\n\t\tdefaultDuration = defaultDuration || 999 //ms\n\t\tif (!edgeOffset && edgeOffset !== 0) {\n\t\t\t// When scrolling, this amount of distance is kept from the edges of the container:\n\t\t\tedgeOffset = 9 //px\n\t\t}\n\n\t\t// Handling the life-cycle of the scroller\n\t\tvar scrollTimeoutId\n\t\tvar setScrollTimeoutId = function (newValue) {\n\t\t\tscrollTimeoutId = newValue\n\t\t}\n\n\t\t/**\n\t\t * Stop the current smooth scroll operation immediately\n\t\t */\n\t\tvar stopScroll = function () {\n\t\t\tclearTimeout(scrollTimeoutId)\n\t\t\tsetScrollTimeoutId(0)\n\t\t}\n\n\t\tvar getTopWithEdgeOffset = function (elem) {\n\t\t\treturn Math.max(0, container.getTopOf(elem) - edgeOffset)\n\t\t}\n\n\t\t/**\n\t\t * Scrolls to a specific vertical position in the document.\n\t\t *\n\t\t * @param {targetY} The vertical position within the document.\n\t\t * @param {duration} Optionally the duration of the scroll operation.\n\t\t * If not provided the default duration is used.\n\t\t * @param {onDone} An optional callback function to be invoked once the scroll finished.\n\t\t */\n\t\tvar scrollToY = function (targetY, duration, onDone) {\n\t\t\tstopScroll()\n\t\t\tif (duration === 0 || (duration && duration < 0) || isNativeSmoothScrollEnabledOn(container.body)) {\n\t\t\t\tcontainer.toY(targetY)\n\t\t\t\tif (onDone) {\n\t\t\t\t\tonDone()\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar startY = container.getY()\n\t\t\t\tvar distance = Math.max(0, targetY) - startY\n\t\t\t\tvar startTime = new Date().getTime()\n\t\t\t\tduration = duration || Math.min(Math.abs(distance), defaultDuration);\n\t\t\t\t(function loopScroll() {\n\t\t\t\t\tsetScrollTimeoutId(setTimeout(function () {\n\t\t\t\t\t\t// Calculate percentage:\n\t\t\t\t\t\tvar p = Math.min(1, (new Date().getTime() - startTime) / duration)\n\t\t\t\t\t\t// Calculate the absolute vertical position:\n\t\t\t\t\t\tvar y = Math.max(0, Math.floor(startY + distance*(p < 0.5 ? 2*p*p : p*(4 - p*2)-1)))\n\t\t\t\t\t\tcontainer.toY(y)\n\t\t\t\t\t\tif (p < 1 && (container.getHeight() + y) < container.body.scrollHeight) {\n\t\t\t\t\t\t\tloopScroll()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsetTimeout(stopScroll, 99) // with cooldown time\n\t\t\t\t\t\t\tif (onDone) {\n\t\t\t\t\t\t\t\tonDone()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 9))\n\t\t\t\t})()\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Scrolls to the top of a specific element.\n\t\t *\n\t\t * @param {elem} The element to scroll to.\n\t\t * @param {duration} Optionally the duration of the scroll operation.\n\t\t * @param {onDone} An optional callback function to be invoked once the scroll finished.\n\t\t */\n\t\tvar scrollToElem = function (elem, duration, onDone) {\n\t\t\tscrollToY(getTopWithEdgeOffset(elem), duration, onDone)\n\t\t}\n\n\t\t/**\n\t\t * Scrolls an element into view if necessary.\n\t\t *\n\t\t * @param {elem} The element.\n\t\t * @param {duration} Optionally the duration of the scroll operation.\n\t\t * @param {onDone} An optional callback function to be invoked once the scroll finished.\n\t\t */\n\t\tvar scrollIntoView = function (elem, duration, onDone) {\n\t\t\tvar elemHeight = elem.getBoundingClientRect().height\n\t\t\tvar elemBottom = container.getTopOf(elem) + elemHeight\n\t\t\tvar containerHeight = container.getHeight()\n\t\t\tvar y = container.getY()\n\t\t\tvar containerBottom = y + containerHeight\n\t\t\tif (getTopWithEdgeOffset(elem) < y || (elemHeight + edgeOffset) > containerHeight) {\n\t\t\t\t// Element is clipped at top or is higher than screen.\n\t\t\t\tscrollToElem(elem, duration, onDone)\n\t\t\t} else if ((elemBottom + edgeOffset) > containerBottom) {\n\t\t\t\t// Element is clipped at the bottom.\n\t\t\t\tscrollToY(elemBottom - containerHeight + edgeOffset, duration, onDone)\n\t\t\t} else if (onDone) {\n\t\t\t\tonDone()\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Scrolls to the center of an element.\n\t\t *\n\t\t * @param {elem} The element.\n\t\t * @param {duration} Optionally the duration of the scroll operation.\n\t\t * @param {offset} Optionally the offset of the top of the element from the center of the screen.\n\t\t * @param {onDone} An optional callback function to be invoked once the scroll finished.\n\t\t */\n\t\tvar scrollToCenterOf = function (elem, duration, offset, onDone) {\n\t\t\tscrollToY(Math.max(0, container.getTopOf(elem) - container.getHeight()/2 + (offset || elem.getBoundingClientRect().height/2)), duration, onDone)\n\t\t}\n\n\t\t/**\n\t\t * Changes default settings for this scroller.\n\t\t *\n\t\t * @param {newDefaultDuration} Optionally a new value for default duration, used for each scroll method by default.\n\t\t * Ignored if null or undefined.\n\t\t * @param {newEdgeOffset} Optionally a new value for the edge offset, used by each scroll method by default. Ignored if null or undefined.\n\t\t * @returns An object with the current values.\n\t\t */\n\t\tvar setup = function (newDefaultDuration, newEdgeOffset) {\n\t\t\tif (newDefaultDuration === 0 || newDefaultDuration) {\n\t\t\t\tdefaultDuration = newDefaultDuration\n\t\t\t}\n\t\t\tif (newEdgeOffset === 0 || newEdgeOffset) {\n\t\t\t\tedgeOffset = newEdgeOffset\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tdefaultDuration: defaultDuration,\n\t\t\t\tedgeOffset: edgeOffset\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tsetup: setup,\n\t\t\tto: scrollToElem,\n\t\t\ttoY: scrollToY,\n\t\t\tintoView: scrollIntoView,\n\t\t\tcenter: scrollToCenterOf,\n\t\t\tstop: stopScroll,\n\t\t\tmoving: function () { return !!scrollTimeoutId },\n\t\t\tgetY: container.getY,\n\t\t\tgetTopOf: container.getTopOf\n\t\t}\n\n\t}\n\n\n\tvar docElem = document.documentElement\n\tvar getDocY = function () { return window.scrollY || docElem.scrollTop }\n\n\t// Create a scroller for the document:\n\tvar zenscroll = makeScroller({\n\t\tbody: document.scrollingElement || document.body,\n\t\ttoY: function (y) { window.scrollTo(0, y) },\n\t\tgetY: getDocY,\n\t\tgetHeight: function () { return window.innerHeight || docElem.clientHeight },\n\t\tgetTopOf: function (elem) { return elem.getBoundingClientRect().top + getDocY() - docElem.offsetTop }\n\t})\n\n\n\t/**\n\t * Creates a scroller from the provided container element (e.g., a DIV)\n\t *\n\t * @param {scrollContainer} The vertical position within the document.\n\t * @param {defaultDuration} Optionally a value for default duration, used for each scroll method by default.\n\t * Ignored if 0 or null or undefined.\n\t * @param {edgeOffset} Optionally a value for the edge offset, used by each scroll method by default. \n\t * Ignored if null or undefined.\n\t * @returns A scroller object, similar to `zenscroll` but controlling the provided element.\n\t */\n\tzenscroll.createScroller = function (scrollContainer, defaultDuration, edgeOffset) {\n\t\treturn makeScroller({\n\t\t\tbody: scrollContainer,\n\t\t\ttoY: function (y) { scrollContainer.scrollTop = y },\n\t\t\tgetY: function () { return scrollContainer.scrollTop },\n\t\t\tgetHeight: function () { return Math.min(scrollContainer.clientHeight, window.innerHeight || docElem.clientHeight) },\n\t\t\tgetTopOf: function (elem) { return elem.offsetTop }\n\t\t}, defaultDuration, edgeOffset)\n\t}\n\n\n\t// Automatic link-smoothing on achors\n\t// Exclude IE8- or when native is enabled or Zenscroll auto- is disabled\n\tif (\"addEventListener\" in window && !window.noZensmooth && !isNativeSmoothScrollEnabledOn(document.body)) {\n\n\n\t\tvar isScrollRestorationSupported = \"scrollRestoration\" in history\n\n\t\t// On first load & refresh make sure the browser restores the position first\n\t\tif (isScrollRestorationSupported) {\n\t\t\thistory.scrollRestoration = \"auto\"\n\t\t}\n\n\t\twindow.addEventListener(\"load\", function () {\n\n\t\t\tif (isScrollRestorationSupported) {\n\t\t\t\t// Set it to manual\n\t\t\t\tsetTimeout(function () { history.scrollRestoration = \"manual\" }, 9)\n\t\t\t\twindow.addEventListener(\"popstate\", function (event) {\n\t\t\t\t\tif (event.state && \"zenscrollY\" in event.state) {\n\t\t\t\t\t\tzenscroll.toY(event.state.zenscrollY)\n\t\t\t\t\t}\n\t\t\t\t}, false)\n\t\t\t}\n\n\t\t\t// Add edge offset on first load if necessary\n\t\t\t// This may not work on IE (or older computer?) as it requires more timeout, around 100 ms\n\t\t\tif (window.location.hash) {\n\t\t\t\tsetTimeout(function () {\n\t\t\t\t\t// Adjustment is only needed if there is an edge offset:\n\t\t\t\t\tvar edgeOffset = zenscroll.setup().edgeOffset\n\t\t\t\t\tif (edgeOffset) {\n\t\t\t\t\t\tvar targetElem = document.getElementById(window.location.href.split(\"#\")[1])\n\t\t\t\t\t\tif (targetElem) {\n\t\t\t\t\t\t\tvar targetY = Math.max(0, zenscroll.getTopOf(targetElem) - edgeOffset)\n\t\t\t\t\t\t\tvar diff = zenscroll.getY() - targetY\n\t\t\t\t\t\t\t// Only do the adjustment if the browser is very close to the element:\n\t\t\t\t\t\t\tif (0 <= diff && diff < 9 ) {\n\t\t\t\t\t\t\t\twindow.scrollTo(0, targetY)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, 9)\n\t\t\t}\n\n\t\t}, false)\n\n\t\t// Handling clicks on anchors\n\t\tvar RE_noZensmooth = new RegExp(\"(^|\\\\s)noZensmooth(\\\\s|$)\")\n\t\twindow.addEventListener(\"click\", function (event) {\n\t\t\tvar anchor = event.target\n\t\t\twhile (anchor && anchor.tagName !== \"A\") {\n\t\t\t\tanchor = anchor.parentNode\n\t\t\t}\n\t\t\t// Let the browser handle the click if it wasn't with the primary button, or with some modifier keys:\n\t\t\tif (!anchor || event.which !== 1 || event.shiftKey || event.metaKey || event.ctrlKey || event.altKey) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Save the current scrolling position so it can be used for scroll restoration:\n\t\t\tif (isScrollRestorationSupported) {\n\t\t\t\ttry {\n\t\t\t\t\thistory.replaceState({ zenscrollY: zenscroll.getY() }, \"\")\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// Avoid the Chrome Security exception on file protocol, e.g., file://index.html\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Find the referenced ID:\n\t\t\tvar href = anchor.getAttribute(\"href\") || \"\"\n\t\t\tif (href.indexOf(\"#\") === 0 && !RE_noZensmooth.test(anchor.className)) {\n\t\t\t\tvar targetY = 0\n\t\t\t\tvar targetElem = document.getElementById(href.substring(1))\n\t\t\t\tif (href !== \"#\") {\n\t\t\t\t\tif (!targetElem) {\n\t\t\t\t\t\t// Let the browser handle the click if the target ID is not found.\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\ttargetY = zenscroll.getTopOf(targetElem)\n\t\t\t\t}\n\t\t\t\tevent.preventDefault()\n\t\t\t\t// By default trigger the browser's `hashchange` event...\n\t\t\t\tvar onDone = function () { window.location = href }\n\t\t\t\t// ...unless there is an edge offset specified\n\t\t\t\tvar edgeOffset = zenscroll.setup().edgeOffset\n\t\t\t\tif (edgeOffset) {\n\t\t\t\t\ttargetY = Math.max(0, targetY - edgeOffset)\n\t\t\t\t\tonDone = function () { history.pushState(null, \"\", href) }\n\t\t\t\t}\n\t\t\t\tzenscroll.toY(targetY, null, onDone)\n\t\t\t}\n\t\t}, false)\n\n\t}\n\n\n\treturn zenscroll\n\n\n}));\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL34vemVuc2Nyb2xsL3plbnNjcm9sbC5qcz8yNzMyIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogWmVuc2Nyb2xsIDQuMC4wXG4gKiBodHRwczovL2dpdGh1Yi5jb20vemVuZ2Fib3IvemVuc2Nyb2xsL1xuICpcbiAqIENvcHlyaWdodCAyMDE14oCTMjAxNyBHYWJvciBMZW5hcmRcbiAqXG4gKiBUaGlzIGlzIGZyZWUgYW5kIHVuZW5jdW1iZXJlZCBzb2Z0d2FyZSByZWxlYXNlZCBpbnRvIHRoZSBwdWJsaWMgZG9tYWluLlxuICogXG4gKiBBbnlvbmUgaXMgZnJlZSB0byBjb3B5LCBtb2RpZnksIHB1Ymxpc2gsIHVzZSwgY29tcGlsZSwgc2VsbCwgb3JcbiAqIGRpc3RyaWJ1dGUgdGhpcyBzb2Z0d2FyZSwgZWl0aGVyIGluIHNvdXJjZSBjb2RlIGZvcm0gb3IgYXMgYSBjb21waWxlZFxuICogYmluYXJ5LCBmb3IgYW55IHB1cnBvc2UsIGNvbW1lcmNpYWwgb3Igbm9uLWNvbW1lcmNpYWwsIGFuZCBieSBhbnlcbiAqIG1lYW5zLlxuICogXG4gKiBJbiBqdXJpc2RpY3Rpb25zIHRoYXQgcmVjb2duaXplIGNvcHlyaWdodCBsYXdzLCB0aGUgYXV0aG9yIG9yIGF1dGhvcnNcbiAqIG9mIHRoaXMgc29mdHdhcmUgZGVkaWNhdGUgYW55IGFuZCBhbGwgY29weXJpZ2h0IGludGVyZXN0IGluIHRoZVxuICogc29mdHdhcmUgdG8gdGhlIHB1YmxpYyBkb21haW4uIFdlIG1ha2UgdGhpcyBkZWRpY2F0aW9uIGZvciB0aGUgYmVuZWZpdFxuICogb2YgdGhlIHB1YmxpYyBhdCBsYXJnZSBhbmQgdG8gdGhlIGRldHJpbWVudCBvZiBvdXIgaGVpcnMgYW5kXG4gKiBzdWNjZXNzb3JzLiBXZSBpbnRlbmQgdGhpcyBkZWRpY2F0aW9uIHRvIGJlIGFuIG92ZXJ0IGFjdCBvZlxuICogcmVsaW5xdWlzaG1lbnQgaW4gcGVycGV0dWl0eSBvZiBhbGwgcHJlc2VudCBhbmQgZnV0dXJlIHJpZ2h0cyB0byB0aGlzXG4gKiBzb2Z0d2FyZSB1bmRlciBjb3B5cmlnaHQgbGF3LlxuICogXG4gKiBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELFxuICogRVhQUkVTUyBPUiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GXG4gKiBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuXG4gKiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUlxuICogT1RIRVIgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsXG4gKiBBUklTSU5HIEZST00sIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1JcbiAqIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS5cbiAqIFxuICogRm9yIG1vcmUgaW5mb3JtYXRpb24sIHBsZWFzZSByZWZlciB0byA8aHR0cDovL3VubGljZW5zZS5vcmc+XG4gKiBcbiAqL1xuXG4vKmpzaGludCBkZXZlbDp0cnVlLCBhc2k6dHJ1ZSAqL1xuXG4vKmdsb2JhbCBkZWZpbmUsIG1vZHVsZSAqL1xuXG5cbihmdW5jdGlvbiAocm9vdCwgZmFjdG9yeSkge1xuXHRpZiAodHlwZW9mIGRlZmluZSA9PT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIHtcblx0XHRkZWZpbmUoW10sIGZhY3RvcnkoKSlcblx0fSBlbHNlIGlmICh0eXBlb2YgbW9kdWxlID09PSBcIm9iamVjdFwiICYmIG1vZHVsZS5leHBvcnRzKSB7XG5cdFx0bW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KClcblx0fSBlbHNlIHtcblx0XHQoZnVuY3Rpb24gaW5zdGFsbCgpIHtcblx0XHRcdC8vIFRvIG1ha2Ugc3VyZSBaZW5zY3JvbGwgY2FuIGJlIHJlZmVyZW5jZWQgZnJvbSB0aGUgaGVhZGVyLCBiZWZvcmUgYGJvZHlgIGlzIGF2YWlsYWJsZVxuXHRcdFx0aWYgKGRvY3VtZW50ICYmIGRvY3VtZW50LmJvZHkpIHtcblx0XHRcdFx0cm9vdC56ZW5zY3JvbGwgPSBmYWN0b3J5KClcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8vIHJldHJ5IDltcyBsYXRlclxuXHRcdFx0XHRzZXRUaW1lb3V0KGluc3RhbGwsIDkpXG5cdFx0XHR9XG5cdFx0fSkoKVxuXHR9XG59KHRoaXMsIGZ1bmN0aW9uICgpIHtcblx0XCJ1c2Ugc3RyaWN0XCJcblxuXG5cdC8vIERldGVjdCBpZiB0aGUgYnJvd3NlciBhbHJlYWR5IHN1cHBvcnRzIG5hdGl2ZSBzbW9vdGggc2Nyb2xsaW5nIChlLmcuLCBGaXJlZm94IDM2KyBhbmQgQ2hyb21lIDQ5KykgYW5kIGl0IGlzIGVuYWJsZWQ6XG5cdHZhciBpc05hdGl2ZVNtb290aFNjcm9sbEVuYWJsZWRPbiA9IGZ1bmN0aW9uIChlbGVtKSB7XG5cdFx0cmV0dXJuIChcImdldENvbXB1dGVkU3R5bGVcIiBpbiB3aW5kb3cpICYmXG5cdFx0XHR3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShlbGVtKVtcInNjcm9sbC1iZWhhdmlvclwiXSA9PT0gXCJzbW9vdGhcIlxuXHR9XG5cblxuXHQvLyBFeGl0IGlmIGl04oCZcyBub3QgYSBicm93c2VyIGVudmlyb25tZW50OlxuXHRpZiAodHlwZW9mIHdpbmRvdyA9PT0gXCJ1bmRlZmluZWRcIiB8fCAhKFwiZG9jdW1lbnRcIiBpbiB3aW5kb3cpKSB7XG5cdFx0cmV0dXJuIHt9XG5cdH1cblxuXG5cdHZhciBtYWtlU2Nyb2xsZXIgPSBmdW5jdGlvbiAoY29udGFpbmVyLCBkZWZhdWx0RHVyYXRpb24sIGVkZ2VPZmZzZXQpIHtcblxuXHRcdC8vIFVzZSBkZWZhdWx0cyBpZiBub3QgcHJvdmlkZWRcblx0XHRkZWZhdWx0RHVyYXRpb24gPSBkZWZhdWx0RHVyYXRpb24gfHwgOTk5IC8vbXNcblx0XHRpZiAoIWVkZ2VPZmZzZXQgJiYgZWRnZU9mZnNldCAhPT0gMCkge1xuXHRcdFx0Ly8gV2hlbiBzY3JvbGxpbmcsIHRoaXMgYW1vdW50IG9mIGRpc3RhbmNlIGlzIGtlcHQgZnJvbSB0aGUgZWRnZXMgb2YgdGhlIGNvbnRhaW5lcjpcblx0XHRcdGVkZ2VPZmZzZXQgPSA5IC8vcHhcblx0XHR9XG5cblx0XHQvLyBIYW5kbGluZyB0aGUgbGlmZS1jeWNsZSBvZiB0aGUgc2Nyb2xsZXJcblx0XHR2YXIgc2Nyb2xsVGltZW91dElkXG5cdFx0dmFyIHNldFNjcm9sbFRpbWVvdXRJZCA9IGZ1bmN0aW9uIChuZXdWYWx1ZSkge1xuXHRcdFx0c2Nyb2xsVGltZW91dElkID0gbmV3VmFsdWVcblx0XHR9XG5cblx0XHQvKipcblx0XHQgKiBTdG9wIHRoZSBjdXJyZW50IHNtb290aCBzY3JvbGwgb3BlcmF0aW9uIGltbWVkaWF0ZWx5XG5cdFx0ICovXG5cdFx0dmFyIHN0b3BTY3JvbGwgPSBmdW5jdGlvbiAoKSB7XG5cdFx0XHRjbGVhclRpbWVvdXQoc2Nyb2xsVGltZW91dElkKVxuXHRcdFx0c2V0U2Nyb2xsVGltZW91dElkKDApXG5cdFx0fVxuXG5cdFx0dmFyIGdldFRvcFdpdGhFZGdlT2Zmc2V0ID0gZnVuY3Rpb24gKGVsZW0pIHtcblx0XHRcdHJldHVybiBNYXRoLm1heCgwLCBjb250YWluZXIuZ2V0VG9wT2YoZWxlbSkgLSBlZGdlT2Zmc2V0KVxuXHRcdH1cblxuXHRcdC8qKlxuXHRcdCAqIFNjcm9sbHMgdG8gYSBzcGVjaWZpYyB2ZXJ0aWNhbCBwb3NpdGlvbiBpbiB0aGUgZG9jdW1lbnQuXG5cdFx0ICpcblx0XHQgKiBAcGFyYW0ge3RhcmdldFl9IFRoZSB2ZXJ0aWNhbCBwb3NpdGlvbiB3aXRoaW4gdGhlIGRvY3VtZW50LlxuXHRcdCAqIEBwYXJhbSB7ZHVyYXRpb259IE9wdGlvbmFsbHkgdGhlIGR1cmF0aW9uIG9mIHRoZSBzY3JvbGwgb3BlcmF0aW9uLlxuXHRcdCAqICAgICAgICBJZiBub3QgcHJvdmlkZWQgdGhlIGRlZmF1bHQgZHVyYXRpb24gaXMgdXNlZC5cblx0XHQgKiBAcGFyYW0ge29uRG9uZX0gQW4gb3B0aW9uYWwgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCBvbmNlIHRoZSBzY3JvbGwgZmluaXNoZWQuXG5cdFx0ICovXG5cdFx0dmFyIHNjcm9sbFRvWSA9IGZ1bmN0aW9uICh0YXJnZXRZLCBkdXJhdGlvbiwgb25Eb25lKSB7XG5cdFx0XHRzdG9wU2Nyb2xsKClcblx0XHRcdGlmIChkdXJhdGlvbiA9PT0gMCB8fCAoZHVyYXRpb24gJiYgZHVyYXRpb24gPCAwKSB8fCBpc05hdGl2ZVNtb290aFNjcm9sbEVuYWJsZWRPbihjb250YWluZXIuYm9keSkpIHtcblx0XHRcdFx0Y29udGFpbmVyLnRvWSh0YXJnZXRZKVxuXHRcdFx0XHRpZiAob25Eb25lKSB7XG5cdFx0XHRcdFx0b25Eb25lKClcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dmFyIHN0YXJ0WSA9IGNvbnRhaW5lci5nZXRZKClcblx0XHRcdFx0dmFyIGRpc3RhbmNlID0gTWF0aC5tYXgoMCwgdGFyZ2V0WSkgLSBzdGFydFlcblx0XHRcdFx0dmFyIHN0YXJ0VGltZSA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpXG5cdFx0XHRcdGR1cmF0aW9uID0gZHVyYXRpb24gfHwgTWF0aC5taW4oTWF0aC5hYnMoZGlzdGFuY2UpLCBkZWZhdWx0RHVyYXRpb24pO1xuXHRcdFx0XHQoZnVuY3Rpb24gbG9vcFNjcm9sbCgpIHtcblx0XHRcdFx0XHRzZXRTY3JvbGxUaW1lb3V0SWQoc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdFx0XHQvLyBDYWxjdWxhdGUgcGVyY2VudGFnZTpcblx0XHRcdFx0XHRcdHZhciBwID0gTWF0aC5taW4oMSwgKG5ldyBEYXRlKCkuZ2V0VGltZSgpIC0gc3RhcnRUaW1lKSAvIGR1cmF0aW9uKVxuXHRcdFx0XHRcdFx0Ly8gQ2FsY3VsYXRlIHRoZSBhYnNvbHV0ZSB2ZXJ0aWNhbCBwb3NpdGlvbjpcblx0XHRcdFx0XHRcdHZhciB5ID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihzdGFydFkgKyBkaXN0YW5jZSoocCA8IDAuNSA/IDIqcCpwIDogcCooNCAtIHAqMiktMSkpKVxuXHRcdFx0XHRcdFx0Y29udGFpbmVyLnRvWSh5KVxuXHRcdFx0XHRcdFx0aWYgKHAgPCAxICYmIChjb250YWluZXIuZ2V0SGVpZ2h0KCkgKyB5KSA8IGNvbnRhaW5lci5ib2R5LnNjcm9sbEhlaWdodCkge1xuXHRcdFx0XHRcdFx0XHRsb29wU2Nyb2xsKClcblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdHNldFRpbWVvdXQoc3RvcFNjcm9sbCwgOTkpIC8vIHdpdGggY29vbGRvd24gdGltZVxuXHRcdFx0XHRcdFx0XHRpZiAob25Eb25lKSB7XG5cdFx0XHRcdFx0XHRcdFx0b25Eb25lKClcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0sIDkpKVxuXHRcdFx0XHR9KSgpXG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0LyoqXG5cdFx0ICogU2Nyb2xscyB0byB0aGUgdG9wIG9mIGEgc3BlY2lmaWMgZWxlbWVudC5cblx0XHQgKlxuXHRcdCAqIEBwYXJhbSB7ZWxlbX0gVGhlIGVsZW1lbnQgdG8gc2Nyb2xsIHRvLlxuXHRcdCAqIEBwYXJhbSB7ZHVyYXRpb259IE9wdGlvbmFsbHkgdGhlIGR1cmF0aW9uIG9mIHRoZSBzY3JvbGwgb3BlcmF0aW9uLlxuXHRcdCAqIEBwYXJhbSB7b25Eb25lfSBBbiBvcHRpb25hbCBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkIG9uY2UgdGhlIHNjcm9sbCBmaW5pc2hlZC5cblx0XHQgKi9cblx0XHR2YXIgc2Nyb2xsVG9FbGVtID0gZnVuY3Rpb24gKGVsZW0sIGR1cmF0aW9uLCBvbkRvbmUpIHtcblx0XHRcdHNjcm9sbFRvWShnZXRUb3BXaXRoRWRnZU9mZnNldChlbGVtKSwgZHVyYXRpb24sIG9uRG9uZSlcblx0XHR9XG5cblx0XHQvKipcblx0XHQgKiBTY3JvbGxzIGFuIGVsZW1lbnQgaW50byB2aWV3IGlmIG5lY2Vzc2FyeS5cblx0XHQgKlxuXHRcdCAqIEBwYXJhbSB7ZWxlbX0gVGhlIGVsZW1lbnQuXG5cdFx0ICogQHBhcmFtIHtkdXJhdGlvbn0gT3B0aW9uYWxseSB0aGUgZHVyYXRpb24gb2YgdGhlIHNjcm9sbCBvcGVyYXRpb24uXG5cdFx0ICogQHBhcmFtIHtvbkRvbmV9IEFuIG9wdGlvbmFsIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgb25jZSB0aGUgc2Nyb2xsIGZpbmlzaGVkLlxuXHRcdCAqL1xuXHRcdHZhciBzY3JvbGxJbnRvVmlldyA9IGZ1bmN0aW9uIChlbGVtLCBkdXJhdGlvbiwgb25Eb25lKSB7XG5cdFx0XHR2YXIgZWxlbUhlaWdodCA9IGVsZW0uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuaGVpZ2h0XG5cdFx0XHR2YXIgZWxlbUJvdHRvbSA9IGNvbnRhaW5lci5nZXRUb3BPZihlbGVtKSArIGVsZW1IZWlnaHRcblx0XHRcdHZhciBjb250YWluZXJIZWlnaHQgPSBjb250YWluZXIuZ2V0SGVpZ2h0KClcblx0XHRcdHZhciB5ID0gY29udGFpbmVyLmdldFkoKVxuXHRcdFx0dmFyIGNvbnRhaW5lckJvdHRvbSA9IHkgKyBjb250YWluZXJIZWlnaHRcblx0XHRcdGlmIChnZXRUb3BXaXRoRWRnZU9mZnNldChlbGVtKSA8IHkgfHwgKGVsZW1IZWlnaHQgKyBlZGdlT2Zmc2V0KSA+IGNvbnRhaW5lckhlaWdodCkge1xuXHRcdFx0XHQvLyBFbGVtZW50IGlzIGNsaXBwZWQgYXQgdG9wIG9yIGlzIGhpZ2hlciB0aGFuIHNjcmVlbi5cblx0XHRcdFx0c2Nyb2xsVG9FbGVtKGVsZW0sIGR1cmF0aW9uLCBvbkRvbmUpXG5cdFx0XHR9IGVsc2UgaWYgKChlbGVtQm90dG9tICsgZWRnZU9mZnNldCkgPiBjb250YWluZXJCb3R0b20pIHtcblx0XHRcdFx0Ly8gRWxlbWVudCBpcyBjbGlwcGVkIGF0IHRoZSBib3R0b20uXG5cdFx0XHRcdHNjcm9sbFRvWShlbGVtQm90dG9tIC0gY29udGFpbmVySGVpZ2h0ICsgZWRnZU9mZnNldCwgZHVyYXRpb24sIG9uRG9uZSlcblx0XHRcdH0gZWxzZSBpZiAob25Eb25lKSB7XG5cdFx0XHRcdG9uRG9uZSgpXG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0LyoqXG5cdFx0ICogU2Nyb2xscyB0byB0aGUgY2VudGVyIG9mIGFuIGVsZW1lbnQuXG5cdFx0ICpcblx0XHQgKiBAcGFyYW0ge2VsZW19IFRoZSBlbGVtZW50LlxuXHRcdCAqIEBwYXJhbSB7ZHVyYXRpb259IE9wdGlvbmFsbHkgdGhlIGR1cmF0aW9uIG9mIHRoZSBzY3JvbGwgb3BlcmF0aW9uLlxuXHRcdCAqIEBwYXJhbSB7b2Zmc2V0fSBPcHRpb25hbGx5IHRoZSBvZmZzZXQgb2YgdGhlIHRvcCBvZiB0aGUgZWxlbWVudCBmcm9tIHRoZSBjZW50ZXIgb2YgdGhlIHNjcmVlbi5cblx0XHQgKiBAcGFyYW0ge29uRG9uZX0gQW4gb3B0aW9uYWwgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCBvbmNlIHRoZSBzY3JvbGwgZmluaXNoZWQuXG5cdFx0ICovXG5cdFx0dmFyIHNjcm9sbFRvQ2VudGVyT2YgPSBmdW5jdGlvbiAoZWxlbSwgZHVyYXRpb24sIG9mZnNldCwgb25Eb25lKSB7XG5cdFx0XHRzY3JvbGxUb1koTWF0aC5tYXgoMCwgY29udGFpbmVyLmdldFRvcE9mKGVsZW0pIC0gY29udGFpbmVyLmdldEhlaWdodCgpLzIgKyAob2Zmc2V0IHx8IGVsZW0uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuaGVpZ2h0LzIpKSwgZHVyYXRpb24sIG9uRG9uZSlcblx0XHR9XG5cblx0XHQvKipcblx0XHQgKiBDaGFuZ2VzIGRlZmF1bHQgc2V0dGluZ3MgZm9yIHRoaXMgc2Nyb2xsZXIuXG5cdFx0ICpcblx0XHQgKiBAcGFyYW0ge25ld0RlZmF1bHREdXJhdGlvbn0gT3B0aW9uYWxseSBhIG5ldyB2YWx1ZSBmb3IgZGVmYXVsdCBkdXJhdGlvbiwgdXNlZCBmb3IgZWFjaCBzY3JvbGwgbWV0aG9kIGJ5IGRlZmF1bHQuXG5cdFx0ICogICAgICAgIElnbm9yZWQgaWYgbnVsbCBvciB1bmRlZmluZWQuXG5cdFx0ICogQHBhcmFtIHtuZXdFZGdlT2Zmc2V0fSBPcHRpb25hbGx5IGEgbmV3IHZhbHVlIGZvciB0aGUgZWRnZSBvZmZzZXQsIHVzZWQgYnkgZWFjaCBzY3JvbGwgbWV0aG9kIGJ5IGRlZmF1bHQuIElnbm9yZWQgaWYgbnVsbCBvciB1bmRlZmluZWQuXG5cdFx0ICogQHJldHVybnMgQW4gb2JqZWN0IHdpdGggdGhlIGN1cnJlbnQgdmFsdWVzLlxuXHRcdCAqL1xuXHRcdHZhciBzZXR1cCA9IGZ1bmN0aW9uIChuZXdEZWZhdWx0RHVyYXRpb24sIG5ld0VkZ2VPZmZzZXQpIHtcblx0XHRcdGlmIChuZXdEZWZhdWx0RHVyYXRpb24gPT09IDAgfHwgbmV3RGVmYXVsdER1cmF0aW9uKSB7XG5cdFx0XHRcdGRlZmF1bHREdXJhdGlvbiA9IG5ld0RlZmF1bHREdXJhdGlvblxuXHRcdFx0fVxuXHRcdFx0aWYgKG5ld0VkZ2VPZmZzZXQgPT09IDAgfHwgbmV3RWRnZU9mZnNldCkge1xuXHRcdFx0XHRlZGdlT2Zmc2V0ID0gbmV3RWRnZU9mZnNldFxuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIHtcblx0XHRcdFx0ZGVmYXVsdER1cmF0aW9uOiBkZWZhdWx0RHVyYXRpb24sXG5cdFx0XHRcdGVkZ2VPZmZzZXQ6IGVkZ2VPZmZzZXRcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4ge1xuXHRcdFx0c2V0dXA6IHNldHVwLFxuXHRcdFx0dG86IHNjcm9sbFRvRWxlbSxcblx0XHRcdHRvWTogc2Nyb2xsVG9ZLFxuXHRcdFx0aW50b1ZpZXc6IHNjcm9sbEludG9WaWV3LFxuXHRcdFx0Y2VudGVyOiBzY3JvbGxUb0NlbnRlck9mLFxuXHRcdFx0c3RvcDogc3RvcFNjcm9sbCxcblx0XHRcdG1vdmluZzogZnVuY3Rpb24gKCkgeyByZXR1cm4gISFzY3JvbGxUaW1lb3V0SWQgfSxcblx0XHRcdGdldFk6IGNvbnRhaW5lci5nZXRZLFxuXHRcdFx0Z2V0VG9wT2Y6IGNvbnRhaW5lci5nZXRUb3BPZlxuXHRcdH1cblxuXHR9XG5cblxuXHR2YXIgZG9jRWxlbSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudFxuXHR2YXIgZ2V0RG9jWSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHdpbmRvdy5zY3JvbGxZIHx8IGRvY0VsZW0uc2Nyb2xsVG9wIH1cblxuXHQvLyBDcmVhdGUgYSBzY3JvbGxlciBmb3IgdGhlIGRvY3VtZW50OlxuXHR2YXIgemVuc2Nyb2xsID0gbWFrZVNjcm9sbGVyKHtcblx0XHRib2R5OiBkb2N1bWVudC5zY3JvbGxpbmdFbGVtZW50IHx8IGRvY3VtZW50LmJvZHksXG5cdFx0dG9ZOiBmdW5jdGlvbiAoeSkgeyB3aW5kb3cuc2Nyb2xsVG8oMCwgeSkgfSxcblx0XHRnZXRZOiBnZXREb2NZLFxuXHRcdGdldEhlaWdodDogZnVuY3Rpb24gKCkgeyByZXR1cm4gd2luZG93LmlubmVySGVpZ2h0IHx8IGRvY0VsZW0uY2xpZW50SGVpZ2h0IH0sXG5cdFx0Z2V0VG9wT2Y6IGZ1bmN0aW9uIChlbGVtKSB7IHJldHVybiBlbGVtLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCArIGdldERvY1koKSAtIGRvY0VsZW0ub2Zmc2V0VG9wIH1cblx0fSlcblxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGEgc2Nyb2xsZXIgZnJvbSB0aGUgcHJvdmlkZWQgY29udGFpbmVyIGVsZW1lbnQgKGUuZy4sIGEgRElWKVxuXHQgKlxuXHQgKiBAcGFyYW0ge3Njcm9sbENvbnRhaW5lcn0gVGhlIHZlcnRpY2FsIHBvc2l0aW9uIHdpdGhpbiB0aGUgZG9jdW1lbnQuXG5cdCAqIEBwYXJhbSB7ZGVmYXVsdER1cmF0aW9ufSBPcHRpb25hbGx5IGEgdmFsdWUgZm9yIGRlZmF1bHQgZHVyYXRpb24sIHVzZWQgZm9yIGVhY2ggc2Nyb2xsIG1ldGhvZCBieSBkZWZhdWx0LlxuXHQgKiAgICAgICAgSWdub3JlZCBpZiAwIG9yIG51bGwgb3IgdW5kZWZpbmVkLlxuXHQgKiBAcGFyYW0ge2VkZ2VPZmZzZXR9IE9wdGlvbmFsbHkgYSB2YWx1ZSBmb3IgdGhlIGVkZ2Ugb2Zmc2V0LCB1c2VkIGJ5IGVhY2ggc2Nyb2xsIG1ldGhvZCBieSBkZWZhdWx0LiBcblx0ICogICAgICAgIElnbm9yZWQgaWYgbnVsbCBvciB1bmRlZmluZWQuXG5cdCAqIEByZXR1cm5zIEEgc2Nyb2xsZXIgb2JqZWN0LCBzaW1pbGFyIHRvIGB6ZW5zY3JvbGxgIGJ1dCBjb250cm9sbGluZyB0aGUgcHJvdmlkZWQgZWxlbWVudC5cblx0ICovXG5cdHplbnNjcm9sbC5jcmVhdGVTY3JvbGxlciA9IGZ1bmN0aW9uIChzY3JvbGxDb250YWluZXIsIGRlZmF1bHREdXJhdGlvbiwgZWRnZU9mZnNldCkge1xuXHRcdHJldHVybiBtYWtlU2Nyb2xsZXIoe1xuXHRcdFx0Ym9keTogc2Nyb2xsQ29udGFpbmVyLFxuXHRcdFx0dG9ZOiBmdW5jdGlvbiAoeSkgeyBzY3JvbGxDb250YWluZXIuc2Nyb2xsVG9wID0geSB9LFxuXHRcdFx0Z2V0WTogZnVuY3Rpb24gKCkgeyByZXR1cm4gc2Nyb2xsQ29udGFpbmVyLnNjcm9sbFRvcCB9LFxuXHRcdFx0Z2V0SGVpZ2h0OiBmdW5jdGlvbiAoKSB7IHJldHVybiBNYXRoLm1pbihzY3JvbGxDb250YWluZXIuY2xpZW50SGVpZ2h0LCB3aW5kb3cuaW5uZXJIZWlnaHQgfHwgZG9jRWxlbS5jbGllbnRIZWlnaHQpIH0sXG5cdFx0XHRnZXRUb3BPZjogZnVuY3Rpb24gKGVsZW0pIHsgcmV0dXJuIGVsZW0ub2Zmc2V0VG9wIH1cblx0XHR9LCBkZWZhdWx0RHVyYXRpb24sIGVkZ2VPZmZzZXQpXG5cdH1cblxuXG5cdC8vIEF1dG9tYXRpYyBsaW5rLXNtb290aGluZyBvbiBhY2hvcnNcblx0Ly8gRXhjbHVkZSBJRTgtIG9yIHdoZW4gbmF0aXZlIGlzIGVuYWJsZWQgb3IgWmVuc2Nyb2xsIGF1dG8tIGlzIGRpc2FibGVkXG5cdGlmIChcImFkZEV2ZW50TGlzdGVuZXJcIiBpbiB3aW5kb3cgJiYgIXdpbmRvdy5ub1plbnNtb290aCAmJiAhaXNOYXRpdmVTbW9vdGhTY3JvbGxFbmFibGVkT24oZG9jdW1lbnQuYm9keSkpIHtcblxuXG5cdFx0dmFyIGlzU2Nyb2xsUmVzdG9yYXRpb25TdXBwb3J0ZWQgPSBcInNjcm9sbFJlc3RvcmF0aW9uXCIgaW4gaGlzdG9yeVxuXG5cdFx0Ly8gT24gZmlyc3QgbG9hZCAmIHJlZnJlc2ggbWFrZSBzdXJlIHRoZSBicm93c2VyIHJlc3RvcmVzIHRoZSBwb3NpdGlvbiBmaXJzdFxuXHRcdGlmIChpc1Njcm9sbFJlc3RvcmF0aW9uU3VwcG9ydGVkKSB7XG5cdFx0XHRoaXN0b3J5LnNjcm9sbFJlc3RvcmF0aW9uID0gXCJhdXRvXCJcblx0XHR9XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcImxvYWRcIiwgZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRpZiAoaXNTY3JvbGxSZXN0b3JhdGlvblN1cHBvcnRlZCkge1xuXHRcdFx0XHQvLyBTZXQgaXQgdG8gbWFudWFsXG5cdFx0XHRcdHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgeyBoaXN0b3J5LnNjcm9sbFJlc3RvcmF0aW9uID0gXCJtYW51YWxcIiB9LCA5KVxuXHRcdFx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcInBvcHN0YXRlXCIsIGZ1bmN0aW9uIChldmVudCkge1xuXHRcdFx0XHRcdGlmIChldmVudC5zdGF0ZSAmJiBcInplbnNjcm9sbFlcIiBpbiBldmVudC5zdGF0ZSkge1xuXHRcdFx0XHRcdFx0emVuc2Nyb2xsLnRvWShldmVudC5zdGF0ZS56ZW5zY3JvbGxZKVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSwgZmFsc2UpXG5cdFx0XHR9XG5cblx0XHRcdC8vIEFkZCBlZGdlIG9mZnNldCBvbiBmaXJzdCBsb2FkIGlmIG5lY2Vzc2FyeVxuXHRcdFx0Ly8gVGhpcyBtYXkgbm90IHdvcmsgb24gSUUgKG9yIG9sZGVyIGNvbXB1dGVyPykgYXMgaXQgcmVxdWlyZXMgbW9yZSB0aW1lb3V0LCBhcm91bmQgMTAwIG1zXG5cdFx0XHRpZiAod2luZG93LmxvY2F0aW9uLmhhc2gpIHtcblx0XHRcdFx0c2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdFx0Ly8gQWRqdXN0bWVudCBpcyBvbmx5IG5lZWRlZCBpZiB0aGVyZSBpcyBhbiBlZGdlIG9mZnNldDpcblx0XHRcdFx0XHR2YXIgZWRnZU9mZnNldCA9IHplbnNjcm9sbC5zZXR1cCgpLmVkZ2VPZmZzZXRcblx0XHRcdFx0XHRpZiAoZWRnZU9mZnNldCkge1xuXHRcdFx0XHRcdFx0dmFyIHRhcmdldEVsZW0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh3aW5kb3cubG9jYXRpb24uaHJlZi5zcGxpdChcIiNcIilbMV0pXG5cdFx0XHRcdFx0XHRpZiAodGFyZ2V0RWxlbSkge1xuXHRcdFx0XHRcdFx0XHR2YXIgdGFyZ2V0WSA9IE1hdGgubWF4KDAsIHplbnNjcm9sbC5nZXRUb3BPZih0YXJnZXRFbGVtKSAtIGVkZ2VPZmZzZXQpXG5cdFx0XHRcdFx0XHRcdHZhciBkaWZmID0gemVuc2Nyb2xsLmdldFkoKSAtIHRhcmdldFlcblx0XHRcdFx0XHRcdFx0Ly8gT25seSBkbyB0aGUgYWRqdXN0bWVudCBpZiB0aGUgYnJvd3NlciBpcyB2ZXJ5IGNsb3NlIHRvIHRoZSBlbGVtZW50OlxuXHRcdFx0XHRcdFx0XHRpZiAoMCA8PSBkaWZmICYmIGRpZmYgPCA5ICkge1xuXHRcdFx0XHRcdFx0XHRcdHdpbmRvdy5zY3JvbGxUbygwLCB0YXJnZXRZKVxuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9LCA5KVxuXHRcdFx0fVxuXG5cdFx0fSwgZmFsc2UpXG5cblx0XHQvLyBIYW5kbGluZyBjbGlja3Mgb24gYW5jaG9yc1xuXHRcdHZhciBSRV9ub1plbnNtb290aCA9IG5ldyBSZWdFeHAoXCIoXnxcXFxccylub1plbnNtb290aChcXFxcc3wkKVwiKVxuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwiY2xpY2tcIiwgZnVuY3Rpb24gKGV2ZW50KSB7XG5cdFx0XHR2YXIgYW5jaG9yID0gZXZlbnQudGFyZ2V0XG5cdFx0XHR3aGlsZSAoYW5jaG9yICYmIGFuY2hvci50YWdOYW1lICE9PSBcIkFcIikge1xuXHRcdFx0XHRhbmNob3IgPSBhbmNob3IucGFyZW50Tm9kZVxuXHRcdFx0fVxuXHRcdFx0Ly8gTGV0IHRoZSBicm93c2VyIGhhbmRsZSB0aGUgY2xpY2sgaWYgaXQgd2Fzbid0IHdpdGggdGhlIHByaW1hcnkgYnV0dG9uLCBvciB3aXRoIHNvbWUgbW9kaWZpZXIga2V5czpcblx0XHRcdGlmICghYW5jaG9yIHx8IGV2ZW50LndoaWNoICE9PSAxIHx8IGV2ZW50LnNoaWZ0S2V5IHx8IGV2ZW50Lm1ldGFLZXkgfHwgZXZlbnQuY3RybEtleSB8fCBldmVudC5hbHRLZXkpIHtcblx0XHRcdFx0cmV0dXJuXG5cdFx0XHR9XG5cdFx0XHQvLyBTYXZlIHRoZSBjdXJyZW50IHNjcm9sbGluZyBwb3NpdGlvbiBzbyBpdCBjYW4gYmUgdXNlZCBmb3Igc2Nyb2xsIHJlc3RvcmF0aW9uOlxuXHRcdFx0aWYgKGlzU2Nyb2xsUmVzdG9yYXRpb25TdXBwb3J0ZWQpIHtcblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRoaXN0b3J5LnJlcGxhY2VTdGF0ZSh7IHplbnNjcm9sbFk6IHplbnNjcm9sbC5nZXRZKCkgfSwgXCJcIilcblx0XHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHRcdC8vIEF2b2lkIHRoZSBDaHJvbWUgU2VjdXJpdHkgZXhjZXB0aW9uIG9uIGZpbGUgcHJvdG9jb2wsIGUuZy4sIGZpbGU6Ly9pbmRleC5odG1sXG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdC8vIEZpbmQgdGhlIHJlZmVyZW5jZWQgSUQ6XG5cdFx0XHR2YXIgaHJlZiA9IGFuY2hvci5nZXRBdHRyaWJ1dGUoXCJocmVmXCIpIHx8IFwiXCJcblx0XHRcdGlmIChocmVmLmluZGV4T2YoXCIjXCIpID09PSAwICYmICFSRV9ub1plbnNtb290aC50ZXN0KGFuY2hvci5jbGFzc05hbWUpKSB7XG5cdFx0XHRcdHZhciB0YXJnZXRZID0gMFxuXHRcdFx0XHR2YXIgdGFyZ2V0RWxlbSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGhyZWYuc3Vic3RyaW5nKDEpKVxuXHRcdFx0XHRpZiAoaHJlZiAhPT0gXCIjXCIpIHtcblx0XHRcdFx0XHRpZiAoIXRhcmdldEVsZW0pIHtcblx0XHRcdFx0XHRcdC8vIExldCB0aGUgYnJvd3NlciBoYW5kbGUgdGhlIGNsaWNrIGlmIHRoZSB0YXJnZXQgSUQgaXMgbm90IGZvdW5kLlxuXHRcdFx0XHRcdFx0cmV0dXJuXG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdHRhcmdldFkgPSB6ZW5zY3JvbGwuZ2V0VG9wT2YodGFyZ2V0RWxlbSlcblx0XHRcdFx0fVxuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpXG5cdFx0XHRcdC8vIEJ5IGRlZmF1bHQgdHJpZ2dlciB0aGUgYnJvd3NlcidzIGBoYXNoY2hhbmdlYCBldmVudC4uLlxuXHRcdFx0XHR2YXIgb25Eb25lID0gZnVuY3Rpb24gKCkgeyB3aW5kb3cubG9jYXRpb24gPSBocmVmIH1cblx0XHRcdFx0Ly8gLi4udW5sZXNzIHRoZXJlIGlzIGFuIGVkZ2Ugb2Zmc2V0IHNwZWNpZmllZFxuXHRcdFx0XHR2YXIgZWRnZU9mZnNldCA9IHplbnNjcm9sbC5zZXR1cCgpLmVkZ2VPZmZzZXRcblx0XHRcdFx0aWYgKGVkZ2VPZmZzZXQpIHtcblx0XHRcdFx0XHR0YXJnZXRZID0gTWF0aC5tYXgoMCwgdGFyZ2V0WSAtIGVkZ2VPZmZzZXQpXG5cdFx0XHRcdFx0b25Eb25lID0gZnVuY3Rpb24gKCkgeyBoaXN0b3J5LnB1c2hTdGF0ZShudWxsLCBcIlwiLCBocmVmKSB9XG5cdFx0XHRcdH1cblx0XHRcdFx0emVuc2Nyb2xsLnRvWSh0YXJnZXRZLCBudWxsLCBvbkRvbmUpXG5cdFx0XHR9XG5cdFx0fSwgZmFsc2UpXG5cblx0fVxuXG5cblx0cmV0dXJuIHplbnNjcm9sbFxuXG5cbn0pKTtcblxuXG5cbi8vLy8vLy8vLy8vLy8vLy8vL1xuLy8gV0VCUEFDSyBGT09URVJcbi8vIC4vfi96ZW5zY3JvbGwvemVuc2Nyb2xsLmpzXG4vLyBtb2R1bGUgaWQgPSAxXG4vLyBtb2R1bGUgY2h1bmtzID0gMCJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9"); - -/***/ }), -/* 2 */ -/* unknown exports provided */ -/* all exports used */ -/*!******************************!*\ - !*** ./src/js/build-html.js ***! - \******************************/ -/***/ (function(module, exports) { - -eval("/**\n * This file is responsible for building the DOM and updating DOM state.\n *\n * @author Tim Scanlin\n */\n\nmodule.exports = function (options) {\n var forEach = [].forEach\n var some = [].some\n var body = document.body\n var currentlyHighlighting = true\n var SPACE_CHAR = ' '\n\n /**\n * Create link and list elements.\n * @param {Object} d\n * @param {HTMLElement} container\n * @return {HTMLElement}\n */\n function createEl (d, container) {\n var link = container.appendChild(createLink(d))\n if (d.children.length) {\n var list = createList(d.isCollapsed)\n d.children.forEach(function (child) {\n createEl(child, list)\n })\n link.appendChild(list)\n }\n }\n\n /**\n * Render nested heading array data into a given selector.\n * @param {String} selector\n * @param {Array} data\n * @return {HTMLElement}\n */\n function render (selector, data) {\n var collapsed = false\n var container = createList(collapsed)\n\n data.forEach(function (d) {\n createEl(d, container)\n })\n\n var parent = document.querySelector(selector)\n\n // Return if no parent is found.\n if (parent === null) {\n return\n }\n\n // Remove existing child if it exists.\n if (parent.firstChild) {\n parent.removeChild(parent.firstChild)\n }\n\n // Append the Elements that have been created;\n return parent.appendChild(container)\n }\n\n /**\n * Create link element.\n * @param {Object} data\n * @return {HTMLElement}\n */\n function createLink (data) {\n var item = document.createElement('li')\n var a = document.createElement('a')\n if (options.listItemClass) {\n item.setAttribute('class', options.listItemClass)\n }\n if (options.includeHtml && data.childNodes.length) {\n forEach.call(data.childNodes, function (node) {\n a.appendChild(node.cloneNode(true))\n })\n } else {\n // Default behavior.\n a.textContent = data.textContent\n }\n a.setAttribute('href', '#' + data.id)\n a.setAttribute('class', options.linkClass +\n SPACE_CHAR + 'node-name--' + data.nodeName +\n SPACE_CHAR + options.extraLinkClasses)\n item.appendChild(a)\n return item\n }\n\n /**\n * Create list element.\n * @param {Boolean} isCollapsed\n * @return {HTMLElement}\n */\n function createList (isCollapsed) {\n var list = document.createElement('ul')\n var classes = options.listClass +\n SPACE_CHAR + options.extraListClasses\n if (isCollapsed) {\n classes += SPACE_CHAR + options.collapsibleClass\n classes += SPACE_CHAR + options.isCollapsedClass\n }\n list.setAttribute('class', classes)\n return list\n }\n\n /**\n * Update fixed sidebar class.\n * @return {HTMLElement}\n */\n function updateFixedSidebarClass () {\n var top = document.documentElement.scrollTop || body.scrollTop\n var posFixedEl = document.querySelector(options.positionFixedSelector)\n\n if (options.fixedSidebarOffset === 'auto') {\n options.fixedSidebarOffset = document.querySelector(options.tocSelector).offsetTop\n }\n\n if (top > options.fixedSidebarOffset) {\n if (posFixedEl.className.indexOf(options.positionFixedClass) === -1) {\n posFixedEl.className += SPACE_CHAR + options.positionFixedClass\n }\n } else {\n posFixedEl.className = posFixedEl.className.split(SPACE_CHAR + options.positionFixedClass).join('')\n }\n }\n\n /**\n * Update TOC highlighting and collpased groupings.\n */\n function updateToc (headingsArray) {\n var top = document.documentElement.scrollTop || body.scrollTop\n\n // Add fixed class at offset;\n if (options.positionFixedSelector) {\n updateFixedSidebarClass()\n }\n\n // Get the top most heading currently visible on the page so we know what to highlight.\n var headings = headingsArray\n var topHeader\n // Using some instead of each so that we can escape early.\n if (currentlyHighlighting &&\n document.querySelector(options.tocSelector) !== null &&\n headings.length > 0) {\n some.call(headings, function (heading, i) {\n if (heading.offsetTop > top + options.headingsOffset + 10) {\n // Don't allow negative index value.\n var index = (i === 0) ? i : i - 1\n topHeader = headings[index]\n return true\n } else if (i === headings.length - 1) {\n // This allows scrolling for the last heading on the page.\n topHeader = headings[headings.length - 1]\n return true\n }\n })\n\n // Remove the active class from the other tocLinks.\n var tocLinks = document.querySelector(options.tocSelector)\n .querySelectorAll('.' + options.linkClass)\n forEach.call(tocLinks, function (tocLink) {\n tocLink.className = tocLink.className.split(SPACE_CHAR + options.activeLinkClass).join('')\n })\n\n // Add the active class to the active tocLink.\n var activeTocLink = document.querySelector(options.tocSelector)\n .querySelector('.' + options.linkClass +\n '.node-name--' + topHeader.nodeName +\n '[href=\"#' + topHeader.id + '\"]')\n activeTocLink.className += SPACE_CHAR + options.activeLinkClass\n\n var tocLists = document.querySelector(options.tocSelector)\n .querySelectorAll('.' + options.listClass + '.' + options.collapsibleClass)\n\n // Collapse the other collapsible lists.\n forEach.call(tocLists, function (list) {\n var collapsedClass = SPACE_CHAR + options.isCollapsedClass\n if (list.className.indexOf(collapsedClass) === -1) {\n list.className += SPACE_CHAR + options.isCollapsedClass\n }\n })\n\n // Expand the active link's collapsible list and its sibling if applicable.\n if (activeTocLink.nextSibling) {\n activeTocLink.nextSibling.className = activeTocLink.nextSibling.className.split(SPACE_CHAR + options.isCollapsedClass).join('')\n }\n removeCollapsedFromParents(activeTocLink.parentNode.parentNode)\n }\n }\n\n /**\n * Remove collpased class from parent elements.\n * @param {HTMLElement} element\n * @return {HTMLElement}\n */\n function removeCollapsedFromParents (element) {\n if (element.className.indexOf(options.collapsibleClass) !== -1) {\n element.className = element.className.split(SPACE_CHAR + options.isCollapsedClass).join('')\n return removeCollapsedFromParents(element.parentNode.parentNode)\n }\n return element\n }\n\n /**\n * Disable TOC Animation when a link is clicked.\n * @param {Event} event\n */\n function disableTocAnimation (event) {\n var target = event.target || event.srcElement\n if (typeof target.className !== 'string' || target.className.indexOf(options.linkClass) === -1) {\n return\n }\n // Bind to tocLink clicks to temporarily disable highlighting\n // while smoothScroll is animating.\n currentlyHighlighting = false\n }\n\n /**\n * Enable TOC Animation.\n */\n function enableTocAnimation () {\n currentlyHighlighting = true\n }\n\n return {\n enableTocAnimation: enableTocAnimation,\n disableTocAnimation: disableTocAnimation,\n render: render,\n updateToc: updateToc\n }\n}\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMi5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3NyYy9qcy9idWlsZC1odG1sLmpzPzdkMDEiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIGJ1aWxkaW5nIHRoZSBET00gYW5kIHVwZGF0aW5nIERPTSBzdGF0ZS5cbiAqXG4gKiBAYXV0aG9yIFRpbSBTY2FubGluXG4gKi9cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuICB2YXIgZm9yRWFjaCA9IFtdLmZvckVhY2hcbiAgdmFyIHNvbWUgPSBbXS5zb21lXG4gIHZhciBib2R5ID0gZG9jdW1lbnQuYm9keVxuICB2YXIgY3VycmVudGx5SGlnaGxpZ2h0aW5nID0gdHJ1ZVxuICB2YXIgU1BBQ0VfQ0hBUiA9ICcgJ1xuXG4gIC8qKlxuICAgKiBDcmVhdGUgbGluayBhbmQgbGlzdCBlbGVtZW50cy5cbiAgICogQHBhcmFtIHtPYmplY3R9IGRcbiAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gY29udGFpbmVyXG4gICAqIEByZXR1cm4ge0hUTUxFbGVtZW50fVxuICAgKi9cbiAgZnVuY3Rpb24gY3JlYXRlRWwgKGQsIGNvbnRhaW5lcikge1xuICAgIHZhciBsaW5rID0gY29udGFpbmVyLmFwcGVuZENoaWxkKGNyZWF0ZUxpbmsoZCkpXG4gICAgaWYgKGQuY2hpbGRyZW4ubGVuZ3RoKSB7XG4gICAgICB2YXIgbGlzdCA9IGNyZWF0ZUxpc3QoZC5pc0NvbGxhcHNlZClcbiAgICAgIGQuY2hpbGRyZW4uZm9yRWFjaChmdW5jdGlvbiAoY2hpbGQpIHtcbiAgICAgICAgY3JlYXRlRWwoY2hpbGQsIGxpc3QpXG4gICAgICB9KVxuICAgICAgbGluay5hcHBlbmRDaGlsZChsaXN0KVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZW5kZXIgbmVzdGVkIGhlYWRpbmcgYXJyYXkgZGF0YSBpbnRvIGEgZ2l2ZW4gc2VsZWN0b3IuXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzZWxlY3RvclxuICAgKiBAcGFyYW0ge0FycmF5fSBkYXRhXG4gICAqIEByZXR1cm4ge0hUTUxFbGVtZW50fVxuICAgKi9cbiAgZnVuY3Rpb24gcmVuZGVyIChzZWxlY3RvciwgZGF0YSkge1xuICAgIHZhciBjb2xsYXBzZWQgPSBmYWxzZVxuICAgIHZhciBjb250YWluZXIgPSBjcmVhdGVMaXN0KGNvbGxhcHNlZClcblxuICAgIGRhdGEuZm9yRWFjaChmdW5jdGlvbiAoZCkge1xuICAgICAgY3JlYXRlRWwoZCwgY29udGFpbmVyKVxuICAgIH0pXG5cbiAgICB2YXIgcGFyZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWxlY3RvcilcblxuICAgIC8vIFJldHVybiBpZiBubyBwYXJlbnQgaXMgZm91bmQuXG4gICAgaWYgKHBhcmVudCA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gUmVtb3ZlIGV4aXN0aW5nIGNoaWxkIGlmIGl0IGV4aXN0cy5cbiAgICBpZiAocGFyZW50LmZpcnN0Q2hpbGQpIHtcbiAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChwYXJlbnQuZmlyc3RDaGlsZClcbiAgICB9XG5cbiAgICAvLyBBcHBlbmQgdGhlIEVsZW1lbnRzIHRoYXQgaGF2ZSBiZWVuIGNyZWF0ZWQ7XG4gICAgcmV0dXJuIHBhcmVudC5hcHBlbmRDaGlsZChjb250YWluZXIpXG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGxpbmsgZWxlbWVudC5cbiAgICogQHBhcmFtIHtPYmplY3R9IGRhdGFcbiAgICogQHJldHVybiB7SFRNTEVsZW1lbnR9XG4gICAqL1xuICBmdW5jdGlvbiBjcmVhdGVMaW5rIChkYXRhKSB7XG4gICAgdmFyIGl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpXG4gICAgdmFyIGEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJylcbiAgICBpZiAob3B0aW9ucy5saXN0SXRlbUNsYXNzKSB7XG4gICAgICBpdGVtLnNldEF0dHJpYnV0ZSgnY2xhc3MnLCBvcHRpb25zLmxpc3RJdGVtQ2xhc3MpXG4gICAgfVxuICAgIGlmIChvcHRpb25zLmluY2x1ZGVIdG1sICYmIGRhdGEuY2hpbGROb2Rlcy5sZW5ndGgpIHtcbiAgICAgIGZvckVhY2guY2FsbChkYXRhLmNoaWxkTm9kZXMsIGZ1bmN0aW9uIChub2RlKSB7XG4gICAgICAgIGEuYXBwZW5kQ2hpbGQobm9kZS5jbG9uZU5vZGUodHJ1ZSkpXG4gICAgICB9KVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEZWZhdWx0IGJlaGF2aW9yLlxuICAgICAgYS50ZXh0Q29udGVudCA9IGRhdGEudGV4dENvbnRlbnRcbiAgICB9XG4gICAgYS5zZXRBdHRyaWJ1dGUoJ2hyZWYnLCAnIycgKyBkYXRhLmlkKVxuICAgIGEuc2V0QXR0cmlidXRlKCdjbGFzcycsIG9wdGlvbnMubGlua0NsYXNzICtcbiAgICAgIFNQQUNFX0NIQVIgKyAnbm9kZS1uYW1lLS0nICsgZGF0YS5ub2RlTmFtZSArXG4gICAgICBTUEFDRV9DSEFSICsgb3B0aW9ucy5leHRyYUxpbmtDbGFzc2VzKVxuICAgIGl0ZW0uYXBwZW5kQ2hpbGQoYSlcbiAgICByZXR1cm4gaXRlbVxuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBsaXN0IGVsZW1lbnQuXG4gICAqIEBwYXJhbSB7Qm9vbGVhbn0gaXNDb2xsYXBzZWRcbiAgICogQHJldHVybiB7SFRNTEVsZW1lbnR9XG4gICAqL1xuICBmdW5jdGlvbiBjcmVhdGVMaXN0IChpc0NvbGxhcHNlZCkge1xuICAgIHZhciBsaXN0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndWwnKVxuICAgIHZhciBjbGFzc2VzID0gb3B0aW9ucy5saXN0Q2xhc3MgK1xuICAgICAgU1BBQ0VfQ0hBUiArIG9wdGlvbnMuZXh0cmFMaXN0Q2xhc3Nlc1xuICAgIGlmIChpc0NvbGxhcHNlZCkge1xuICAgICAgY2xhc3NlcyArPSBTUEFDRV9DSEFSICsgb3B0aW9ucy5jb2xsYXBzaWJsZUNsYXNzXG4gICAgICBjbGFzc2VzICs9IFNQQUNFX0NIQVIgKyBvcHRpb25zLmlzQ29sbGFwc2VkQ2xhc3NcbiAgICB9XG4gICAgbGlzdC5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgY2xhc3NlcylcbiAgICByZXR1cm4gbGlzdFxuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSBmaXhlZCBzaWRlYmFyIGNsYXNzLlxuICAgKiBAcmV0dXJuIHtIVE1MRWxlbWVudH1cbiAgICovXG4gIGZ1bmN0aW9uIHVwZGF0ZUZpeGVkU2lkZWJhckNsYXNzICgpIHtcbiAgICB2YXIgdG9wID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNjcm9sbFRvcCB8fCBib2R5LnNjcm9sbFRvcFxuICAgIHZhciBwb3NGaXhlZEVsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihvcHRpb25zLnBvc2l0aW9uRml4ZWRTZWxlY3RvcilcblxuICAgIGlmIChvcHRpb25zLmZpeGVkU2lkZWJhck9mZnNldCA9PT0gJ2F1dG8nKSB7XG4gICAgICBvcHRpb25zLmZpeGVkU2lkZWJhck9mZnNldCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Iob3B0aW9ucy50b2NTZWxlY3Rvcikub2Zmc2V0VG9wXG4gICAgfVxuXG4gICAgaWYgKHRvcCA+IG9wdGlvbnMuZml4ZWRTaWRlYmFyT2Zmc2V0KSB7XG4gICAgICBpZiAocG9zRml4ZWRFbC5jbGFzc05hbWUuaW5kZXhPZihvcHRpb25zLnBvc2l0aW9uRml4ZWRDbGFzcykgPT09IC0xKSB7XG4gICAgICAgIHBvc0ZpeGVkRWwuY2xhc3NOYW1lICs9IFNQQUNFX0NIQVIgKyBvcHRpb25zLnBvc2l0aW9uRml4ZWRDbGFzc1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBwb3NGaXhlZEVsLmNsYXNzTmFtZSA9IHBvc0ZpeGVkRWwuY2xhc3NOYW1lLnNwbGl0KFNQQUNFX0NIQVIgKyBvcHRpb25zLnBvc2l0aW9uRml4ZWRDbGFzcykuam9pbignJylcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIFRPQyBoaWdobGlnaHRpbmcgYW5kIGNvbGxwYXNlZCBncm91cGluZ3MuXG4gICAqL1xuICBmdW5jdGlvbiB1cGRhdGVUb2MgKGhlYWRpbmdzQXJyYXkpIHtcbiAgICB2YXIgdG9wID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNjcm9sbFRvcCB8fCBib2R5LnNjcm9sbFRvcFxuXG4gICAgLy8gQWRkIGZpeGVkIGNsYXNzIGF0IG9mZnNldDtcbiAgICBpZiAob3B0aW9ucy5wb3NpdGlvbkZpeGVkU2VsZWN0b3IpIHtcbiAgICAgIHVwZGF0ZUZpeGVkU2lkZWJhckNsYXNzKClcbiAgICB9XG5cbiAgICAvLyBHZXQgdGhlIHRvcCBtb3N0IGhlYWRpbmcgY3VycmVudGx5IHZpc2libGUgb24gdGhlIHBhZ2Ugc28gd2Uga25vdyB3aGF0IHRvIGhpZ2hsaWdodC5cbiAgICB2YXIgaGVhZGluZ3MgPSBoZWFkaW5nc0FycmF5XG4gICAgdmFyIHRvcEhlYWRlclxuICAgIC8vIFVzaW5nIHNvbWUgaW5zdGVhZCBvZiBlYWNoIHNvIHRoYXQgd2UgY2FuIGVzY2FwZSBlYXJseS5cbiAgICBpZiAoY3VycmVudGx5SGlnaGxpZ2h0aW5nICYmXG4gICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKG9wdGlvbnMudG9jU2VsZWN0b3IpICE9PSBudWxsICYmXG4gICAgICBoZWFkaW5ncy5sZW5ndGggPiAwKSB7XG4gICAgICBzb21lLmNhbGwoaGVhZGluZ3MsIGZ1bmN0aW9uIChoZWFkaW5nLCBpKSB7XG4gICAgICAgIGlmIChoZWFkaW5nLm9mZnNldFRvcCA+IHRvcCArIG9wdGlvbnMuaGVhZGluZ3NPZmZzZXQgKyAxMCkge1xuICAgICAgICAgIC8vIERvbid0IGFsbG93IG5lZ2F0aXZlIGluZGV4IHZhbHVlLlxuICAgICAgICAgIHZhciBpbmRleCA9IChpID09PSAwKSA/IGkgOiBpIC0gMVxuICAgICAgICAgIHRvcEhlYWRlciA9IGhlYWRpbmdzW2luZGV4XVxuICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH0gZWxzZSBpZiAoaSA9PT0gaGVhZGluZ3MubGVuZ3RoIC0gMSkge1xuICAgICAgICAgIC8vIFRoaXMgYWxsb3dzIHNjcm9sbGluZyBmb3IgdGhlIGxhc3QgaGVhZGluZyBvbiB0aGUgcGFnZS5cbiAgICAgICAgICB0b3BIZWFkZXIgPSBoZWFkaW5nc1toZWFkaW5ncy5sZW5ndGggLSAxXVxuICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH1cbiAgICAgIH0pXG5cbiAgICAgIC8vIFJlbW92ZSB0aGUgYWN0aXZlIGNsYXNzIGZyb20gdGhlIG90aGVyIHRvY0xpbmtzLlxuICAgICAgdmFyIHRvY0xpbmtzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihvcHRpb25zLnRvY1NlbGVjdG9yKVxuICAgICAgICAucXVlcnlTZWxlY3RvckFsbCgnLicgKyBvcHRpb25zLmxpbmtDbGFzcylcbiAgICAgIGZvckVhY2guY2FsbCh0b2NMaW5rcywgZnVuY3Rpb24gKHRvY0xpbmspIHtcbiAgICAgICAgdG9jTGluay5jbGFzc05hbWUgPSB0b2NMaW5rLmNsYXNzTmFtZS5zcGxpdChTUEFDRV9DSEFSICsgb3B0aW9ucy5hY3RpdmVMaW5rQ2xhc3MpLmpvaW4oJycpXG4gICAgICB9KVxuXG4gICAgICAvLyBBZGQgdGhlIGFjdGl2ZSBjbGFzcyB0byB0aGUgYWN0aXZlIHRvY0xpbmsuXG4gICAgICB2YXIgYWN0aXZlVG9jTGluayA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Iob3B0aW9ucy50b2NTZWxlY3RvcilcbiAgICAgICAgLnF1ZXJ5U2VsZWN0b3IoJy4nICsgb3B0aW9ucy5saW5rQ2xhc3MgK1xuICAgICAgICAgICcubm9kZS1uYW1lLS0nICsgdG9wSGVhZGVyLm5vZGVOYW1lICtcbiAgICAgICAgICAnW2hyZWY9XCIjJyArIHRvcEhlYWRlci5pZCArICdcIl0nKVxuICAgICAgYWN0aXZlVG9jTGluay5jbGFzc05hbWUgKz0gU1BBQ0VfQ0hBUiArIG9wdGlvbnMuYWN0aXZlTGlua0NsYXNzXG5cbiAgICAgIHZhciB0b2NMaXN0cyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3Iob3B0aW9ucy50b2NTZWxlY3RvcilcbiAgICAgICAgLnF1ZXJ5U2VsZWN0b3JBbGwoJy4nICsgb3B0aW9ucy5saXN0Q2xhc3MgKyAnLicgKyBvcHRpb25zLmNvbGxhcHNpYmxlQ2xhc3MpXG5cbiAgICAgIC8vIENvbGxhcHNlIHRoZSBvdGhlciBjb2xsYXBzaWJsZSBsaXN0cy5cbiAgICAgIGZvckVhY2guY2FsbCh0b2NMaXN0cywgZnVuY3Rpb24gKGxpc3QpIHtcbiAgICAgICAgdmFyIGNvbGxhcHNlZENsYXNzID0gU1BBQ0VfQ0hBUiArIG9wdGlvbnMuaXNDb2xsYXBzZWRDbGFzc1xuICAgICAgICBpZiAobGlzdC5jbGFzc05hbWUuaW5kZXhPZihjb2xsYXBzZWRDbGFzcykgPT09IC0xKSB7XG4gICAgICAgICAgbGlzdC5jbGFzc05hbWUgKz0gU1BBQ0VfQ0hBUiArIG9wdGlvbnMuaXNDb2xsYXBzZWRDbGFzc1xuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICAvLyBFeHBhbmQgdGhlIGFjdGl2ZSBsaW5rJ3MgY29sbGFwc2libGUgbGlzdCBhbmQgaXRzIHNpYmxpbmcgaWYgYXBwbGljYWJsZS5cbiAgICAgIGlmIChhY3RpdmVUb2NMaW5rLm5leHRTaWJsaW5nKSB7XG4gICAgICAgIGFjdGl2ZVRvY0xpbmsubmV4dFNpYmxpbmcuY2xhc3NOYW1lID0gYWN0aXZlVG9jTGluay5uZXh0U2libGluZy5jbGFzc05hbWUuc3BsaXQoU1BBQ0VfQ0hBUiArIG9wdGlvbnMuaXNDb2xsYXBzZWRDbGFzcykuam9pbignJylcbiAgICAgIH1cbiAgICAgIHJlbW92ZUNvbGxhcHNlZEZyb21QYXJlbnRzKGFjdGl2ZVRvY0xpbmsucGFyZW50Tm9kZS5wYXJlbnROb2RlKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmUgY29sbHBhc2VkIGNsYXNzIGZyb20gcGFyZW50IGVsZW1lbnRzLlxuICAgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbGVtZW50XG4gICAqIEByZXR1cm4ge0hUTUxFbGVtZW50fVxuICAgKi9cbiAgZnVuY3Rpb24gcmVtb3ZlQ29sbGFwc2VkRnJvbVBhcmVudHMgKGVsZW1lbnQpIHtcbiAgICBpZiAoZWxlbWVudC5jbGFzc05hbWUuaW5kZXhPZihvcHRpb25zLmNvbGxhcHNpYmxlQ2xhc3MpICE9PSAtMSkge1xuICAgICAgZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5zcGxpdChTUEFDRV9DSEFSICsgb3B0aW9ucy5pc0NvbGxhcHNlZENsYXNzKS5qb2luKCcnKVxuICAgICAgcmV0dXJuIHJlbW92ZUNvbGxhcHNlZEZyb21QYXJlbnRzKGVsZW1lbnQucGFyZW50Tm9kZS5wYXJlbnROb2RlKVxuICAgIH1cbiAgICByZXR1cm4gZWxlbWVudFxuICB9XG5cbiAgLyoqXG4gICAqIERpc2FibGUgVE9DIEFuaW1hdGlvbiB3aGVuIGEgbGluayBpcyBjbGlja2VkLlxuICAgKiBAcGFyYW0ge0V2ZW50fSBldmVudFxuICAgKi9cbiAgZnVuY3Rpb24gZGlzYWJsZVRvY0FuaW1hdGlvbiAoZXZlbnQpIHtcbiAgICB2YXIgdGFyZ2V0ID0gZXZlbnQudGFyZ2V0IHx8IGV2ZW50LnNyY0VsZW1lbnRcbiAgICBpZiAodHlwZW9mIHRhcmdldC5jbGFzc05hbWUgIT09ICdzdHJpbmcnIHx8IHRhcmdldC5jbGFzc05hbWUuaW5kZXhPZihvcHRpb25zLmxpbmtDbGFzcykgPT09IC0xKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgLy8gQmluZCB0byB0b2NMaW5rIGNsaWNrcyB0byB0ZW1wb3JhcmlseSBkaXNhYmxlIGhpZ2hsaWdodGluZ1xuICAgIC8vIHdoaWxlIHNtb290aFNjcm9sbCBpcyBhbmltYXRpbmcuXG4gICAgY3VycmVudGx5SGlnaGxpZ2h0aW5nID0gZmFsc2VcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmFibGUgVE9DIEFuaW1hdGlvbi5cbiAgICovXG4gIGZ1bmN0aW9uIGVuYWJsZVRvY0FuaW1hdGlvbiAoKSB7XG4gICAgY3VycmVudGx5SGlnaGxpZ2h0aW5nID0gdHJ1ZVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBlbmFibGVUb2NBbmltYXRpb246IGVuYWJsZVRvY0FuaW1hdGlvbixcbiAgICBkaXNhYmxlVG9jQW5pbWF0aW9uOiBkaXNhYmxlVG9jQW5pbWF0aW9uLFxuICAgIHJlbmRlcjogcmVuZGVyLFxuICAgIHVwZGF0ZVRvYzogdXBkYXRlVG9jXG4gIH1cbn1cblxuXG5cbi8vLy8vLy8vLy8vLy8vLy8vL1xuLy8gV0VCUEFDSyBGT09URVJcbi8vIC4vc3JjL2pzL2J1aWxkLWh0bWwuanNcbi8vIG1vZHVsZSBpZCA9IDJcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ=="); - -/***/ }), -/* 3 */ -/* unknown exports provided */ -/* all exports used */ -/*!***********************************!*\ - !*** ./src/js/default-options.js ***! - \***********************************/ -/***/ (function(module, exports) { - -eval("module.exports = {\n // Where to render the table of contents.\n tocSelector: '.js-toc',\n // Where to grab the headings to build the table of contents.\n contentSelector: '.js-toc-content',\n // Which headings to grab inside of the contentSelector element.\n headingSelector: 'h1, h2, h3',\n // Headings that match the ignoreSelector will be skipped.\n ignoreSelector: '.js-toc-ignore',\n // Main class to add to links.\n linkClass: 'toc-link',\n // Extra classes to add to links.\n extraLinkClasses: '',\n // Class to add to active links,\n // the link corresponding to the top most heading on the page.\n activeLinkClass: 'is-active-link',\n // Main class to add to lists.\n listClass: 'toc-list',\n // Extra classes to add to lists.\n extraListClasses: '',\n // Class that gets added when a list should be collapsed.\n isCollapsedClass: 'is-collapsed',\n // Class that gets added when a list should be able\n // to be collapsed but isn't necessarily collpased.\n collapsibleClass: 'is-collapsible',\n // Class to add to list items.\n listItemClass: 'toc-list-item',\n // How many heading levels should not be collpased.\n // For example, number 6 will show everything since\n // there are only 6 heading levels and number 0 will collpase them all.\n // The sections that are hidden will open\n // and close as you scroll to headings within them.\n collapseDepth: 0,\n // Smooth scrolling enabled.\n smoothScroll: true,\n // Smooth scroll duration.\n smoothScrollDuration: 420,\n // Callback for scroll end (requires: smoothScroll).\n scrollEndCallback: function (e) {},\n // Headings offset between the headings and the top of the document.\n headingsOffset: 0,\n // Timeout between events firing to make sure it's\n // not too rapid (for performance reasons).\n throttleTimeout: 50,\n // Element to add the positionFixedClass to.\n positionFixedSelector: null,\n // Fixed position class to add to make sidebar fixed after scrolling\n // down past the fixedSidebarOffset.\n positionFixedClass: 'is-position-fixed',\n // fixedSidebarOffset can be any number but by default is set\n // to auto which sets the fixedSidebarOffset to the sidebar\n // element's offsetTop from the top of the document on init.\n fixedSidebarOffset: 'auto',\n // includeHtml can be set to true to include the HTML markup from the\n // heading node instead of just including the textContent.\n includeHtml: false\n}\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3NyYy9qcy9kZWZhdWx0LW9wdGlvbnMuanM/MTg1MSJdLCJzb3VyY2VzQ29udGVudCI6WyJtb2R1bGUuZXhwb3J0cyA9IHtcbiAgLy8gV2hlcmUgdG8gcmVuZGVyIHRoZSB0YWJsZSBvZiBjb250ZW50cy5cbiAgdG9jU2VsZWN0b3I6ICcuanMtdG9jJyxcbiAgLy8gV2hlcmUgdG8gZ3JhYiB0aGUgaGVhZGluZ3MgdG8gYnVpbGQgdGhlIHRhYmxlIG9mIGNvbnRlbnRzLlxuICBjb250ZW50U2VsZWN0b3I6ICcuanMtdG9jLWNvbnRlbnQnLFxuICAvLyBXaGljaCBoZWFkaW5ncyB0byBncmFiIGluc2lkZSBvZiB0aGUgY29udGVudFNlbGVjdG9yIGVsZW1lbnQuXG4gIGhlYWRpbmdTZWxlY3RvcjogJ2gxLCBoMiwgaDMnLFxuICAvLyBIZWFkaW5ncyB0aGF0IG1hdGNoIHRoZSBpZ25vcmVTZWxlY3RvciB3aWxsIGJlIHNraXBwZWQuXG4gIGlnbm9yZVNlbGVjdG9yOiAnLmpzLXRvYy1pZ25vcmUnLFxuICAvLyBNYWluIGNsYXNzIHRvIGFkZCB0byBsaW5rcy5cbiAgbGlua0NsYXNzOiAndG9jLWxpbmsnLFxuICAvLyBFeHRyYSBjbGFzc2VzIHRvIGFkZCB0byBsaW5rcy5cbiAgZXh0cmFMaW5rQ2xhc3NlczogJycsXG4gIC8vIENsYXNzIHRvIGFkZCB0byBhY3RpdmUgbGlua3MsXG4gIC8vIHRoZSBsaW5rIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHRvcCBtb3N0IGhlYWRpbmcgb24gdGhlIHBhZ2UuXG4gIGFjdGl2ZUxpbmtDbGFzczogJ2lzLWFjdGl2ZS1saW5rJyxcbiAgLy8gTWFpbiBjbGFzcyB0byBhZGQgdG8gbGlzdHMuXG4gIGxpc3RDbGFzczogJ3RvYy1saXN0JyxcbiAgLy8gRXh0cmEgY2xhc3NlcyB0byBhZGQgdG8gbGlzdHMuXG4gIGV4dHJhTGlzdENsYXNzZXM6ICcnLFxuICAvLyBDbGFzcyB0aGF0IGdldHMgYWRkZWQgd2hlbiBhIGxpc3Qgc2hvdWxkIGJlIGNvbGxhcHNlZC5cbiAgaXNDb2xsYXBzZWRDbGFzczogJ2lzLWNvbGxhcHNlZCcsXG4gIC8vIENsYXNzIHRoYXQgZ2V0cyBhZGRlZCB3aGVuIGEgbGlzdCBzaG91bGQgYmUgYWJsZVxuICAvLyB0byBiZSBjb2xsYXBzZWQgYnV0IGlzbid0IG5lY2Vzc2FyaWx5IGNvbGxwYXNlZC5cbiAgY29sbGFwc2libGVDbGFzczogJ2lzLWNvbGxhcHNpYmxlJyxcbiAgLy8gQ2xhc3MgdG8gYWRkIHRvIGxpc3QgaXRlbXMuXG4gIGxpc3RJdGVtQ2xhc3M6ICd0b2MtbGlzdC1pdGVtJyxcbiAgLy8gSG93IG1hbnkgaGVhZGluZyBsZXZlbHMgc2hvdWxkIG5vdCBiZSBjb2xscGFzZWQuXG4gIC8vIEZvciBleGFtcGxlLCBudW1iZXIgNiB3aWxsIHNob3cgZXZlcnl0aGluZyBzaW5jZVxuICAvLyB0aGVyZSBhcmUgb25seSA2IGhlYWRpbmcgbGV2ZWxzIGFuZCBudW1iZXIgMCB3aWxsIGNvbGxwYXNlIHRoZW0gYWxsLlxuICAvLyBUaGUgc2VjdGlvbnMgdGhhdCBhcmUgaGlkZGVuIHdpbGwgb3BlblxuICAvLyBhbmQgY2xvc2UgYXMgeW91IHNjcm9sbCB0byBoZWFkaW5ncyB3aXRoaW4gdGhlbS5cbiAgY29sbGFwc2VEZXB0aDogMCxcbiAgLy8gU21vb3RoIHNjcm9sbGluZyBlbmFibGVkLlxuICBzbW9vdGhTY3JvbGw6IHRydWUsXG4gIC8vIFNtb290aCBzY3JvbGwgZHVyYXRpb24uXG4gIHNtb290aFNjcm9sbER1cmF0aW9uOiA0MjAsXG4gIC8vIENhbGxiYWNrIGZvciBzY3JvbGwgZW5kIChyZXF1aXJlczogc21vb3RoU2Nyb2xsKS5cbiAgc2Nyb2xsRW5kQ2FsbGJhY2s6IGZ1bmN0aW9uIChlKSB7fSxcbiAgLy8gSGVhZGluZ3Mgb2Zmc2V0IGJldHdlZW4gdGhlIGhlYWRpbmdzIGFuZCB0aGUgdG9wIG9mIHRoZSBkb2N1bWVudC5cbiAgaGVhZGluZ3NPZmZzZXQ6IDAsXG4gIC8vIFRpbWVvdXQgYmV0d2VlbiBldmVudHMgZmlyaW5nIHRvIG1ha2Ugc3VyZSBpdCdzXG4gIC8vIG5vdCB0b28gcmFwaWQgKGZvciBwZXJmb3JtYW5jZSByZWFzb25zKS5cbiAgdGhyb3R0bGVUaW1lb3V0OiA1MCxcbiAgLy8gRWxlbWVudCB0byBhZGQgdGhlIHBvc2l0aW9uRml4ZWRDbGFzcyB0by5cbiAgcG9zaXRpb25GaXhlZFNlbGVjdG9yOiBudWxsLFxuICAvLyBGaXhlZCBwb3NpdGlvbiBjbGFzcyB0byBhZGQgdG8gbWFrZSBzaWRlYmFyIGZpeGVkIGFmdGVyIHNjcm9sbGluZ1xuICAvLyBkb3duIHBhc3QgdGhlIGZpeGVkU2lkZWJhck9mZnNldC5cbiAgcG9zaXRpb25GaXhlZENsYXNzOiAnaXMtcG9zaXRpb24tZml4ZWQnLFxuICAvLyBmaXhlZFNpZGViYXJPZmZzZXQgY2FuIGJlIGFueSBudW1iZXIgYnV0IGJ5IGRlZmF1bHQgaXMgc2V0XG4gIC8vIHRvIGF1dG8gd2hpY2ggc2V0cyB0aGUgZml4ZWRTaWRlYmFyT2Zmc2V0IHRvIHRoZSBzaWRlYmFyXG4gIC8vIGVsZW1lbnQncyBvZmZzZXRUb3AgZnJvbSB0aGUgdG9wIG9mIHRoZSBkb2N1bWVudCBvbiBpbml0LlxuICBmaXhlZFNpZGViYXJPZmZzZXQ6ICdhdXRvJyxcbiAgLy8gaW5jbHVkZUh0bWwgY2FuIGJlIHNldCB0byB0cnVlIHRvIGluY2x1ZGUgdGhlIEhUTUwgbWFya3VwIGZyb20gdGhlXG4gIC8vIGhlYWRpbmcgbm9kZSBpbnN0ZWFkIG9mIGp1c3QgaW5jbHVkaW5nIHRoZSB0ZXh0Q29udGVudC5cbiAgaW5jbHVkZUh0bWw6IGZhbHNlXG59XG5cblxuXG4vLy8vLy8vLy8vLy8vLy8vLy9cbi8vIFdFQlBBQ0sgRk9PVEVSXG4vLyAuL3NyYy9qcy9kZWZhdWx0LW9wdGlvbnMuanNcbi8vIG1vZHVsZSBpZCA9IDNcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9"); - -/***/ }), -/* 4 */ -/* unknown exports provided */ -/* all exports used */ -/*!*********************************!*\ - !*** ./src/js/parse-content.js ***! - \*********************************/ -/***/ (function(module, exports) { - -eval("/**\n * This file is responsible for parsing the content from the DOM and making\n * sure data is nested properly.\n *\n * @author Tim Scanlin\n */\n\nmodule.exports = function parseContent (options) {\n var reduce = [].reduce\n\n /**\n * Get the last item in an array and return a reference to it.\n * @param {Array} array\n * @return {Object}\n */\n function getLastItem (array) {\n return array[array.length - 1]\n }\n\n /**\n * Get heading level for a heading dom node.\n * @param {HTMLElement} heading\n * @return {Number}\n */\n function getHeadingLevel (heading) {\n return +heading.nodeName.split('H').join('')\n }\n\n /**\n * Get important properties from a heading element and store in a plain object.\n * @param {HTMLElement} heading\n * @return {Object}\n */\n function getHeadingObject (heading) {\n var obj = {\n id: heading.id,\n children: [],\n nodeName: heading.nodeName,\n headingLevel: getHeadingLevel(heading),\n textContent: heading.textContent.trim()\n }\n\n if (options.includeHtml) {\n obj.childNodes = heading.childNodes\n }\n\n return obj\n }\n\n /**\n * Add a node to the nested array.\n * @param {Object} node\n * @param {Array} nest\n * @return {Array}\n */\n function addNode (node, nest) {\n var obj = getHeadingObject(node)\n var level = getHeadingLevel(node)\n var array = nest\n var lastItem = getLastItem(array)\n var lastItemLevel = lastItem\n ? lastItem.headingLevel\n : 0\n var counter = level - lastItemLevel\n\n while (counter > 0) {\n lastItem = getLastItem(array)\n if (lastItem && lastItem.children !== undefined) {\n array = lastItem.children\n }\n counter--\n }\n\n if (level >= options.collapseDepth) {\n obj.isCollapsed = true\n }\n\n array.push(obj)\n return array\n }\n\n /**\n * Select headings in content area, exclude any selector in options.ignoreSelector\n * @param {String} contentSelector\n * @param {Array} headingSelector\n * @return {Array}\n */\n function selectHeadings (contentSelector, headingSelector) {\n var selectors = headingSelector\n if (options.ignoreSelector) {\n selectors = headingSelector.split(',')\n .map(function mapSelectors (selector) {\n return selector.trim() + ':not(' + options.ignoreSelector + ')'\n })\n }\n try {\n return document.querySelector(contentSelector)\n .querySelectorAll(selectors)\n } catch (e) {\n console.warn('Element not found: ' + contentSelector); // eslint-disable-line\n return null\n }\n }\n\n /**\n * Nest headings array into nested arrays with 'children' property.\n * @param {Array} headingsArray\n * @return {Object}\n */\n function nestHeadingsArray (headingsArray) {\n return reduce.call(headingsArray, function reducer (prev, curr) {\n var currentHeading = getHeadingObject(curr)\n\n addNode(currentHeading, prev.nest)\n return prev\n }, {\n nest: []\n })\n }\n\n return {\n nestHeadingsArray: nestHeadingsArray,\n selectHeadings: selectHeadings\n }\n}\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3NyYy9qcy9wYXJzZS1jb250ZW50LmpzPzg3ZjAiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGNvbnRlbnQgZnJvbSB0aGUgRE9NIGFuZCBtYWtpbmdcbiAqIHN1cmUgZGF0YSBpcyBuZXN0ZWQgcHJvcGVybHkuXG4gKlxuICogQGF1dGhvciBUaW0gU2NhbmxpblxuICovXG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gcGFyc2VDb250ZW50IChvcHRpb25zKSB7XG4gIHZhciByZWR1Y2UgPSBbXS5yZWR1Y2VcblxuICAvKipcbiAgICogR2V0IHRoZSBsYXN0IGl0ZW0gaW4gYW4gYXJyYXkgYW5kIHJldHVybiBhIHJlZmVyZW5jZSB0byBpdC5cbiAgICogQHBhcmFtIHtBcnJheX0gYXJyYXlcbiAgICogQHJldHVybiB7T2JqZWN0fVxuICAgKi9cbiAgZnVuY3Rpb24gZ2V0TGFzdEl0ZW0gKGFycmF5KSB7XG4gICAgcmV0dXJuIGFycmF5W2FycmF5Lmxlbmd0aCAtIDFdXG4gIH1cblxuICAvKipcbiAgICogR2V0IGhlYWRpbmcgbGV2ZWwgZm9yIGEgaGVhZGluZyBkb20gbm9kZS5cbiAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gaGVhZGluZ1xuICAgKiBAcmV0dXJuIHtOdW1iZXJ9XG4gICAqL1xuICBmdW5jdGlvbiBnZXRIZWFkaW5nTGV2ZWwgKGhlYWRpbmcpIHtcbiAgICByZXR1cm4gK2hlYWRpbmcubm9kZU5hbWUuc3BsaXQoJ0gnKS5qb2luKCcnKVxuICB9XG5cbiAgLyoqXG4gICAqIEdldCBpbXBvcnRhbnQgcHJvcGVydGllcyBmcm9tIGEgaGVhZGluZyBlbGVtZW50IGFuZCBzdG9yZSBpbiBhIHBsYWluIG9iamVjdC5cbiAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gaGVhZGluZ1xuICAgKiBAcmV0dXJuIHtPYmplY3R9XG4gICAqL1xuICBmdW5jdGlvbiBnZXRIZWFkaW5nT2JqZWN0IChoZWFkaW5nKSB7XG4gICAgdmFyIG9iaiA9IHtcbiAgICAgIGlkOiBoZWFkaW5nLmlkLFxuICAgICAgY2hpbGRyZW46IFtdLFxuICAgICAgbm9kZU5hbWU6IGhlYWRpbmcubm9kZU5hbWUsXG4gICAgICBoZWFkaW5nTGV2ZWw6IGdldEhlYWRpbmdMZXZlbChoZWFkaW5nKSxcbiAgICAgIHRleHRDb250ZW50OiBoZWFkaW5nLnRleHRDb250ZW50LnRyaW0oKVxuICAgIH1cblxuICAgIGlmIChvcHRpb25zLmluY2x1ZGVIdG1sKSB7XG4gICAgICBvYmouY2hpbGROb2RlcyA9IGhlYWRpbmcuY2hpbGROb2Rlc1xuICAgIH1cblxuICAgIHJldHVybiBvYmpcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgYSBub2RlIHRvIHRoZSBuZXN0ZWQgYXJyYXkuXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBub2RlXG4gICAqIEBwYXJhbSB7QXJyYXl9IG5lc3RcbiAgICogQHJldHVybiB7QXJyYXl9XG4gICAqL1xuICBmdW5jdGlvbiBhZGROb2RlIChub2RlLCBuZXN0KSB7XG4gICAgdmFyIG9iaiA9IGdldEhlYWRpbmdPYmplY3Qobm9kZSlcbiAgICB2YXIgbGV2ZWwgPSBnZXRIZWFkaW5nTGV2ZWwobm9kZSlcbiAgICB2YXIgYXJyYXkgPSBuZXN0XG4gICAgdmFyIGxhc3RJdGVtID0gZ2V0TGFzdEl0ZW0oYXJyYXkpXG4gICAgdmFyIGxhc3RJdGVtTGV2ZWwgPSBsYXN0SXRlbVxuICAgICAgPyBsYXN0SXRlbS5oZWFkaW5nTGV2ZWxcbiAgICAgIDogMFxuICAgIHZhciBjb3VudGVyID0gbGV2ZWwgLSBsYXN0SXRlbUxldmVsXG5cbiAgICB3aGlsZSAoY291bnRlciA+IDApIHtcbiAgICAgIGxhc3RJdGVtID0gZ2V0TGFzdEl0ZW0oYXJyYXkpXG4gICAgICBpZiAobGFzdEl0ZW0gJiYgbGFzdEl0ZW0uY2hpbGRyZW4gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBhcnJheSA9IGxhc3RJdGVtLmNoaWxkcmVuXG4gICAgICB9XG4gICAgICBjb3VudGVyLS1cbiAgICB9XG5cbiAgICBpZiAobGV2ZWwgPj0gb3B0aW9ucy5jb2xsYXBzZURlcHRoKSB7XG4gICAgICBvYmouaXNDb2xsYXBzZWQgPSB0cnVlXG4gICAgfVxuXG4gICAgYXJyYXkucHVzaChvYmopXG4gICAgcmV0dXJuIGFycmF5XG4gIH1cblxuICAvKipcbiAgICogU2VsZWN0IGhlYWRpbmdzIGluIGNvbnRlbnQgYXJlYSwgZXhjbHVkZSBhbnkgc2VsZWN0b3IgaW4gb3B0aW9ucy5pZ25vcmVTZWxlY3RvclxuICAgKiBAcGFyYW0ge1N0cmluZ30gY29udGVudFNlbGVjdG9yXG4gICAqIEBwYXJhbSB7QXJyYXl9IGhlYWRpbmdTZWxlY3RvclxuICAgKiBAcmV0dXJuIHtBcnJheX1cbiAgICovXG4gIGZ1bmN0aW9uIHNlbGVjdEhlYWRpbmdzIChjb250ZW50U2VsZWN0b3IsIGhlYWRpbmdTZWxlY3Rvcikge1xuICAgIHZhciBzZWxlY3RvcnMgPSBoZWFkaW5nU2VsZWN0b3JcbiAgICBpZiAob3B0aW9ucy5pZ25vcmVTZWxlY3Rvcikge1xuICAgICAgc2VsZWN0b3JzID0gaGVhZGluZ1NlbGVjdG9yLnNwbGl0KCcsJylcbiAgICAgICAgLm1hcChmdW5jdGlvbiBtYXBTZWxlY3RvcnMgKHNlbGVjdG9yKSB7XG4gICAgICAgICAgcmV0dXJuIHNlbGVjdG9yLnRyaW0oKSArICc6bm90KCcgKyBvcHRpb25zLmlnbm9yZVNlbGVjdG9yICsgJyknXG4gICAgICAgIH0pXG4gICAgfVxuICAgIHRyeSB7XG4gICAgICByZXR1cm4gZG9jdW1lbnQucXVlcnlTZWxlY3Rvcihjb250ZW50U2VsZWN0b3IpXG4gICAgICAgIC5xdWVyeVNlbGVjdG9yQWxsKHNlbGVjdG9ycylcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ0VsZW1lbnQgbm90IGZvdW5kOiAnICsgY29udGVudFNlbGVjdG9yKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZVxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTmVzdCBoZWFkaW5ncyBhcnJheSBpbnRvIG5lc3RlZCBhcnJheXMgd2l0aCAnY2hpbGRyZW4nIHByb3BlcnR5LlxuICAgKiBAcGFyYW0ge0FycmF5fSBoZWFkaW5nc0FycmF5XG4gICAqIEByZXR1cm4ge09iamVjdH1cbiAgICovXG4gIGZ1bmN0aW9uIG5lc3RIZWFkaW5nc0FycmF5IChoZWFkaW5nc0FycmF5KSB7XG4gICAgcmV0dXJuIHJlZHVjZS5jYWxsKGhlYWRpbmdzQXJyYXksIGZ1bmN0aW9uIHJlZHVjZXIgKHByZXYsIGN1cnIpIHtcbiAgICAgIHZhciBjdXJyZW50SGVhZGluZyA9IGdldEhlYWRpbmdPYmplY3QoY3VycilcblxuICAgICAgYWRkTm9kZShjdXJyZW50SGVhZGluZywgcHJldi5uZXN0KVxuICAgICAgcmV0dXJuIHByZXZcbiAgICB9LCB7XG4gICAgICBuZXN0OiBbXVxuICAgIH0pXG4gIH1cblxuICByZXR1cm4ge1xuICAgIG5lc3RIZWFkaW5nc0FycmF5OiBuZXN0SGVhZGluZ3NBcnJheSxcbiAgICBzZWxlY3RIZWFkaW5nczogc2VsZWN0SGVhZGluZ3NcbiAgfVxufVxuXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9zcmMvanMvcGFyc2UtY29udGVudC5qc1xuLy8gbW9kdWxlIGlkID0gNFxuLy8gbW9kdWxlIGNodW5rcyA9IDAiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9"); - -/***/ }), -/* 5 */ -/* unknown exports provided */ -/* all exports used */ -/*!*************************!*\ - !*** ./src/js/index.js ***! - \*************************/ -/***/ (function(module, exports, __webpack_require__) { - -eval("/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/**\n * Tocbot\n * Tocbot creates a toble of contents based on HTML headings on a page,\n * this allows users to easily jump to different sections of the document.\n * Tocbot was inspired by tocify (https://gregfranko.com/jquery.tocify.js/).\n * The main differences are that it works natively without any need for jquery or jquery UI).\n *\n * @author Tim Scanlin\n */\n\n/* globals define */\n\n(function (root, factory) {\n if (true) {\n !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory(root)),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?\n\t\t\t\t(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))\n } else if (typeof exports === 'object') {\n module.exports = factory(root)\n } else {\n root.tocbot = factory(root)\n }\n})(typeof global !== 'undefined' ? global : this.window || this.global, function (root) {\n 'use strict'\n\n // Default options.\n var defaultOptions = __webpack_require__(/*! ./default-options.js */ 3)\n // Object to store current options.\n var options = {}\n // Object for public APIs.\n var tocbot = {}\n\n var BuildHtml = __webpack_require__(/*! ./build-html.js */ 2)\n var ParseContent = __webpack_require__(/*! ./parse-content.js */ 4)\n // Keep these variables at top scope once options are passed in.\n var buildHtml\n var parseContent\n\n // Just return if its not a browser.\n if (typeof window === 'undefined') {\n return\n }\n var supports = !!root.document.querySelector && !!root.addEventListener // Feature test\n var headingsArray\n\n // From: https://github.com/Raynos/xtend\n var hasOwnProperty = Object.prototype.hasOwnProperty\n function extend () {\n var target = {}\n for (var i = 0; i < arguments.length; i++) {\n var source = arguments[i]\n for (var key in source) {\n if (hasOwnProperty.call(source, key)) {\n target[key] = source[key]\n }\n }\n }\n return target\n }\n\n // From: https://remysharp.com/2010/07/21/throttling-function-calls\n function throttle (fn, threshhold, scope) {\n threshhold || (threshhold = 250)\n var last\n var deferTimer\n return function () {\n var context = scope || this\n var now = +new Date()\n var args = arguments\n if (last && now < last + threshhold) {\n // hold on to it\n clearTimeout(deferTimer)\n deferTimer = setTimeout(function () {\n last = now\n fn.apply(context, args)\n }, threshhold)\n } else {\n last = now\n fn.apply(context, args)\n }\n }\n }\n\n /**\n * Destroy tocbot.\n */\n tocbot.destroy = function () {\n // Clear HTML.\n try {\n document.querySelector(options.tocSelector).innerHTML = ''\n } catch (e) {\n console.warn('Element not found: ' + options.tocSelector); // eslint-disable-line\n }\n\n // Remove event listeners.\n document.removeEventListener('scroll', this._scrollListener, false)\n document.removeEventListener('resize', this._scrollListener, false)\n if (buildHtml) {\n document.removeEventListener('click', this._clickListener, false)\n }\n }\n\n /**\n * Initialize tocbot.\n * @param {object} customOptions\n */\n tocbot.init = function (customOptions) {\n // feature test\n if (!supports) {\n return\n }\n\n // Merge defaults with user options.\n // Set to options variable at the top.\n options = extend(defaultOptions, customOptions || {})\n this.options = options\n this.state = {}\n\n // Init smooth scroll if enabled (default).\n if (options.smoothScroll) {\n tocbot.zenscroll = __webpack_require__(/*! zenscroll */ 1)\n tocbot.zenscroll.setup(options.smoothScrollDuration)\n }\n\n // Pass options to these modules.\n buildHtml = BuildHtml(options)\n parseContent = ParseContent(options)\n\n // For testing purposes.\n this._buildHtml = buildHtml\n this._parseContent = parseContent\n\n // Destroy it if it exists first.\n tocbot.destroy()\n\n // Get headings array.\n headingsArray = parseContent.selectHeadings(options.contentSelector, options.headingSelector)\n // Return if no headings are found.\n if (headingsArray === null) {\n return\n }\n\n // Build nested headings array.\n var nestedHeadingsObj = parseContent.nestHeadingsArray(headingsArray)\n var nestedHeadings = nestedHeadingsObj.nest\n\n // Render.\n buildHtml.render(options.tocSelector, nestedHeadings)\n\n // Update Sidebar and bind listeners.\n this._scrollListener = throttle(function (e) {\n buildHtml.updateToc(headingsArray)\n var isTop = e && e.target && e.target.scrollingElement && e.target.scrollingElement.scrollTop === 0\n if ((e && e.eventPhase === 0) || isTop) {\n buildHtml.enableTocAnimation()\n buildHtml.updateToc(headingsArray)\n if (options.scrollEndCallback) {\n options.scrollEndCallback(e)\n }\n }\n }, options.throttleTimeout)\n this._scrollListener()\n document.addEventListener('scroll', this._scrollListener, false)\n document.addEventListener('resize', this._scrollListener, false)\n\n // Bind click listeners to disable animation.\n this._clickListener = throttle(function (event) {\n if (options.smoothScroll) {\n buildHtml.disableTocAnimation(event)\n }\n buildHtml.updateToc(headingsArray)\n }, options.throttleTimeout)\n document.addEventListener('click', this._clickListener, false)\n\n return this\n }\n\n /**\n * Refresh tocbot.\n */\n tocbot.refresh = function (customOptions) {\n tocbot.destroy()\n tocbot.init(customOptions || this.options)\n }\n\n // Make tocbot available globally.\n root.tocbot = tocbot\n\n return tocbot\n})\n\n/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(/*! ./../../~/webpack/buildin/global.js */ 0)))//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3NyYy9qcy9pbmRleC5qcz9iYzY2Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVG9jYm90XG4gKiBUb2Nib3QgY3JlYXRlcyBhIHRvYmxlIG9mIGNvbnRlbnRzIGJhc2VkIG9uIEhUTUwgaGVhZGluZ3Mgb24gYSBwYWdlLFxuICogdGhpcyBhbGxvd3MgdXNlcnMgdG8gZWFzaWx5IGp1bXAgdG8gZGlmZmVyZW50IHNlY3Rpb25zIG9mIHRoZSBkb2N1bWVudC5cbiAqIFRvY2JvdCB3YXMgaW5zcGlyZWQgYnkgdG9jaWZ5IChodHRwOi8vZ3JlZ2ZyYW5rby5jb20vanF1ZXJ5LnRvY2lmeS5qcy8pLlxuICogVGhlIG1haW4gZGlmZmVyZW5jZXMgYXJlIHRoYXQgaXQgd29ya3MgbmF0aXZlbHkgd2l0aG91dCBhbnkgbmVlZCBmb3IganF1ZXJ5IG9yIGpxdWVyeSBVSSkuXG4gKlxuICogQGF1dGhvciBUaW0gU2NhbmxpblxuICovXG5cbi8qIGdsb2JhbHMgZGVmaW5lICovXG5cbihmdW5jdGlvbiAocm9vdCwgZmFjdG9yeSkge1xuICBpZiAodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKSB7XG4gICAgZGVmaW5lKFtdLCBmYWN0b3J5KHJvb3QpKVxuICB9IGVsc2UgaWYgKHR5cGVvZiBleHBvcnRzID09PSAnb2JqZWN0Jykge1xuICAgIG1vZHVsZS5leHBvcnRzID0gZmFjdG9yeShyb290KVxuICB9IGVsc2Uge1xuICAgIHJvb3QudG9jYm90ID0gZmFjdG9yeShyb290KVxuICB9XG59KSh0eXBlb2YgZ2xvYmFsICE9PSAndW5kZWZpbmVkJyA/IGdsb2JhbCA6IHRoaXMud2luZG93IHx8IHRoaXMuZ2xvYmFsLCBmdW5jdGlvbiAocm9vdCkge1xuICAndXNlIHN0cmljdCdcblxuICAvLyBEZWZhdWx0IG9wdGlvbnMuXG4gIHZhciBkZWZhdWx0T3B0aW9ucyA9IHJlcXVpcmUoJy4vZGVmYXVsdC1vcHRpb25zLmpzJylcbiAgLy8gT2JqZWN0IHRvIHN0b3JlIGN1cnJlbnQgb3B0aW9ucy5cbiAgdmFyIG9wdGlvbnMgPSB7fVxuICAvLyBPYmplY3QgZm9yIHB1YmxpYyBBUElzLlxuICB2YXIgdG9jYm90ID0ge31cblxuICB2YXIgQnVpbGRIdG1sID0gcmVxdWlyZSgnLi9idWlsZC1odG1sLmpzJylcbiAgdmFyIFBhcnNlQ29udGVudCA9IHJlcXVpcmUoJy4vcGFyc2UtY29udGVudC5qcycpXG4gIC8vIEtlZXAgdGhlc2UgdmFyaWFibGVzIGF0IHRvcCBzY29wZSBvbmNlIG9wdGlvbnMgYXJlIHBhc3NlZCBpbi5cbiAgdmFyIGJ1aWxkSHRtbFxuICB2YXIgcGFyc2VDb250ZW50XG5cbiAgLy8gSnVzdCByZXR1cm4gaWYgaXRzIG5vdCBhIGJyb3dzZXIuXG4gIGlmICh0eXBlb2Ygd2luZG93ID09PSAndW5kZWZpbmVkJykge1xuICAgIHJldHVyblxuICB9XG4gIHZhciBzdXBwb3J0cyA9ICEhcm9vdC5kb2N1bWVudC5xdWVyeVNlbGVjdG9yICYmICEhcm9vdC5hZGRFdmVudExpc3RlbmVyIC8vIEZlYXR1cmUgdGVzdFxuICB2YXIgaGVhZGluZ3NBcnJheVxuXG4gIC8vIEZyb206IGh0dHBzOi8vZ2l0aHViLmNvbS9SYXlub3MveHRlbmRcbiAgdmFyIGhhc093blByb3BlcnR5ID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eVxuICBmdW5jdGlvbiBleHRlbmQgKCkge1xuICAgIHZhciB0YXJnZXQgPSB7fVxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgc291cmNlID0gYXJndW1lbnRzW2ldXG4gICAgICBmb3IgKHZhciBrZXkgaW4gc291cmNlKSB7XG4gICAgICAgIGlmIChoYXNPd25Qcm9wZXJ0eS5jYWxsKHNvdXJjZSwga2V5KSkge1xuICAgICAgICAgIHRhcmdldFtrZXldID0gc291cmNlW2tleV1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGFyZ2V0XG4gIH1cblxuICAvLyBGcm9tOiBodHRwczovL3JlbXlzaGFycC5jb20vMjAxMC8wNy8yMS90aHJvdHRsaW5nLWZ1bmN0aW9uLWNhbGxzXG4gIGZ1bmN0aW9uIHRocm90dGxlIChmbiwgdGhyZXNoaG9sZCwgc2NvcGUpIHtcbiAgICB0aHJlc2hob2xkIHx8ICh0aHJlc2hob2xkID0gMjUwKVxuICAgIHZhciBsYXN0XG4gICAgdmFyIGRlZmVyVGltZXJcbiAgICByZXR1cm4gZnVuY3Rpb24gKCkge1xuICAgICAgdmFyIGNvbnRleHQgPSBzY29wZSB8fCB0aGlzXG4gICAgICB2YXIgbm93ID0gK25ldyBEYXRlKClcbiAgICAgIHZhciBhcmdzID0gYXJndW1lbnRzXG4gICAgICBpZiAobGFzdCAmJiBub3cgPCBsYXN0ICsgdGhyZXNoaG9sZCkge1xuICAgICAgICAvLyBob2xkIG9uIHRvIGl0XG4gICAgICAgIGNsZWFyVGltZW91dChkZWZlclRpbWVyKVxuICAgICAgICBkZWZlclRpbWVyID0gc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgbGFzdCA9IG5vd1xuICAgICAgICAgIGZuLmFwcGx5KGNvbnRleHQsIGFyZ3MpXG4gICAgICAgIH0sIHRocmVzaGhvbGQpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsYXN0ID0gbm93XG4gICAgICAgIGZuLmFwcGx5KGNvbnRleHQsIGFyZ3MpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIERlc3Ryb3kgdG9jYm90LlxuICAgKi9cbiAgdG9jYm90LmRlc3Ryb3kgPSBmdW5jdGlvbiAoKSB7XG4gICAgLy8gQ2xlYXIgSFRNTC5cbiAgICB0cnkge1xuICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcihvcHRpb25zLnRvY1NlbGVjdG9yKS5pbm5lckhUTUwgPSAnJ1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUud2FybignRWxlbWVudCBub3QgZm91bmQ6ICcgKyBvcHRpb25zLnRvY1NlbGVjdG9yKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZVxuICAgIH1cblxuICAgIC8vIFJlbW92ZSBldmVudCBsaXN0ZW5lcnMuXG4gICAgZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhpcy5fc2Nyb2xsTGlzdGVuZXIsIGZhbHNlKVxuICAgIGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHRoaXMuX3Njcm9sbExpc3RlbmVyLCBmYWxzZSlcbiAgICBpZiAoYnVpbGRIdG1sKSB7XG4gICAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdjbGljaycsIHRoaXMuX2NsaWNrTGlzdGVuZXIsIGZhbHNlKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplIHRvY2JvdC5cbiAgICogQHBhcmFtIHtvYmplY3R9IGN1c3RvbU9wdGlvbnNcbiAgICovXG4gIHRvY2JvdC5pbml0ID0gZnVuY3Rpb24gKGN1c3RvbU9wdGlvbnMpIHtcbiAgICAvLyBmZWF0dXJlIHRlc3RcbiAgICBpZiAoIXN1cHBvcnRzKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBNZXJnZSBkZWZhdWx0cyB3aXRoIHVzZXIgb3B0aW9ucy5cbiAgICAvLyBTZXQgdG8gb3B0aW9ucyB2YXJpYWJsZSBhdCB0aGUgdG9wLlxuICAgIG9wdGlvbnMgPSBleHRlbmQoZGVmYXVsdE9wdGlvbnMsIGN1c3RvbU9wdGlvbnMgfHwge30pXG4gICAgdGhpcy5vcHRpb25zID0gb3B0aW9uc1xuICAgIHRoaXMuc3RhdGUgPSB7fVxuXG4gICAgLy8gSW5pdCBzbW9vdGggc2Nyb2xsIGlmIGVuYWJsZWQgKGRlZmF1bHQpLlxuICAgIGlmIChvcHRpb25zLnNtb290aFNjcm9sbCkge1xuICAgICAgdG9jYm90LnplbnNjcm9sbCA9IHJlcXVpcmUoJ3plbnNjcm9sbCcpXG4gICAgICB0b2Nib3QuemVuc2Nyb2xsLnNldHVwKG9wdGlvbnMuc21vb3RoU2Nyb2xsRHVyYXRpb24pXG4gICAgfVxuXG4gICAgLy8gUGFzcyBvcHRpb25zIHRvIHRoZXNlIG1vZHVsZXMuXG4gICAgYnVpbGRIdG1sID0gQnVpbGRIdG1sKG9wdGlvbnMpXG4gICAgcGFyc2VDb250ZW50ID0gUGFyc2VDb250ZW50KG9wdGlvbnMpXG5cbiAgICAvLyBGb3IgdGVzdGluZyBwdXJwb3Nlcy5cbiAgICB0aGlzLl9idWlsZEh0bWwgPSBidWlsZEh0bWxcbiAgICB0aGlzLl9wYXJzZUNvbnRlbnQgPSBwYXJzZUNvbnRlbnRcblxuICAgIC8vIERlc3Ryb3kgaXQgaWYgaXQgZXhpc3RzIGZpcnN0LlxuICAgIHRvY2JvdC5kZXN0cm95KClcblxuICAgIC8vIEdldCBoZWFkaW5ncyBhcnJheS5cbiAgICBoZWFkaW5nc0FycmF5ID0gcGFyc2VDb250ZW50LnNlbGVjdEhlYWRpbmdzKG9wdGlvbnMuY29udGVudFNlbGVjdG9yLCBvcHRpb25zLmhlYWRpbmdTZWxlY3RvcilcbiAgICAvLyBSZXR1cm4gaWYgbm8gaGVhZGluZ3MgYXJlIGZvdW5kLlxuICAgIGlmIChoZWFkaW5nc0FycmF5ID09PSBudWxsKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBCdWlsZCBuZXN0ZWQgaGVhZGluZ3MgYXJyYXkuXG4gICAgdmFyIG5lc3RlZEhlYWRpbmdzT2JqID0gcGFyc2VDb250ZW50Lm5lc3RIZWFkaW5nc0FycmF5KGhlYWRpbmdzQXJyYXkpXG4gICAgdmFyIG5lc3RlZEhlYWRpbmdzID0gbmVzdGVkSGVhZGluZ3NPYmoubmVzdFxuXG4gICAgLy8gUmVuZGVyLlxuICAgIGJ1aWxkSHRtbC5yZW5kZXIob3B0aW9ucy50b2NTZWxlY3RvciwgbmVzdGVkSGVhZGluZ3MpXG5cbiAgICAvLyBVcGRhdGUgU2lkZWJhciBhbmQgYmluZCBsaXN0ZW5lcnMuXG4gICAgdGhpcy5fc2Nyb2xsTGlzdGVuZXIgPSB0aHJvdHRsZShmdW5jdGlvbiAoZSkge1xuICAgICAgYnVpbGRIdG1sLnVwZGF0ZVRvYyhoZWFkaW5nc0FycmF5KVxuICAgICAgdmFyIGlzVG9wID0gZSAmJiBlLnRhcmdldCAmJiBlLnRhcmdldC5zY3JvbGxpbmdFbGVtZW50ICYmIGUudGFyZ2V0LnNjcm9sbGluZ0VsZW1lbnQuc2Nyb2xsVG9wID09PSAwXG4gICAgICBpZiAoKGUgJiYgZS5ldmVudFBoYXNlID09PSAwKSB8fCBpc1RvcCkge1xuICAgICAgICBidWlsZEh0bWwuZW5hYmxlVG9jQW5pbWF0aW9uKClcbiAgICAgICAgYnVpbGRIdG1sLnVwZGF0ZVRvYyhoZWFkaW5nc0FycmF5KVxuICAgICAgICBpZiAob3B0aW9ucy5zY3JvbGxFbmRDYWxsYmFjaykge1xuICAgICAgICAgIG9wdGlvbnMuc2Nyb2xsRW5kQ2FsbGJhY2soZSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0sIG9wdGlvbnMudGhyb3R0bGVUaW1lb3V0KVxuICAgIHRoaXMuX3Njcm9sbExpc3RlbmVyKClcbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCB0aGlzLl9zY3JvbGxMaXN0ZW5lciwgZmFsc2UpXG4gICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigncmVzaXplJywgdGhpcy5fc2Nyb2xsTGlzdGVuZXIsIGZhbHNlKVxuXG4gICAgLy8gQmluZCBjbGljayBsaXN0ZW5lcnMgdG8gZGlzYWJsZSBhbmltYXRpb24uXG4gICAgdGhpcy5fY2xpY2tMaXN0ZW5lciA9IHRocm90dGxlKGZ1bmN0aW9uIChldmVudCkge1xuICAgICAgaWYgKG9wdGlvbnMuc21vb3RoU2Nyb2xsKSB7XG4gICAgICAgIGJ1aWxkSHRtbC5kaXNhYmxlVG9jQW5pbWF0aW9uKGV2ZW50KVxuICAgICAgfVxuICAgICAgYnVpbGRIdG1sLnVwZGF0ZVRvYyhoZWFkaW5nc0FycmF5KVxuICAgIH0sIG9wdGlvbnMudGhyb3R0bGVUaW1lb3V0KVxuICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgdGhpcy5fY2xpY2tMaXN0ZW5lciwgZmFsc2UpXG5cbiAgICByZXR1cm4gdGhpc1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZnJlc2ggdG9jYm90LlxuICAgKi9cbiAgdG9jYm90LnJlZnJlc2ggPSBmdW5jdGlvbiAoY3VzdG9tT3B0aW9ucykge1xuICAgIHRvY2JvdC5kZXN0cm95KClcbiAgICB0b2Nib3QuaW5pdChjdXN0b21PcHRpb25zIHx8IHRoaXMub3B0aW9ucylcbiAgfVxuXG4gIC8vIE1ha2UgdG9jYm90IGF2YWlsYWJsZSBnbG9iYWxseS5cbiAgcm9vdC50b2Nib3QgPSB0b2Nib3RcblxuICByZXR1cm4gdG9jYm90XG59KVxuXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9zcmMvanMvaW5kZXguanNcbi8vIG1vZHVsZSBpZCA9IDVcbi8vIG1vZHVsZSBjaHVua3MgPSAwIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFBQTtBQUFBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBIiwic291cmNlUm9vdCI6IiJ9"); - -/***/ }) -/******/ ]); \ No newline at end of file diff --git a/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.min.js b/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.min.js deleted file mode 100644 index 4468ef4ee73c..000000000000 --- a/documentation/src/docs/asciidoc/tocbot-3.0.2/tocbot.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){function t(o){if(n[o])return n[o].exports;var i=n[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=5)}([function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){var o,i,r;!function(n,l){i=[],o=l(),void 0!==(r="function"==typeof o?o.apply(t,i):o)&&(e.exports=r)}(0,function(){"use strict";var e=function(e){return"getComputedStyle"in window&&"smooth"===window.getComputedStyle(e)["scroll-behavior"]};if("undefined"==typeof window||!("document"in window))return{};var t=function(t,n,o){n=n||999,o||0===o||(o=9);var i,r=function(e){i=e},l=function(){clearTimeout(i),r(0)},s=function(e){return Math.max(0,t.getTopOf(e)-o)},c=function(o,i,s){if(l(),0===i||i&&i<0||e(t.body))t.toY(o),s&&s();else{var c=t.getY(),a=Math.max(0,o)-c,u=(new Date).getTime();i=i||Math.min(Math.abs(a),n),function e(){r(setTimeout(function(){var n=Math.min(1,((new Date).getTime()-u)/i),o=Math.max(0,Math.floor(c+a*(n<.5?2*n*n:n*(4-2*n)-1)));t.toY(o),n<1&&t.getHeight()+ou?a(e,n,i):l+o>f?c(l-u+o,n,i):i&&i()},d=function(e,n,o,i){c(Math.max(0,t.getTopOf(e)-t.getHeight()/2+(o||e.getBoundingClientRect().height/2)),n,i)};return{setup:function(e,t){return(0===e||e)&&(n=e),(0===t||t)&&(o=t),{defaultDuration:n,edgeOffset:o}},to:a,toY:c,intoView:u,center:d,stop:l,moving:function(){return!!i},getY:t.getY,getTopOf:t.getTopOf}},n=document.documentElement,o=function(){return window.scrollY||n.scrollTop},i=t({body:document.scrollingElement||document.body,toY:function(e){window.scrollTo(0,e)},getY:o,getHeight:function(){return window.innerHeight||n.clientHeight},getTopOf:function(e){return e.getBoundingClientRect().top+o()-n.offsetTop}});if(i.createScroller=function(e,o,i){return t({body:e,toY:function(t){e.scrollTop=t},getY:function(){return e.scrollTop},getHeight:function(){return Math.min(e.clientHeight,window.innerHeight||n.clientHeight)},getTopOf:function(e){return e.offsetTop}},o,i)},"addEventListener"in window&&!window.noZensmooth&&!e(document.body)){var r="scrollRestoration"in history;r&&(history.scrollRestoration="auto"),window.addEventListener("load",function(){r&&(setTimeout(function(){history.scrollRestoration="manual"},9),window.addEventListener("popstate",function(e){e.state&&"zenscrollY"in e.state&&i.toY(e.state.zenscrollY)},!1)),window.location.hash&&setTimeout(function(){var e=i.setup().edgeOffset;if(e){var t=document.getElementById(window.location.href.split("#")[1]);if(t){var n=Math.max(0,i.getTopOf(t)-e),o=i.getY()-n;0<=o&&o<9&&window.scrollTo(0,n)}}},9)},!1);var l=new RegExp("(^|\\s)noZensmooth(\\s|$)");window.addEventListener("click",function(e){for(var t=e.target;t&&"A"!==t.tagName;)t=t.parentNode;if(!(!t||1!==e.which||e.shiftKey||e.metaKey||e.ctrlKey||e.altKey)){if(r)try{history.replaceState({zenscrollY:i.getY()},"")}catch(e){}var n=t.getAttribute("href")||"";if(0===n.indexOf("#")&&!l.test(t.className)){var o=0,s=document.getElementById(n.substring(1));if("#"!==n){if(!s)return;o=i.getTopOf(s)}e.preventDefault();var c=function(){window.location=n},a=i.setup().edgeOffset;a&&(o=Math.max(0,o-a),c=function(){history.pushState(null,"",n)}),i.toY(o,null,c)}}},!1)}return i})},function(e,t){e.exports=function(e){function t(e,n){var r=n.appendChild(o(e));if(e.children.length){var l=i(e.isCollapsed);e.children.forEach(function(e){t(e,l)}),r.appendChild(l)}}function n(e,n){var o=i(!1);n.forEach(function(e){t(e,o)});var r=document.querySelector(e);if(null!==r)return r.firstChild&&r.removeChild(r.firstChild),r.appendChild(o)}function o(t){var n=document.createElement("li"),o=document.createElement("a");return e.listItemClass&&n.setAttribute("class",e.listItemClass),e.includeHtml&&t.childNodes.length?u.call(t.childNodes,function(e){o.appendChild(e.cloneNode(!0))}):o.textContent=t.textContent,o.setAttribute("href","#"+t.id),o.setAttribute("class",e.linkClass+p+"node-name--"+t.nodeName+p+e.extraLinkClasses),n.appendChild(o),n}function i(t){var n=document.createElement("ul"),o=e.listClass+p+e.extraListClasses;return t&&(o+=p+e.collapsibleClass,o+=p+e.isCollapsedClass),n.setAttribute("class",o),n}function r(){var t=document.documentElement.scrollTop||f.scrollTop,n=document.querySelector(e.positionFixedSelector);"auto"===e.fixedSidebarOffset&&(e.fixedSidebarOffset=document.querySelector(e.tocSelector).offsetTop),t>e.fixedSidebarOffset?-1===n.className.indexOf(e.positionFixedClass)&&(n.className+=p+e.positionFixedClass):n.className=n.className.split(p+e.positionFixedClass).join("")}function l(t){var n=document.documentElement.scrollTop||f.scrollTop;e.positionFixedSelector&&r();var o,i=t;if(m&&null!==document.querySelector(e.tocSelector)&&i.length>0){d.call(i,function(t,r){if(t.offsetTop>n+e.headingsOffset+10){return o=i[0===r?r:r-1],!0}if(r===i.length-1)return o=i[i.length-1],!0});var l=document.querySelector(e.tocSelector).querySelectorAll("."+e.linkClass);u.call(l,function(t){t.className=t.className.split(p+e.activeLinkClass).join("")});var c=document.querySelector(e.tocSelector).querySelector("."+e.linkClass+".node-name--"+o.nodeName+'[href="#'+o.id+'"]');c.className+=p+e.activeLinkClass;var a=document.querySelector(e.tocSelector).querySelectorAll("."+e.listClass+"."+e.collapsibleClass);u.call(a,function(t){var n=p+e.isCollapsedClass;-1===t.className.indexOf(n)&&(t.className+=p+e.isCollapsedClass)}),c.nextSibling&&(c.nextSibling.className=c.nextSibling.className.split(p+e.isCollapsedClass).join("")),s(c.parentNode.parentNode)}}function s(t){return-1!==t.className.indexOf(e.collapsibleClass)?(t.className=t.className.split(p+e.isCollapsedClass).join(""),s(t.parentNode.parentNode)):t}function c(t){var n=t.target||t.srcElement;"string"==typeof n.className&&-1!==n.className.indexOf(e.linkClass)&&(m=!1)}function a(){m=!0}var u=[].forEach,d=[].some,f=document.body,m=!0,p=" ";return{enableTocAnimation:a,disableTocAnimation:c,render:n,updateToc:l}}},function(e,t){e.exports={tocSelector:".js-toc",contentSelector:".js-toc-content",headingSelector:"h1, h2, h3",ignoreSelector:".js-toc-ignore",linkClass:"toc-link",extraLinkClasses:"",activeLinkClass:"is-active-link",listClass:"toc-list",extraListClasses:"",isCollapsedClass:"is-collapsed",collapsibleClass:"is-collapsible",listItemClass:"toc-list-item",collapseDepth:0,smoothScroll:!0,smoothScrollDuration:420,scrollEndCallback:function(e){},headingsOffset:0,throttleTimeout:50,positionFixedSelector:null,positionFixedClass:"is-position-fixed",fixedSidebarOffset:"auto",includeHtml:!1}},function(e,t){e.exports=function(e){function t(e){return e[e.length-1]}function n(e){return+e.nodeName.split("H").join("")}function o(t){var o={id:t.id,children:[],nodeName:t.nodeName,headingLevel:n(t),textContent:t.textContent.trim()};return e.includeHtml&&(o.childNodes=t.childNodes),o}function i(i,r){for(var l=o(i),s=n(i),c=r,a=t(c),u=a?a.headingLevel:0,d=s-u;d>0;)a=t(c),a&&void 0!==a.children&&(c=a.children),d--;return s>=e.collapseDepth&&(l.isCollapsed=!0),c.push(l),c}function r(t,n){var o=n;e.ignoreSelector&&(o=n.split(",").map(function(t){return t.trim()+":not("+e.ignoreSelector+")"}));try{return document.querySelector(t).querySelectorAll(o)}catch(e){return console.warn("Element not found: "+t),null}}function l(e){return s.call(e,function(e,t){return i(o(t),e.nest),e},{nest:[]})}var s=[].reduce;return{nestHeadingsArray:l,selectHeadings:r}}},function(e,t,n){(function(o){var i,r,l;!function(n,o){r=[],i=o(n),void 0!==(l="function"==typeof i?i.apply(t,r):i)&&(e.exports=l)}(void 0!==o?o:this.window||this.global,function(e){"use strict";function t(){for(var e={},t=0;t>, _programmatically_ via -<>, or _automatically_ via -Java's <> mechanism. - -[[extensions-registration-declarative]] -==== Declarative Extension Registration - -Developers can register one or more extensions _declaratively_ by annotating a test -interface, test class, test method, or custom _<>_ with `@ExtendWith(...)` and supplying class references for the extensions to -register. `@ExtendWith` may also be declared on fields or on parameters in test class -constructors, in test methods, and in `@BeforeAll`, `@AfterAll`, `@BeforeEach`, and -`@AfterEach` lifecycle methods. - -For example, to register a `WebServerExtension` for a particular test method, you would -annotate the test method as follows. We assume the `WebServerExtension` starts a local web -server and injects the server's URL into parameters annotated with `@WebServerUrl`. - -[source,java,indent=0] ----- -@Test -@ExtendWith(WebServerExtension.class) -void getProductList(@WebServerUrl String serverUrl) { - WebClient webClient = new WebClient(); - // Use WebClient to connect to web server using serverUrl and verify response - assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus()); -} ----- - -To register the `WebServerExtension` for all tests in a particular class and its -subclasses, you would annotate the test class as follows. - -[source,java,indent=0] ----- -@ExtendWith(WebServerExtension.class) -class MyTests { - // ... -} ----- - -Multiple extensions can be registered together like this: - -[source,java,indent=0] ----- -@ExtendWith({ DatabaseExtension.class, WebServerExtension.class }) -class MyFirstTests { - // ... -} ----- - -As an alternative, multiple extensions can be registered separately like this: - -[source,java,indent=0] ----- -@ExtendWith(DatabaseExtension.class) -@ExtendWith(WebServerExtension.class) -class MySecondTests { - // ... -} ----- - -[TIP] -.Extension Registration Order -==== -Extensions registered declaratively via `@ExtendWith` at the class level, method level, or -parameter level will be executed in the order in which they are declared in the source -code. For example, the execution of tests in both `MyFirstTests` and `MySecondTests` will -be extended by the `DatabaseExtension` and `WebServerExtension`, **in exactly that order**. -==== - -If you wish to combine multiple extensions in a reusable way, you can define a custom -_<>_ and use `@ExtendWith` as a -_meta-annotation_ as in the following code listing. Then `@DatabaseAndWebServerExtension` -can be used in place of `@ExtendWith({ DatabaseExtension.class, WebServerExtension.class })`. - -[source,java,indent=0] ----- -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -@ExtendWith({ DatabaseExtension.class, WebServerExtension.class }) -public @interface DatabaseAndWebServerExtension { -} ----- - -The above examples demonstrate how `@ExtendWith` can be applied at the class level or at -the method level; however, for certain use cases it makes sense for an extension to be -registered declaratively at the field or parameter level. Consider a -`RandomNumberExtension` which generates random numbers that can be injected into a field or -via a parameter in a constructor, test method, or lifecycle method. If the extension -provides a `@Random` annotation that is meta-annotated with -`@ExtendWith(RandomNumberExtension.class)` (see listing below), the extension can be used -transparently as in the following `RandomNumberDemo` example. - -[source,java,indent=0] ----- -include::{testDir}/example/extensions/Random.java[tags=user_guide] ----- - -[source,java,indent=0] ----- -include::{testDir}/example/extensions/RandomNumberDemo.java[tags=user_guide] ----- - -[[extensions-RandomNumberExtension]] -The following code listing provides an example of how one might choose to implement such a -`RandomNumberExtension`. This implementation works for the use cases in -`RandomNumberDemo`; however, it may not prove robust enough to cover all use cases -- for -example, the random number generation support is limited to integers; it uses -`java.util.Random` instead of `java.security.SecureRandom`; etc. In any case, it is -important to note which extension APIs are implemented and for what reasons. - -Specifically, `RandomNumberExtension` implements the following extension APIs: - -- `BeforeAllCallback`: to support static field injection -- `TestInstancePostProcessor`: to support non-static field injection -- `ParameterResolver`: to support constructor and method injection - -[source,java,indent=0] ----- -include::{testDir}/example/extensions/RandomNumberExtension.java[tags=user_guide] ----- - -[TIP] -.Extension Registration Order for `@ExtendWith` on Fields -==== -Extensions registered declaratively via `@ExtendWith` on fields will be ordered relative -to `@RegisterExtension` fields and other `@ExtendWith` fields using an algorithm that is -deterministic but intentionally nonobvious. However, `@ExtendWith` fields can be ordered -using the `@Order` annotation. See the <> tip for `@RegisterExtension` fields for details. -==== - -[TIP] -.Extension Inheritance -==== -Extensions registered declaratively via `@ExtendWith` on fields in superclasses will be -inherited. - -See <> for details. -==== - -NOTE: `@ExtendWith` fields may be either `static` or non-static. The documentation on -<> and -<> for -`@RegisterExtension` fields also applies to `@ExtendWith` fields. - -[[extensions-registration-programmatic]] -==== Programmatic Extension Registration - -Developers can register extensions _programmatically_ by annotating fields in test classes -with `{RegisterExtension}`. - -When an extension is registered _declaratively_ via -<>, it can typically only be configured -via annotations. In contrast, when an extension is registered via `@RegisterExtension`, it -can be configured _programmatically_ -- for example, in order to pass arguments to the -extension's constructor, a static factory method, or a builder API. - -[[extensions-registration-programmatic-order]] -[TIP] -.Extension Registration Order -==== -By default, extensions registered programmatically via `@RegisterExtension` or -declaratively via `@ExtendWith` on fields will be ordered using an algorithm that is -deterministic but intentionally nonobvious. This ensures that subsequent runs of a test -suite execute extensions in the same order, thereby allowing for repeatable builds. -However, there are times when extensions need to be registered in an explicit order. To -achieve that, annotate `@RegisterExtension` fields or `@ExtendWith` fields with `{Order}`. - -Any `@RegisterExtension` field or `@ExtendWith` field not annotated with `@Order` will be -ordered using the _default_ order which has a value of `Integer.MAX_VALUE / 2`. This -allows `@Order` annotated extension fields to be explicitly ordered before or after -non-annotated extension fields. Extensions with an explicit order value less than the -default order value will be registered before non-annotated extensions. Similarly, -extensions with an explicit order value greater than the default order value will be -registered after non-annotated extensions. For example, assigning an extension an explicit -order value that is greater than the default order value allows _before_ callback -extensions to be registered last and _after_ callback extensions to be registered first, -relative to other programmatically registered extensions. -==== - -[TIP] -.Extension Inheritance -==== -Extensions registered via `@RegisterExtension` or `@ExtendWith` on fields in superclasses -will be inherited. - -See <> for details. -==== - -NOTE: `@RegisterExtension` fields must not be `null` (at evaluation time) but may be -either `static` or non-static. - -[[extensions-registration-programmatic-static-fields]] -===== Static Fields - -If a `@RegisterExtension` field is `static`, the extension will be registered after -extensions that are registered at the class level via `@ExtendWith`. Such _static -extensions_ are not limited in which extension APIs they can implement. Extensions -registered via static fields may therefore implement class-level and instance-level -extension APIs such as `BeforeAllCallback`, `AfterAllCallback`, -`TestInstancePostProcessor`, and `TestInstancePreDestroyCallback` as well as method-level -extension APIs such as `BeforeEachCallback`, etc. - -In the following example, the `server` field in the test class is initialized -programmatically by using a builder pattern supported by the `WebServerExtension`. The -configured `WebServerExtension` will be automatically registered as an extension at the -class level -- for example, in order to start the server before all tests in the class -and then stop the server after all tests in the class have completed. In addition, static -lifecycle methods annotated with `@BeforeAll` or `@AfterAll` as well as `@BeforeEach`, -`@AfterEach`, and `@Test` methods can access the instance of the extension via the -`server` field if necessary. - -[source,java,indent=0] -.Registering an extension via a static field in Java ----- -include::{testDir}/example/registration/WebServerDemo.java[tags=user_guide] ----- - -[[extensions-registration-programmatic-static-fields-kotlin]] -====== Static Fields in Kotlin - -The Kotlin programming language does not have the concept of a `static` field. However, -the compiler can be instructed to generate a `private static` field using the `@JvmStatic` -annotation in Kotlin. If you want the Kotlin compiler to generate a `public static` field, -you can use the `@JvmField` annotation instead. - -The following example is a version of the `WebServerDemo` from the previous section that -has been ported to Kotlin. - -[source,kotlin,indent=0] -.Registering an extension via a static field in Kotlin ----- -include::{kotlinTestDir}/example/registration/KotlinWebServerDemo.kt[tags=user_guide] ----- - -[[extensions-registration-programmatic-instance-fields]] -===== Instance Fields - -If a `@RegisterExtension` field is non-static (i.e., an instance field), the extension -will be registered after the test class has been instantiated and after each registered -`TestInstancePostProcessor` has been given a chance to post-process the test instance -(potentially injecting the instance of the extension to be used into the annotated -field). Thus, if such an _instance extension_ implements class-level or instance-level -extension APIs such as `BeforeAllCallback`, `AfterAllCallback`, or -`TestInstancePostProcessor`, those APIs will not be honored. Instance extensions will be -registered _before_ extensions that are registered at the method level via `@ExtendWith`. - -In the following example, the `docs` field in the test class is initialized -programmatically by invoking a custom `lookUpDocsDir()` method and supplying the result -to the static `forPath()` factory method in the `DocumentationExtension`. The configured -`DocumentationExtension` will be automatically registered as an extension at the method -level. In addition, `@BeforeEach`, `@AfterEach`, and `@Test` methods can access the -instance of the extension via the `docs` field if necessary. - -[source,java,indent=0] -.An extension registered via an instance field ----- -include::{testDir}/example/registration/DocumentationDemo.java[tags=user_guide] ----- - -[[extensions-registration-automatic]] -==== Automatic Extension Registration - -In addition to <> -and <> support -using annotations, JUnit Jupiter also supports _global extension registration_ via Java's -`{ServiceLoader}` mechanism, allowing third-party extensions to be auto-detected and -automatically registered based on what is available in the classpath. - -Specifically, a custom extension can be registered by supplying its fully qualified class -name in a file named `org.junit.jupiter.api.extension.Extension` within the -`/META-INF/services` folder in its enclosing JAR file. - -[[extensions-registration-automatic-enabling]] -===== Enabling Automatic Extension Detection - -Auto-detection is an advanced feature and is therefore not enabled by default. To enable -it, set the `junit.jupiter.extensions.autodetection.enabled` _configuration parameter_ to -`true`. This can be supplied as a JVM system property, as a _configuration parameter_ in -the `LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform -configuration file (see <> for details). - -For example, to enable auto-detection of extensions, you can start your JVM with the -following system property. - -`-Djunit.jupiter.extensions.autodetection.enabled=true` - -When auto-detection is enabled, extensions discovered via the `{ServiceLoader}` mechanism -will be added to the extension registry after JUnit Jupiter's global extensions (e.g., -support for `TestInfo`, `TestReporter`, etc.). - -[[extensions-registration-automatic-filtering]] -===== Filtering Auto-detected Extensions - -The list of auto-detected extensions can be filtered using include and exclude patterns -via the following <>: - -`junit.jupiter.extensions.autodetection.include=`:: - Comma-separated list of _include_ patterns for auto-detected extensions. -`junit.jupiter.extensions.autodetection.exclude=`:: - Comma-separated list of _exclude_ patterns for auto-detected extensions. - -Include patterns are applied _before_ exclude patterns. If both include and exclude -patterns are provided, only extensions that match at least one include pattern and do not -match any exclude pattern will be auto-detected. - -See <> for details on the pattern syntax. - -[[extensions-registration-inheritance]] -==== Extension Inheritance - -Registered extensions are inherited within test class hierarchies with top-down semantics. -Similarly, extensions registered at the class-level are inherited at the method-level. -This applies to all extensions, independent of how they are registered (declaratively or -programmatically). - -This means that extensions registered declaratively via `@ExtendWith` on a superclass will -be registered before extensions registered declaratively via `@ExtendWith` on a subclass. - -Similarly, extensions registered programmatically via `@RegisterExtension` or -`@ExtendWith` on fields in a superclass will be registered before extensions registered -programmatically via `@RegisterExtension` or `@ExtendWith` on fields in a subclass, unless -`@Order` is used to alter that behavior (see <> for details). - -NOTE: A specific extension implementation can only be registered once for a given -extension context and its parent contexts. Consequently, any attempt to register a -duplicate extension implementation will be ignored. - -[[extensions-conditions]] -=== Conditional Test Execution - -`{ExecutionCondition}` defines the `Extension` API for programmatic, _conditional test -execution_. - -An `ExecutionCondition` is _evaluated_ for each container (e.g., a test class) to -determine if all the tests it contains should be executed based on the supplied -`ExtensionContext`. Similarly, an `ExecutionCondition` is _evaluated_ for each test to -determine if a given test method should be executed based on the supplied -`ExtensionContext`. - -When multiple `ExecutionCondition` extensions are registered, a container or test is -disabled as soon as one of the conditions returns _disabled_. Thus, there is no guarantee -that a condition is evaluated because another extension might have already caused a -container or test to be disabled. In other words, the evaluation works like the -short-circuiting boolean OR operator. - -See the source code of `{DisabledCondition}` and `{Disabled}` for concrete examples. - -[[extensions-conditions-deactivation]] -==== Deactivating Conditions - -Sometimes it can be useful to run a test suite _without_ certain conditions being active. -For example, you may wish to run tests even if they are annotated with `@Disabled` in -order to see if they are still _broken_. To do this, provide a pattern for the -`junit.jupiter.conditions.deactivate` _configuration parameter_ to specify which -conditions should be deactivated (i.e., not evaluated) for the current test run. The -pattern can be supplied as a JVM system property, as a _configuration parameter_ in the -`LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform -configuration file (see <> for details). - -For example, to deactivate JUnit's `@Disabled` condition, you can start your JVM with the -following system property. - -`-Djunit.jupiter.conditions.deactivate=org.junit.*DisabledCondition` - -[[extensions-conditions-deactivation-patterns]] -===== Pattern Matching Syntax - -Refer to <> for details. - -[[extensions-test-instance-pre-construct-callback]] -=== Test Instance Pre-construct Callback - -`{TestInstancePreConstructCallback}` defines the API for `Extensions` that wish to be invoked -_prior_ to test instances being constructed (by a constructor call or via -`{TestInstanceFactory}`). - -This extension provides a symmetric call to `{TestInstancePreDestroyCallback}` and is useful -in combination with other extensions to prepare constructor parameters or keeping track of test -instances and their lifecycle. - -[NOTE] -.Accessing the test-scoped `ExtensionContext` -==== -You may override the `getTestInstantiationExtensionContextScope(...)` method to return -`TEST_METHOD` to make test-specific data available to your extension implementation or if -you want to <> on the test method level. -==== - -[[extensions-test-instance-factories]] -=== Test Instance Factories - -`{TestInstanceFactory}` defines the API for `Extensions` that wish to _create_ test class -instances. - -Common use cases include acquiring the test instance from a dependency injection -framework or invoking a static factory method to create the test class instance. - -If no `TestInstanceFactory` is registered, the framework will invoke the _sole_ -constructor for the test class to instantiate it, potentially resolving constructor -arguments via registered `ParameterResolver` extensions. - -Extensions that implement `TestInstanceFactory` can be registered on test interfaces, -top-level test classes, or `@Nested` test classes. - -[WARNING] -==== -Registering multiple extensions that implement `TestInstanceFactory` for any single class -will result in an exception being thrown for all tests in that class, in any subclass, -and in any nested class. Note that any `TestInstanceFactory` registered in a superclass -or _enclosing_ class (i.e., in the case of a `@Nested` test class) is _inherited_. It is -the user's responsibility to ensure that only a single `TestInstanceFactory` is -registered for any specific test class. -==== - -[NOTE] -.Accessing the test-scoped `ExtensionContext` -==== -You may override the `getTestInstantiationExtensionContextScope(...)` method to return -`TEST_METHOD` to make test-specific data available to your extension implementation or if -you want to <> on the test method level. -==== - -[[extensions-test-instance-post-processing]] -=== Test Instance Post-processing - -`{TestInstancePostProcessor}` defines the API for `Extensions` that wish to _post -process_ test instances. - -Common use cases include injecting dependencies into the test instance, invoking custom -initialization methods on the test instance, etc. - -For a concrete example, consult the source code for the `{MockitoExtension}` and the -`{SpringExtension}`. - -[NOTE] -.Accessing the test-scoped `ExtensionContext` -==== -You may override the `getTestInstantiationExtensionContextScope(...)` method to return -`TEST_METHOD` to make test-specific data available to your extension implementation or if -you want to <> on the test method level. -==== - -[[extensions-test-instance-pre-destroy-callback]] -=== Test Instance Pre-destroy Callback - -`{TestInstancePreDestroyCallback}` defines the API for `Extensions` that wish to process -test instances _after_ they have been used in tests and _before_ they are destroyed. - -Common use cases include cleaning dependencies that have been injected into the -test instance, invoking custom de-initialization methods on the test instance, etc. - -[[extensions-parameter-resolution]] -=== Parameter Resolution - -`{ParameterResolver}` defines the `Extension` API for dynamically resolving parameters at -runtime. - -If a _test class_ constructor, _test method_, or _lifecycle method_ (see -<>) declares a parameter, the parameter must be _resolved_ at -runtime by a `ParameterResolver`. A `ParameterResolver` can either be built-in (see -`{TestInfoParameterResolver}`) or <>. -Generally speaking, parameters may be resolved by _name_, _type_, _annotation_, or any -combination thereof. - -If you wish to implement a custom `{ParameterResolver}` that resolves parameters based -solely on the type of the parameter, you may find it convenient to extend the -`{TypeBasedParameterResolver}` which serves as a generic adapter for such use cases. - -For concrete examples, consult the source code for `{CustomTypeParameterResolver}`, -`{CustomAnnotationParameterResolver}`, and `{MapOfListsTypeBasedParameterResolver}`. - -[WARNING] -==== -Due to a bug in the byte code generated by `javac` on JDK versions prior to JDK 9, -looking up annotations on parameters directly via the core `java.lang.reflect.Parameter` -API will always fail for _inner class_ constructors (e.g., a constructor in a `@Nested` -test class). - -The `{ParameterContext}` API supplied to `ParameterResolver` implementations therefore -includes the following convenience methods for correctly looking up annotations on -parameters. Extension authors are strongly encouraged to use these methods instead of -those provided in `java.lang.reflect.Parameter` in order to avoid this bug in the JDK. - -* `boolean isAnnotated(Class annotationType)` -* `Optional findAnnotation(Class annotationType)` -* `List findRepeatableAnnotations(Class annotationType)` -==== - -[NOTE] -.Accessing the test-scoped `ExtensionContext` -==== -You may override the `getTestInstantiationExtensionContextScope(...)` method to return -`TEST_METHOD` to support injecting test specific data into constructor parameters of the -test class instance. Doing so causes a test-specific `{ExtensionContext}` to be used while -resolving constructor parameters, unless the -<> is set to `PER_CLASS`. -==== - -[TIP] -.Parameter resolution for methods called from extensions -==== -Other extensions can also leverage registered `ParameterResolvers` for method and -constructor invocations, using the `{ExecutableInvoker}` available via the -`getExecutableInvoker()` method in the `ExtensionContext`. -==== - -[[extensions-parameter-resolution-conflicts]] -==== Parameter Conflicts - -If multiple implementations of `ParameterResolver` that support the same type are -registered for a test, a `ParameterResolutionException` will be thrown, with a -message to indicate that competing resolvers have been discovered. See the following -example: - -[source,java,indent=0] -.Conflicting parameter resolution due to multiple resolvers claiming support for integers ----- -include::{testDir}/example/extensions/ParameterResolverConflictDemo.java[tags=user_guide] ----- - -If the conflicting `ParameterResolver` implementations are applied to different test -methods as shown in the following example, no conflict occurs. - -[source,java,indent=0] -.Fine-grained registration to avoid conflict ----- -include::{testDir}/example/extensions/ParameterResolverNoConflictDemo.java[tags=user_guide] ----- - -If the conflicting `ParameterResolver` implementations need to be applied to the same test -method, you can implement a custom type or custom annotation as illustrated by -`{CustomTypeParameterResolver}` and `{CustomAnnotationParameterResolver}`, respectively. - -[source,java,indent=0] -.Custom type to resolve duplicate types ----- -include::{testDir}/example/extensions/ParameterResolverCustomTypeDemo.java[tags=user_guide] ----- - -A custom annotation makes the duplicate type distinguishable from its counterpart: - -[source,java,indent=0] -.Custom annotation to resolve duplicate types ----- -include::{testDir}/example/extensions/ParameterResolverCustomAnnotationDemo.java[tags=user_guide] ----- - -JUnit includes some built-in parameter resolvers that can cause conflicts if a resolver -attempts to claim their supported types. For example, `{TestInfo}` provides metadata about -tests. See <> for details. Third-party frameworks such -as Spring may also define parameter resolvers. Apply one of the techniques in this section -to resolve any conflicts. - -Parameterized tests are another potential source of conflict. Ensure that tests annotated -with `@ParameterizedTest` are not also annotated with `@Test` and see -<> for more details. - -[[extensions-test-result-processing]] -=== Test Result Processing - -`{TestWatcher}` defines the API for extensions that wish to process the results of _test -method_ executions. Specifically, a `TestWatcher` will be invoked with contextual -information for the following events. - -* `testDisabled`: invoked after a disabled _test method_ has been skipped -* `testSuccessful`: invoked after a _test method_ has completed successfully -* `testAborted`: invoked after a _test method_ has been aborted -* `testFailed`: invoked after a _test method_ has failed - -NOTE: In contrast to the definition of "test method" presented in -<>, in this context _test method_ refers to any `@Test` method -or `@TestTemplate` method (for example, a `@RepeatedTest` or `@ParameterizedTest`). - -Extensions implementing this interface can be registered at the class level, instance -level, or method level. When registered at the class level, a `TestWatcher` will be -invoked for any contained _test method_ including those in `@Nested` classes. When -registered at the method level, a `TestWatcher` will only be invoked for the _test method_ -for which it was registered. - -[WARNING] -==== -If a `TestWatcher` is registered via a non-static (instance) field – for example, using -`@RegisterExtension` – and the test class is configured with -`@TestInstance(Lifecycle.PER_METHOD)` semantics (which is the default lifecycle mode), the -`TestWatcher` will **not** be invoked with events for `@TestTemplate` methods (for -example, `@RepeatedTest` or `@ParameterizedTest`). - -To ensure that a `TestWatcher` is invoked for all _test methods_ in a given class, it is -therefore recommended that the `TestWatcher` be registered at the class level with -`@ExtendWith` or via a `static` field with `@RegisterExtension` or `@ExtendWith`. -==== - -If there is a failure at the class level — for example, an exception thrown by a -`@BeforeAll` method — no test results will be reported. Similarly, if the test class is -disabled via an `ExecutionCondition` — for example, `@Disabled` — no test results will be -reported. - -In contrast to other Extension APIs, a `TestWatcher` is not permitted to adversely -influence the execution of tests. Consequently, any exception thrown by a method in the -`TestWatcher` API will be logged at `WARNING` level and will not be allowed to propagate -or fail test execution. - -[WARNING] -==== -Any instances of `ExtensionContext.Store.CloseableResource` stored in the `Store` of the -provided `{ExtensionContext}` will be closed _before_ methods in the `TestWatcher` API are -invoked (see <>). You can use the parent context's `Store` to -work with such resources. -==== - -[[extensions-lifecycle-callbacks]] -=== Test Lifecycle Callbacks - -The following interfaces define the APIs for extending tests at various points in the -test execution lifecycle. Consult the following sections for examples and the Javadoc for -each of these interfaces in the `{extension-api-package}` package for further details. - -* `{BeforeAllCallback}` -** `{BeforeClassTemplateInvocationCallback}` (only applicable for - <>) -*** `{BeforeEachCallback}` -**** `{BeforeTestExecutionCallback}` -**** `{AfterTestExecutionCallback}` -*** `{AfterEachCallback}` -** `{AfterClassTemplateInvocationCallback}` (only applicable for - <>) -* `{AfterAllCallback}` - -.Implementing Multiple Extension APIs -NOTE: Extension developers may choose to implement any number of these interfaces -within a single extension. Consult the source code of the `{SpringExtension}` for a -concrete example. - -[[extensions-lifecycle-callbacks-before-after-execution]] -==== Before and After Test Execution Callbacks - -`{BeforeTestExecutionCallback}` and `{AfterTestExecutionCallback}` define the APIs for -`Extensions` that wish to add behavior that will be executed _immediately before_ and -_immediately after_ a test method is executed, respectively. As such, these callbacks are -well suited for timing, tracing, and similar use cases. If you need to implement -callbacks that are invoked _around_ `@BeforeEach` and `@AfterEach` methods, implement -`BeforeEachCallback` and `AfterEachCallback` instead. - -The following example shows how to use these callbacks to calculate and log the execution -time of a test method. `TimingExtension` implements both `BeforeTestExecutionCallback` -and `AfterTestExecutionCallback` in order to time and log the test execution. - -[[extensions-lifecycle-callbacks-timing-extension]] -[source,java,indent=0] -.An extension that times and logs the execution of test methods ----- -include::{testDir}/example/timing/TimingExtension.java[tags=user_guide] ----- - -Since the `TimingExtensionTests` class registers the `TimingExtension` via `@ExtendWith`, -its tests will have this timing applied when they execute. - -[source,java,indent=0] -.A test class that uses the example TimingExtension ----- -include::{testDir}/example/timing/TimingExtensionTests.java[tags=user_guide] ----- - -The following is an example of the logging produced when `TimingExtensionTests` is run. - -.... -INFO: Method [sleep20ms] took 24 ms. -INFO: Method [sleep50ms] took 53 ms. -.... - -[[extensions-exception-handling]] -=== Exception Handling - -Exceptions thrown during the test execution may be intercepted and handled accordingly -before propagating further, so that certain actions like error logging or resource releasing -may be defined in specialized `Extensions`. JUnit Jupiter offers API for `Extensions` that -wish to handle exceptions thrown during `@Test` methods via `{TestExecutionExceptionHandler}` -and for those thrown during one of test lifecycle methods (`@BeforeAll`, `@BeforeEach`, -`@AfterEach` and `@AfterAll`) via `{LifecycleMethodExecutionExceptionHandler}`. - -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 ----- -include::{testDir}/example/exception/IgnoreIOExceptionExtension.java[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 -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 ----- -include::{testDir}/example/exception/RecordStateOnErrorExtension.java[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 -exception was never thrown. Handlers may also choose to rethrow the exception or throw -a different one, potentially wrapping the original. - -Extensions implementing `{LifecycleMethodExecutionExceptionHandler}` that wish to handle -exceptions thrown during `@BeforeAll` or `@AfterAll` need to be registered on a class level, -while handlers for `BeforeEach` and `AfterEach` may be also registered for individual -test methods. - -[source,java,indent=0] -.Registering multiple exception handling extensions ----- -include::{testDir}/example/exception/MultipleHandlersTestCase.java[tags=user_guide] ----- - -[[extensions-preinterrupt-callback]] -=== Pre-Interrupt Callback - -`{PreInterruptCallback}` defines the API for `Extensions` that wish to react on -timeouts before the `Thread.interrupt()` is called. - -Please refer to <> for additional information. - - -[[extensions-intercepting-invocations]] -=== Intercepting Invocations - -`{InvocationInterceptor}` defines the API for `Extensions` that wish to intercept calls to -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 ----- -include::{testDir}/example/interceptor/SwingEdtInterceptor.java[tags=user_guide] ----- - -[NOTE] -.Accessing the test-scoped `ExtensionContext` -==== -You may override the `getTestInstantiationExtensionContextScope(...)` method to return -`TEST_METHOD` to make test-specific data available to your extension implementation of -`interceptTestClassConstructor` or if you want to <> -on the test method level. -==== - -[[extensions-class-templates]] -=== Providing Invocation Contexts for Class Templates - -A `{ClassTemplate}` class can only be executed when at least one -`{ClassTemplateInvocationContextProvider}` is registered. Each such provider is -responsible for providing a `Stream` of `{ClassTemplateInvocationContext}` instances. -Each context may specify a custom display name and a list of additional extensions that -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}`. - -[source,java,indent=0] -.A class template with accompanying extension ----- -include::{testDir}/example/ClassTemplateDemo.java[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 -`apple` and `banana` as specified by the invocation context. Each invocation registers a -custom `{TestInstancePostProcessor}` which is used to inject a value into a field. The -output when using the `ConsoleLauncher` is as follows. - -.... -└─ ClassTemplateDemo ✔ - ├─ apple ✔ - │ ├─ notNull() ✔ - │ └─ wellKnown() ✔ - └─ banana ✔ - ├─ notNull() ✔ - └─ wellKnown() ✔ -.... - -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 -parameters, by preparing the test class instance differently, or multiple times without -modifying the context. -Please refer to the implementations of -<> which uses this extension -point to provide its functionality. - -[[extensions-test-templates]] -=== Providing Invocation Contexts for Test Templates - -A `{TestTemplate}` method can only be executed when at least one -`{TestTemplateInvocationContextProvider}` is registered. Each such provider is responsible -for providing a `Stream` of `{TestTemplateInvocationContext}` instances. Each context may -specify a custom display name and a list of additional extensions that will only be used -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}`. - -[source,java,indent=0] -.A test template with accompanying extension ----- -include::{testDir}/example/TestTemplateDemo.java[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 -invocation registers a custom `{ParameterResolver}` which is used to resolve the method -parameter. The output when using the `ConsoleLauncher` is as follows. - -.... -└─ testTemplate(String) ✔ - ├─ apple ✔ - └─ banana ✔ -.... - -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 -the test class instance differently, or multiple times without modifying the context. -Please refer to the implementations of <> or -<> which use this extension point -to provide their functionality. - -[[extensions-keeping-state]] -=== Keeping State in Extensions - -Usually, an extension is instantiated only once. So the question becomes relevant: How do -you keep the state from one invocation of an extension to the next? The -`{ExtensionContext}` API provides a `{ExtensionContext_Store}` exactly for this purpose. -Extensions may put values into a store for later retrieval. - -TIP: See the `<>` for an -example of using the `Store` with a method-level scope. - -.The `ExtensionContext` and `Store` hierarchy -image::extensions_StoreHierarchy.svg[alt=UML diagram,role=text-center] - -As illustrated by the diagram above, stores are hierarchical in nature. When looking up a -value, if no value is stored in the current `ExtensionContext` for the supplied key, the -stores of the context's ancestors will be queried for a value with the same key in the -`Namespace` used to create this store. The root `ExtensionContext` represents the engine -level so its `Store` may be used to store or cache values that are used by multiple test -classes or extension. The `{ExtensionContext_StoreScope}` enum allows to go beyond even -that and access the stores on the level of the current `{LauncherExecutionRequest}` or -`{LauncherSession}` which can be used to share data across test engines or inject data -from a registered -<>, -respectively. Please consult the Javadoc of `{ExtensionContext}`, -`{ExtensionContext_Store}`, and `{ExtensionContext_StoreScope}` for details. - -[[extensions-keeping-state-autocloseable-support]] -[NOTE] -.Resource management via `_AutoCloseable_` -==== -An extension context store is bound to its extension context lifecycle. When an extension -context lifecycle ends it closes its associated store. - -All stored values that are instances of `AutoCloseable` are notified by an invocation of -their `close()` method in the inverse order they were added in (unless the -`junit.jupiter.extensions.store.close.autocloseable.enabled` -<> is set to `false`). - -Versions prior to 5.13 only supported `CloseableResource`, which is deprecated but still -available for backward compatibility. -==== - -An example implementation of `AutoCloseable` is shown below, using an `HttpServer` -resource. - -[source,java,indent=0] -.`_HttpServer_` resource implementing `_AutoCloseable_` ----- -include::{testDir}/example/extensions/HttpServerResource.java[tags=user_guide] ----- - -This resource can then be stored in the desired `ExtensionContext`. It may be stored at -class or method level, if desired, but this may add unnecessary overhead for this type of -resource. For this example it might be prudent to store it at root level and instantiate -it lazily to ensure it's only created once per test run and reused across different test -classes and methods. - -[source,java,indent=0] -.Lazily storing in root context with `_Store.computeIfAbsent_` ----- -include::{testDir}/example/extensions/HttpServerExtension.java[tags=user_guide] ----- - -[source,java,indent=0] -.A test case using the `_HttpServerExtension_` ----- -include::{testDir}/example/HttpServerDemo.java[tags=user_guide] ----- - -[[extensions-keeping-state-autocloseable-migration]] -[TIP] -.Migration Note for Resource Cleanup -==== -The framework automatically closes resources stored in the `ExtensionContext.Store` that -implement `AutoCloseable`. In versions prior to 5.13, only resources implementing -`Store.CloseableResource` were automatically closed. - -If you're developing an extension that needs to support both JUnit Jupiter 5.13+ and -earlier versions and your extension stores resources that need to be cleaned up, you -should implement both interfaces: - -[source,java,indent=0] ----- -public class MyResource implements Store.CloseableResource, AutoCloseable { - @Override - public void close() throws Exception { - // Resource cleanup code - } -} ----- - -This ensures that your resource will be properly closed regardless of which JUnit Jupiter -version is being used. -==== - -[[extensions-supported-utilities]] -=== Supported Utilities in Extensions - -The `junit-platform-commons` artifact provides _maintained_ utilities for working with -annotations, classes, reflection, classpath scanning, and conversion tasks. These -utilities can be found in the `{junit-platform-support-package}` and its subpackages. -`TestEngine` and `Extension` authors are encouraged to use these supported utilities in -order to align with the behavior of the JUnit Platform and JUnit Jupiter. - -[[extensions-supported-utilities-annotations]] -==== Annotation Support - -`AnnotationSupport` provides static utility methods that operate on annotated elements -(e.g., packages, annotations, classes, interfaces, constructors, methods, and fields). -These include methods to check whether an element is annotated or meta-annotated with a -particular annotation, to search for specific annotations, and to find annotated methods -and fields in a class or interface. Some of these methods search on implemented -interfaces and within class hierarchies to find annotations. Consult the Javadoc for -`{AnnotationSupport}` for further details. - -NOTE: The `isAnnotated()` methods do not find repeatable annotations. To check for repeatable annotations, -use one of the `findRepeatableAnnotations()` methods and verify that the returned list is not empty. - -NOTE: See also: <> - -[[extensions-supported-utilities-classes]] -==== Class Support - -`ClassSupport` provides static utility methods for working with classes (i.e., instances -of `java.lang.Class`). Consult the Javadoc for `{ClassSupport}` for further details. - -[[extensions-supported-utilities-reflection]] -==== Reflection Support - -`ReflectionSupport` provides static utility methods that augment the standard JDK -reflection and class-loading mechanisms. These include methods to scan the classpath in -search of classes matching specified predicates, to load and create new instances of a -class, and to find and invoke methods. Some of these methods traverse class hierarchies -to locate matching methods. Consult the Javadoc for `{ReflectionSupport}` for further -details. - -NOTE: See also: <> - -[[extensions-supported-utilities-modifier]] -==== Modifier Support - -`ModifierSupport` provides static utility methods for working with member and class -modifiers -- for example, to determine if a member is declared as `public`, `private`, -`abstract`, `static`, etc. Consult the Javadoc for `{ModifierSupport}` for further -details. - -[[extensions-supported-utilities-conversion]] -==== Conversion Support - -`ConversionSupport` (in the `org.junit.platform.commons.support.conversion` package) -provides support for converting from strings to primitive types and their corresponding -wrapper types, date and time types from the `java.time package`, and some additional -common Java types such as `File`, `BigDecimal`, `BigInteger`, `Currency`, `Locale`, `URI`, -`URL`, `UUID`, etc. Consult the Javadoc for `{ConversionSupport}` for further details. - -[[extensions-supported-utilities-search-semantics]] -==== Field and Method Search Semantics - -Various methods in `AnnotationSupport` and `ReflectionSupport` use search algorithms that -traverse type hierarchies to locate matching fields and methods – for example, -`AnnotationSupport.findAnnotatedFields(...)`, `ReflectionSupport.findMethods(...)`, etc. - -The field and method search algorithms adhere to standard Java semantics regarding whether -a given field or method is visible or overridden according to the rules of the Java -language. - -[[extensions-execution-order]] -=== Relative Execution Order of User Code and Extensions - -When executing a test class that contains one or more test methods, a number of extension -callbacks are called in addition to the user-supplied test and lifecycle methods. - -NOTE: See also: <> - -[[extensions-execution-order-overview]] -==== User and Extension Code - -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 -test method and will be repeated for every test method in the test class. - -[[extensions-execution-order-diagram]] -.User code and extension code -image::extensions_lifecycle.png[] - -The following table further explains the sixteen steps in the -<> diagram. - -. *interface* `*org.junit.jupiter.api.extension.BeforeAllCallback*` + -extension code executed before all tests of the container are executed -. *annotation* `*org.junit.jupiter.api.BeforeAll*` + -user code executed before all tests of the container are executed -. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler -#handleBeforeAllMethodExecutionException*` + -extension code for handling exceptions thrown from `@BeforeAll` methods -. *interface* `*org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback*` + -extension code executed before each class template invocation is executed (only applicable -if the test class is a <>) -. *interface* `*org.junit.jupiter.api.extension.BeforeEachCallback*` + -extension code executed before each test is executed -. *annotation* `*org.junit.jupiter.api.BeforeEach*` + -user code executed before each test is executed -. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler -#handleBeforeEachMethodExecutionException*` + -extension code for handling exceptions thrown from `@BeforeEach` methods -. *interface* `*org.junit.jupiter.api.extension.BeforeTestExecutionCallback*` + -extension code executed immediately before a test is executed -. *annotation* `*org.junit.jupiter.api.Test*` + -user code of the actual test method -. *interface* `*org.junit.jupiter.api.extension.TestExecutionExceptionHandler*` + -extension code for handling exceptions thrown during a test -. *interface* `*org.junit.jupiter.api.extension.AfterTestExecutionCallback*` + -extension code executed immediately after test execution and its corresponding exception handlers -. *annotation* `*org.junit.jupiter.api.AfterEach*` + -user code executed after each test is executed -. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler -#handleAfterEachMethodExecutionException*` + -extension code for handling exceptions thrown from `@AfterEach` methods -. *interface* `*org.junit.jupiter.api.extension.AfterEachCallback*` + -extension code executed after each test is executed -. *interface* `*org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback*` + -extension code executed after each class template invocation is executed (only applicable -if the test class is a <>) -. *annotation* `*org.junit.jupiter.api.AfterAll*` + -user code executed after all tests of the container are executed -. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler -#handleAfterAllMethodExecutionException*` + -extension code for handling exceptions thrown from `@AfterAll` methods -. *interface* `*org.junit.jupiter.api.extension.AfterAllCallback*` + -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 -please consult the respective Javadoc for each annotation and extension. - -All invocations of user code methods in the above table can additionally be intercepted -by implementing <>. - -[[extensions-execution-order-wrapping-behavior]] -==== Wrapping Behavior of Callbacks - -JUnit Jupiter always guarantees _wrapping_ behavior for multiple registered extensions -that implement lifecycle callbacks such as `BeforeAllCallback`, `AfterAllCallback`, -`BeforeClassTemplateInvocationCallback`, `AfterClassTemplateInvocationCallback`, -`BeforeEachCallback`, `AfterEachCallback`, `BeforeTestExecutionCallback`, and -`AfterTestExecutionCallback`. - -That means that, given two extensions `Extension1` and `Extension2` with `Extension1` -registered before `Extension2`, any "before" callbacks implemented by `Extension1` are -guaranteed to execute **before** any "before" callbacks implemented by `Extension2`. -Similarly, given the two same two extensions registered in the same order, any "after" -callbacks implemented by `Extension1` are guaranteed to execute **after** any "after" -callbacks implemented by `Extension2`. `Extension1` is therefore said to _wrap_ -`Extension2`. - -JUnit Jupiter also guarantees _wrapping_ behavior within class and interface hierarchies -for user-supplied _lifecycle methods_ (see <>). - -* `@BeforeAll` methods are inherited from superclasses as long as they are not - _overridden_. Furthermore, `@BeforeAll` methods from superclasses will be executed - **before** `@BeforeAll` methods in subclasses. -** Similarly, `@BeforeAll` methods declared in an interface are inherited as long as they - are not _overridden_, and `@BeforeAll` methods from an interface will be executed - **before** `@BeforeAll` methods in the class that implements the interface. -* `@AfterAll` methods are inherited from superclasses as long as they are not - _overridden_. Furthermore, `@AfterAll` methods from superclasses will be executed - **after** `@AfterAll` methods in subclasses. -** Similarly, `@AfterAll` methods declared in an interface are inherited as long as they - are not _overridden_, and `@AfterAll` methods from an interface will be executed - **after** `@AfterAll` methods in the class that implements the interface. -* `@BeforeEach` methods are inherited from superclasses as long as they are not - _overridden_. Furthermore, `@BeforeEach` methods from superclasses will be executed - **before** `@BeforeEach` methods in subclasses. -** Similarly, `@BeforeEach` methods declared as interface default methods are inherited as - long as they are not _overridden_, and `@BeforeEach` default methods will be executed - **before** `@BeforeEach` methods in the class that implements the interface. -* `@AfterEach` methods are inherited from superclasses as long as they are not - _overridden_. Furthermore, `@AfterEach` methods from superclasses will be executed - **after** `@AfterEach` methods in subclasses. -** Similarly, `@AfterEach` methods declared as interface default methods are inherited as - long as they are not _overridden_, and `@AfterEach` default methods will be executed - **after** `@AfterEach` methods in the class that implements the interface. - -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 -user-supplied callback methods and callback methods in extensions. - -[source,java,indent=0] -.Extension1 ----- -include::{testDir}/example/callbacks/Extension1.java[tags=user_guide] ----- - -[source,java,indent=0] -.Extension2 ----- -include::{testDir}/example/callbacks/Extension2.java[tags=user_guide] ----- - -[source,java,indent=0] -.AbstractDatabaseTests ----- -include::{testDir}/example/callbacks/AbstractDatabaseTests.java[tags=user_guide] ----- - -[source,java,indent=0] -.DatabaseTestsDemo ----- -include::{testDir}/example/callbacks/DatabaseTestsDemo.java[tags=user_guide] ----- - -When the `DatabaseTestsDemo` test class is executed, the following is logged. - ----- -@BeforeAll AbstractDatabaseTests.createDatabase() -@BeforeAll DatabaseTestsDemo.beforeAll() - Extension1.beforeEach() - Extension2.beforeEach() - @BeforeEach AbstractDatabaseTests.connectToDatabase() - @BeforeEach DatabaseTestsDemo.insertTestDataIntoDatabase() - @Test DatabaseTestsDemo.testDatabaseFunctionality() - @AfterEach DatabaseTestsDemo.deleteTestDataFromDatabase() - @AfterEach AbstractDatabaseTests.disconnectFromDatabase() - Extension2.afterEach() - Extension1.afterEach() -@BeforeAll DatabaseTestsDemo.afterAll() -@AfterAll AbstractDatabaseTests.destroyDatabase() ----- - -The following sequence diagram helps to shed further light on what actually goes on within -the `JupiterTestEngine` when the `DatabaseTestsDemo` test class is executed. - -//// -PNG generated using ZenUML: https://app.zenuml.com - -See corresponding *.txt file in images folder for the source. -//// -image::extensions_DatabaseTestsDemo.png[caption='',title='DatabaseTestsDemo'] - -JUnit Jupiter does **not** guarantee the execution order of multiple lifecycle methods -that are declared within a _single_ test class or test interface. It may at times appear -that JUnit Jupiter invokes such methods in alphabetical order. However, that is not -precisely true. The ordering is analogous to the ordering for `@Test` methods within a -single test class. - -[NOTE] -==== -Lifecycle methods that are declared within a _single_ test class or test interface will be -ordered using an algorithm that is deterministic but intentionally non-obvious. This -ensures that subsequent runs of a test suite execute lifecycle methods in the same order, -thereby allowing for repeatable builds. -==== - -In addition, JUnit Jupiter does **not** support _wrapping_ behavior for multiple lifecycle -methods declared within a single test class or test interface. - -The following example demonstrates this behavior. Specifically, the lifecycle method -configuration is _broken_ due to the order in which the locally declared lifecycle methods -are executed. - -* Test data is inserted _before_ the database connection has been opened, which results in - a failure to connect to the database. -* 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 ----- -include::{testDir}/example/callbacks/BrokenLifecycleMethodConfigDemo.java[tags=user_guide] ----- - -When the `BrokenLifecycleMethodConfigDemo` test class is executed, the following is logged. - ----- -Extension1.beforeEach() -Extension2.beforeEach() - @BeforeEach BrokenLifecycleMethodConfigDemo.insertTestDataIntoDatabase() - @BeforeEach BrokenLifecycleMethodConfigDemo.connectToDatabase() - @Test BrokenLifecycleMethodConfigDemo.testDatabaseFunctionality() - @AfterEach BrokenLifecycleMethodConfigDemo.disconnectFromDatabase() - @AfterEach BrokenLifecycleMethodConfigDemo.deleteTestDataFromDatabase() -Extension2.afterEach() -Extension1.afterEach() ----- - -The following sequence diagram helps to shed further light on what actually goes on within -the `JupiterTestEngine` when the `BrokenLifecycleMethodConfigDemo` test class is executed. - -//// -PNG generated using ZenUML: https://app.zenuml.com - -See corresponding *.txt file in images folder for the source. -//// -image::extensions_BrokenLifecycleMethodConfigDemo.png[caption='',title='BrokenLifecycleMethodConfigDemo'] - -[TIP] -==== -Due to the aforementioned behavior, the JUnit Team recommends that developers declare at -most one of each type of _lifecycle method_ (see <>) per test -class or test interface unless there are no dependencies between such lifecycle methods. -==== diff --git a/documentation/src/docs/asciidoc/user-guide/index.adoc b/documentation/src/docs/asciidoc/user-guide/index.adoc deleted file mode 100644 index cd35b456e2ee..000000000000 --- a/documentation/src/docs/asciidoc/user-guide/index.adoc +++ /dev/null @@ -1,46 +0,0 @@ -[[user-guide]] -= JUnit User Guide -Stefan Bechtold; Sam Brannen; Johannes Link; Matthias Merdes; Marc Philipp; Juliette de Rancourt; Christian Stein -:basedir: {includedir}/user-guide -:pdf-fontsdir: GEM_FONTS_DIR;{includedir}/resources/fonts -:pdf-theme: {includedir}/resources/themes/junit-pdf-theme.yml -:imagesdir: images -:imagesoutdir: {outdir}/user-guide/images -// -:docinfodir: {includedir}/docinfos -:docinfo2: -// -ifdef::backend-pdf[:imagesdir: {imagesoutdir}] -// -// Blank lines are not permitted in the doc-header: https://asciidoctor.org/docs/user-manual/#doc-header -// -:sectnums: -:toclevels: 4 -:last-update-label!: -:figure-caption!: -// - -include::{includedir}/link-attributes.adoc[] - -include::{basedir}/overview.adoc[] - -include::{basedir}/writing-tests.adoc[] - -include::{basedir}/migration-from-junit4.adoc[] - -include::{basedir}/running-tests.adoc[] - -include::{basedir}/extensions.adoc[] - -include::{basedir}/advanced-topics.adoc[] - -include::{basedir}/api-evolution.adoc[] - -include::{basedir}/contributors.adoc[] - -[[release-notes]] -== Release Notes - -The release notes are available link:{releaseNotesUrl}[here]. - -include::{basedir}/appendix.adoc[] diff --git a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc deleted file mode 100644 index 3099a146c2b4..000000000000 --- a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc +++ /dev/null @@ -1,1216 +0,0 @@ -[[running-tests]] -== Running Tests - -[[running-tests-ide]] -=== IDE Support - -[[running-tests-ide-intellij-idea]] -==== IntelliJ IDEA - -IntelliJ IDEA supports running tests on the JUnit Platform since version 2016.2. For more -information, please consult this https://jb.gg/junit-idea/[IntelliJ IDEA resource]. Note, -however, that it is recommended to use IDEA 2017.3 or newer since more recent versions of -IDEA download the following JARs automatically based on the API version used in the -project: `junit-platform-launcher`, `junit-jupiter-engine`, and `junit-vintage-engine`. - -In order to use a different JUnit version (e.g., {version}), you may need to -include the corresponding versions of the `junit-platform-launcher`, -`junit-jupiter-engine`, and `junit-vintage-engine` JARs in the classpath. - -.Additional Gradle Dependencies -[source,groovy] -[subs=attributes+] ----- -testImplementation(platform("org.junit:junit-bom:{version}")) -testRuntimeOnly("org.junit.platform:junit-platform-launcher") -testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") -testRuntimeOnly("org.junit.vintage:junit-vintage-engine") ----- - -.Additional Maven Dependencies -[source,xml] -[subs=attributes+] ----- - - - - org.junit.platform - junit-platform-launcher - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.vintage - junit-vintage-engine - test - - - - - - org.junit - junit-bom - {version} - pom - import - - - ----- - -[[running-tests-ide-eclipse]] -==== Eclipse - -Eclipse IDE offers support for the JUnit Platform since the Eclipse Oxygen.1a (4.7.1a) -release. - -For more information on using JUnit Platform in Eclipse consult the official _Eclipse -support for JUnit 5_ section of the -https://www.eclipse.org/eclipse/news/4.7.1a/#junit-5-support[Eclipse Project Oxygen.1a -(4.7.1a) - New and Noteworthy] documentation. - -[[running-tests-ide-netbeans]] -==== NetBeans - -NetBeans offers support for JUnit Jupiter and the JUnit Platform since the -https://netbeans.apache.org/download/nb100/nb100.html[Apache NetBeans 10.0 release]. - -For more information consult the JUnit 5 section of the -https://netbeans.apache.org/download/nb100/index.html#_junit_5[Apache NetBeans 10.0 -release notes]. - -[[running-tests-ide-vscode]] -==== Visual Studio Code - -https://code.visualstudio.com/[Visual Studio Code] supports JUnit Jupiter and the JUnit -Platform via the -https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-test[Java Test -Runner] extension which is installed by default as part of the -https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack[Java -Extension Pack]. - -For more information consult the _Testing_ section of the -https://code.visualstudio.com/docs/languages/java#_testing[Java in Visual Studio Code] -documentation. - -[[running-tests-ide-other]] -==== Other IDEs - -If you are using an editor or IDE other than one of those listed in the previous sections, -and it doesn't support running tests on the JUnit Platform, you can use the -<> to run them from the command line. - -[[running-tests-build]] -=== Build Support - -[[running-tests-build-gradle]] -==== Gradle - -Starting with https://docs.gradle.org/4.6/release-notes.html[version 4.6], Gradle provides -https://docs.gradle.org/current/userguide/java_testing.html#using_junit5[native support] -for executing tests on the JUnit Platform. To enable it, you need to specify -`useJUnitPlatform()` within a `test` task declaration in `build.gradle`: - -[source,groovy,indent=0] -[subs=attributes+] ----- -test { - useJUnitPlatform() -} ----- - -Filtering by <>, -<>, or engines is also supported: - -[source,groovy,indent=0] -[subs=attributes+] ----- -test { - useJUnitPlatform { - includeTags("fast", "smoke & feature-a") - // excludeTags("slow", "ci") - includeEngines("junit-jupiter") - // excludeEngines("junit-vintage") - } -} ----- - -Please refer to the -https://docs.gradle.org/current/userguide/java_testing.html[official Gradle documentation] -for a comprehensive list of options. - -[[running-tests-build-gradle-bom]] -===== Aligning dependency versions - -TIP: See <> for details on how to override the version -of JUnit used in your Spring Boot application. - -Unless you're using Spring Boot which defines its own way of managing dependencies, it is -recommended to use the JUnit Platform <> to align the -versions of all JUnit artifacts. - -[source,groovy,indent=0] -[subs=attributes+] -.Explicit platform dependency on the BOM ----- -dependencies { - testImplementation(platform("org.junit:junit-bom:{version}")) - testImplementation("org.junit.jupiter:junit-jupiter") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} ----- - -Using the BOM allows you to omit the version when declaring dependencies on all artifacts -with the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs. - -Since all JUnit artifacts declare a -https://docs.gradle.org/current/userguide/platforms.html[platform] dependency on the BOM, -you usually don't need to declare an explicit dependency on it yourself. Instead, it's -sufficient to declare _one_ regular dependency that includes a version number. Gradle will -then pull in the BOM automatically so you can omit the version for all other JUnit -artifacts. - -[source,groovy,indent=0] -[subs=attributes+] -.Implicit platform dependency on the BOM ----- -dependencies { - testImplementation("org.junit.jupiter:junit-jupiter:{version}") // <1> - testRuntimeOnly("org.junit.platform:junit-platform-launcher") // <2> -} ----- -<1> Dependency declaration with explicit version. Pulls in the `junit-bom` automatically. -<2> Dependency declaration without version. The version is supplied by the `junit-bom`. - -[WARNING] -.Declaring a dependency on junit-platform-launcher -==== -Even though pre-8.0 versions of Gradle don't require declaring an explicit -dependency on `junit-platform-launcher`, it is recommended to do so to ensure the versions -of JUnit artifacts on the test runtime classpath are aligned. - -Moreover, doing so is recommended and in some cases even required when importing the -project into an IDE like <> or -<>. -==== - -[[running-tests-build-gradle-engines-configure]] -===== Configuring Test Engines - -In order to run any tests at all, a `TestEngine` implementation must be on the classpath. - -To configure support for JUnit Jupiter based tests, configure a `testImplementation` dependency -on the dependency-aggregating JUnit Jupiter artifact similar to the following. - -[source,groovy,indent=0] -[subs=attributes+] ----- -dependencies { - testImplementation("org.junit.jupiter:junit-jupiter:{version}") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} ----- - -Alternatively, you can use Gradle's -https://docs.gradle.org/current/userguide/jvm_test_suite_plugin.html[JVM Test Suite] -support. - -[source,kotlin,indent=0] -[subs=attributes+] -.Kotlin DSL ----- -testing { - suites { - named("test") { - useJUnitJupiter("{version}") - } - } -} ----- - -[source,groovy,indent=0] -[subs=attributes+] -.Groovy DSL ----- -testing { - suites { - test { - useJUnitJupiter("{version}") - } - } -} ----- - -The JUnit Platform can run JUnit 4 based tests as long as you configure a `testImplementation` -dependency on JUnit 4 and a `testRuntimeOnly` dependency on the JUnit Vintage `TestEngine` -implementation similar to the following. - -[source,groovy,indent=0] -[subs=attributes+] ----- -dependencies { - testImplementation("junit:junit:{junit4-version}") - testRuntimeOnly("org.junit.vintage:junit-vintage-engine:{version}") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} ----- - -[[running-tests-build-gradle-config-params]] -===== Configuration Parameters - -The standard Gradle `test` task currently does not provide a dedicated DSL to set JUnit -Platform <> to influence test -discovery and execution. However, you can provide configuration parameters within the -build script via system properties (as shown below) or via the -`junit-platform.properties` file. - -[source,groovy,indent=0] ----- -test { - // ... - systemProperty("junit.jupiter.conditions.deactivate", "*") - systemProperty("junit.jupiter.extensions.autodetection.enabled", true) - systemProperty("junit.jupiter.testinstance.lifecycle.default", "per_class") - // ... -} ----- - -[[running-tests-build-gradle-logging]] -===== Configuring Logging (optional) - -JUnit uses the Java Logging APIs in the `java.util.logging` package (a.k.a. _JUL_) to -emit warnings and debug information. Please refer to the official documentation of -`{LogManager}` for configuration options. - -Alternatively, it's possible to redirect log messages to other logging frameworks such as -{Log4j} or {Logback}. To use a logging framework that provides a custom implementation of -`{LogManager}`, set the `java.util.logging.manager` system property to the _fully -qualified class name_ of the `{LogManager}` implementation to use. The example below -demonstrates how to configure Log4j{nbsp}2.x (see {Log4j_JDK_Logging_Adapter} for -details). - -[source,groovy,indent=0] -[subs=attributes+] ----- -test { - systemProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager") - // Avoid overhead (see https://logging.apache.org/log4j/2.x/manual/jmx.html#enabling-jmx) - systemProperty("log4j2.disableJmx", "true") -} ----- - -Other logging frameworks provide different means to redirect messages logged using -`java.util.logging`. For example, for {Logback} you can use the -https://www.slf4j.org/legacy.html#jul-to-slf4j[JUL to SLF4J Bridge] by adding it as a -dependency to the test runtime classpath. - -[[running-tests-build-maven]] -==== Maven - -Maven Surefire and Maven Failsafe provide -https://maven.apache.org/surefire/maven-surefire-plugin/examples/junit-platform.html[native support] -for executing tests on the JUnit Platform. The `pom.xml` file in the -`{junit-jupiter-starter-maven}` project demonstrates how to use the Maven Surefire plugin -and can serve as a starting point for configuring your Maven build. - -[WARNING] -.Minimum required version of Maven Surefire/Failsafe -==== -As of JUnit 6.0, the minimum required version of Maven Surefire/Failsafe is 3.0.0. -==== - -[[running-tests-build-maven-bom]] -===== Aligning dependency versions - -Unless you're using Spring Boot which defines its own way of managing dependencies, it is -recommended to use the JUnit Platform <> to align the -versions of all JUnit artifacts. - -[source,xml,indent=0] -[subs=attributes+] ----- - - - - org.junit - junit-bom - {version} - pom - import - - - ----- - -Using the BOM allows you to omit the version when declaring dependencies on all artifacts -with the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs. - -TIP: See <> for details on how to override the version -of JUnit used in your Spring Boot application. - -[[running-tests-build-maven-engines-configure]] -===== Configuring Test Engines - -In order to have Maven Surefire or Maven Failsafe run any tests at all, at least one -`TestEngine` implementation must be added to the test classpath. - -To configure support for JUnit Jupiter based tests, configure `test` scoped dependencies -on the JUnit Jupiter API and the JUnit Jupiter `TestEngine` implementation similar to the -following. - -[source,xml,indent=0] -[subs=attributes+] ----- - - - - - org.junit.jupiter - junit-jupiter - {version} - test - - - - - - - maven-surefire-plugin - {surefire-version} - - - maven-failsafe-plugin - {surefire-version} - - - - ----- - -Maven Surefire and Maven Failsafe can run JUnit 4 based tests alongside Jupiter tests as -long as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintage -`TestEngine` implementation similar to the following. - -[source,xml,indent=0] -[subs=attributes+] ----- - - - - - junit - junit - {junit4-version} - test - - - org.junit.vintage - junit-vintage-engine - {version} - test - - - - - - - - maven-surefire-plugin - {surefire-version} - - - maven-failsafe-plugin - {surefire-version} - - - - ----- - -[[running-tests-build-maven-filter-test-class-names]] -===== Filtering by Test Class Names - -The Maven Surefire Plugin will scan for test classes whose fully qualified names match -the following patterns. - -- `+++**/Test*.java+++` -- `+++**/*Test.java+++` -- `+++**/*Tests.java+++` -- `+++**/*TestCase.java+++` - -Moreover, it will exclude all nested classes (including static member classes) by default. - -Note, however, that you can override this default behavior by configuring explicit -`include` and `exclude` rules in your `pom.xml` file. For example, to keep Maven Surefire -from excluding static member classes, you can override its exclude rules as follows. - -[source,xml,indent=0] -[subs=attributes+] -.Overriding exclude rules of Maven Surefire ----- - - - - - maven-surefire-plugin - {surefire-version} - - - - - - - - - ----- - -Please see the -https://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html[Inclusions and Exclusions of Tests] -documentation for Maven Surefire for details. - -[[running-tests-build-maven-filter-tags]] -===== Filtering by Tags - -You can filter tests by <> or -<> using the following configuration -properties. - -- to include _tags_ or _tag expressions_, use `groups`. -- to exclude _tags_ or _tag expressions_, use `excludedGroups`. - -[source,xml,indent=0] -[subs=attributes+] ----- - - - - - maven-surefire-plugin - {surefire-version} - - acceptance | !feature-a - integration, regression - - - - - ----- - -[[running-tests-build-maven-config-params]] -===== Configuration Parameters - -You can set JUnit Platform <> to -influence test discovery and execution by declaring the `configurationParameters` -property and providing key-value pairs using the Java `Properties` file syntax (as shown -below) or via the `junit-platform.properties` file. - -[source,xml,indent=0] -[subs=attributes+] ----- - - - - - maven-surefire-plugin - {surefire-version} - - - - junit.jupiter.conditions.deactivate = * - junit.jupiter.extensions.autodetection.enabled = true - junit.jupiter.testinstance.lifecycle.default = per_class - - - - - - - ----- - -[[running-tests-build-ant]] -==== Ant - -Starting with version `1.10.3`, link:https://ant.apache.org/[Ant] has a -link:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher`] task that -provides native support for launching tests on the JUnit Platform. The `junitlauncher` -task is solely responsible for launching the JUnit Platform and passing it the selected -collection of tests. The JUnit Platform then delegates to registered test engines to -discover and execute the tests. - -The `junitlauncher` task attempts to align as closely as possible with native Ant -constructs such as -link:https://ant.apache.org/manual/Types/resources.html#collection[resource collections] -for allowing users to select the tests that they want executed by test engines. This gives -the task a consistent and natural feel when compared to many other core Ant tasks. - -Starting with version `1.10.6` of Ant, the `junitlauncher` task supports -link:https://ant.apache.org/manual/Tasks/junitlauncher.html#fork[forking the tests in a separate JVM]. - -The `build.xml` file in the `{junit-jupiter-starter-ant}` project demonstrates how to use -the task and can serve as a starting point. - -===== Basic Usage - -The following example demonstrates how to configure the `junitlauncher` task to select a -single test class (i.e., `com.example.project.CalculatorTests`). - -[source,xml,indent=0] ----- - - - - - - - - - - - ----- - -The `test` element allows you to specify a single test class that you want to be selected -and executed. The `classpath` element allows you to specify the classpath to be used to -launch the JUnit Platform. This classpath will also be used to locate test classes that -are part of the execution. - -The following example demonstrates how to configure the `junitlauncher` task to select -test classes from multiple locations. - -[source,xml,indent=0] ----- - - - - - - - - - - - - - - - - ----- - -In the above example, the `testclasses` element allows you to select multiple test -classes that reside in different locations. - -For further details on usage and configuration options please refer to the official Ant -documentation for the -link:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher` task]. - -[[running-tests-build-spring-boot]] -==== Spring Boot - -link:https://spring.io/projects/spring-boot[Spring Boot] provides automatic support for -managing the version of JUnit used in your project. In addition, the -`spring-boot-starter-test` artifact automatically includes testing libraries such as JUnit -Jupiter, AssertJ, Mockito, etc. - -If your build relies on dependency management support from Spring Boot, you should not -import JUnit's <> in your build script since that would -result in duplicate (and potentially conflicting) management of JUnit dependencies. - -If you need to override the version of a dependency used in your Spring Boot application, -you have to override the exact name of the -link:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#appendix.dependency-versions.properties[version property] -defined in the BOM used by the Spring Boot plugin. For example, the name of the JUnit -Jupiter version property in Spring Boot is `junit-jupiter.version`. The mechanism for -changing a dependency version is documented for both -link:https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#managing-dependencies.dependency-management-plugin.customizing[Gradle] -and -link:https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/#using.parent-pom[Maven]. - -With Gradle you can override the JUnit Jupiter version by including the following in your -`build.gradle` file. - -[source,groovy,indent=0] -[subs=attributes+] ----- - ext['junit-jupiter.version'] = '{version}' ----- - -With Maven you can override the JUnit Jupiter version by including the following in your -`pom.xml` file. - -[source,xml,indent=0] -[subs=attributes+] ----- - - {version} - ----- - -[[running-tests-console-launcher]] -=== Console Launcher - -The `{ConsoleLauncher}` is a command-line Java application that lets you launch the JUnit -Platform from the console. For example, it can be used to run JUnit Vintage and JUnit -Jupiter tests and print test execution results to the console. - -An executable _Fat JAR_ (`junit-platform-console-standalone-{version}.jar`) that -contains the contents of all of its dependencies is published in the {Maven_Central} -repository under the -https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone] -directory. It contains the contents of the following artifacts: - -include::{standaloneConsoleLauncherShadowedArtifactsFile}[] - -[NOTE] -==== -Since the `junit-platform-console-standalone` JAR contains the contents of all of its -dependencies, its Maven POM does not declare any dependencies. - -Furthermore, it is not very likely that you would need to include a dependency on the -`junit-platform-console-standalone` artifact in your project's Maven POM or Gradle build -script. On the contrary, the executable `junit-platform-console-standalone` JAR is -typically invoked directly from the command line or a shell script without a build script. - -If you need to declare dependencies in your build script on some of the artifacts -contained in the `junit-platform-console-standalone` artifact, you should declare -dependencies only on the JUnit artifacts that are used in your project. To simplify -dependency management of JUnit artifacts in your build, you may wish to use the -`junit-jupiter` aggregator artifact or `junit-bom`. See <> for -details. -==== - -You can https://docs.oracle.com/javase/tutorial/deployment/jar/run.html[run] the -standalone `ConsoleLauncher` as shown below. - -[source,console,subs=attributes+] ----- -$ java -jar junit-platform-console-standalone-{version}.jar execute - -├─ JUnit Vintage -│ └─ example.JUnit4Tests -│ └─ standardJUnit4Test ✔ -└─ JUnit Jupiter - ├─ StandardTests - │ ├─ succeedingTest() ✔ - │ └─ skippedTest() ↷ for demonstration purposes - └─ A special test case - ├─ Custom test name containing spaces ✔ - ├─ ╯°□°)╯ ✔ - └─ 😱 ✔ - -Test run finished after 64 ms -[ 5 containers found ] -[ 0 containers skipped ] -[ 5 containers started ] -[ 0 containers aborted ] -[ 5 containers successful ] -[ 0 containers failed ] -[ 6 tests found ] -[ 1 tests skipped ] -[ 5 tests started ] -[ 0 tests aborted ] -[ 5 tests successful ] -[ 0 tests failed ] ----- - -You can also run the standalone `ConsoleLauncher` as shown below (for example, to include -all jars in a directory): - -[source,console,subs=attributes+] ----- -$ java -cp classes:testlib/* org.junit.platform.console.ConsoleLauncher ----- - -[[running-tests-console-launcher-options]] -==== Subcommands and Options - -The `{ConsoleLauncher}` provides the following subcommands: - ----- -include::{consoleLauncherOptionsFile}[] ----- - -[[running-tests-console-launcher-options-discovering-tests]] -===== Discovering tests - ----- -include::{consoleLauncherDiscoverOptionsFile}[] ----- - -[[running-tests-console-launcher-options-executing-tests]] -===== Executing tests - -.Exit Code -NOTE: On successful runs, the `{ConsoleLauncher}` exits with a status code of `0`. -All non-zero codes indicate an error of some sort. For example, status code `1` -is returned if any containers or tests failed. If no tests are discovered and the -`--fail-if-no-tests` command-line option is supplied, the `ConsoleLauncher` exits -with a status code of `2`. Unexpected or invalid user input yields a status code -of `3`. An exit code of `-1` indicates an unspecified error condition. - ----- -include::{consoleLauncherExecuteOptionsFile}[] ----- - -[[running-tests-console-launcher-options-listing-test-engines]] -===== Listing test engines - ----- -include::{consoleLauncherEnginesOptionsFile}[] ----- - -[[running-tests-console-launcher-argument-files]] -==== Argument Files (@-files) - -On some platforms you may run into system limitations on the length of a command line when -creating a command line with lots of options or with long arguments. - -The `ConsoleLauncher` supports _argument files_, also known as _@-files_. Argument files -are files that themselves contain arguments to be passed to the command. When the -underlying https://github.com/remkop/picocli[picocli] command line parser encounters an -argument beginning with the character `@`, it expands the contents of that file into the -argument list. - -The arguments within a file can be separated by spaces or newlines. If an argument -contains embedded whitespace, the whole argument should be wrapped in double or single -quotes -- for example, `"-f=My Files/Stuff.java"`. - -If the argument file does not exist or cannot be read, the argument will be treated -literally and will not be removed. This will likely result in an "unmatched argument" -error message. You can troubleshoot such errors by executing the command with the -`picocli.trace` system property set to `DEBUG`. - -Multiple _@-files_ may be specified on the command line. The specified path may be -relative to the current directory or absolute. - -You can pass a real parameter with an initial `@` character by escaping it with an -additional `@` symbol. For example, `@@somearg` will become `@somearg` and will not be -subject to expansion. - -[[running-tests-console-launcher-redirecting-stdout-and-stderr]] -==== Redirecting Standard Output/Error to Files - -You can redirect the `System.out` (stdout) and `System.err` (stderr) output streams to -files using the `--redirect-stdout` and `--redirect-stderr` options: - -[source,console,subs=attributes+] ----- -$ java -jar junit-platform-console-standalone-{version}.jar \ - --redirect-stdout=stdout.txt \ - --redirect-stderr=stderr.txt ----- - -[NOTE] -==== -If the `--redirect-stdout` and `--redirect-stderr` arguments point to the same file, both -output streams will be redirected to that file. - -The default charset is used for writing to the files. -==== - -[[running-tests-console-launcher-color-customization]] -==== Color Customization - -The colors used in the output of the `{ConsoleLauncher}` can be customized. -The option `--single-color` will apply a built-in monochrome style, while -`--color-palette` will accept a properties file to override the -https://en.wikipedia.org/wiki/ANSI_escape_code#Colors[ANSI SGR] color styling. -The properties file below demonstrates the default style: - -[source,properties,indent=0] ----- -SUCCESSFUL = 32 -ABORTED = 33 -FAILED = 31 -SKIPPED = 35 -CONTAINER = 35 -TEST = 34 -DYNAMIC = 35 -REPORTED = 37 ----- - -[[running-tests-source-launcher]] -=== Source Launcher - -Starting with Java 25 it is possible to write minimal source code test programs -using the `org.junit.start` module. For example, like in a `HelloTests.java` -file reading: - -```java -import module org.junit.start; - -void main() { - JUnit.run(); -} - -@Test -void stringLength() { - Assertions.assertEquals(11, "Hello JUnit".length()); -} -``` -With all required modular JAR files available in a local `lib/` directory, the -following Java 25+ command will discover and execute tests using the JUnit Platform. -It will also print the result tree to the console. - -```shell -java --module-path lib --add-modules org.junit.start HelloTests.java -╷ -└─ JUnit Jupiter ✔ - └─ HelloTests ✔ - └─ stringLength() ✔ -``` - -Find JUnit's class API documentation here: {JUnit} - -[[running-tests-discovery-selectors]] -=== Discovery Selectors - -The JUnit Platform provides a rich set of discovery selectors that can be used to specify -which tests should be discovered or executed. - -Discovery selectors can be created programmatically using the factory methods in the -`{DiscoverySelectors}` class, specified declaratively via annotations when using the -<>, via options of the <>, or -generically as strings via their identifiers. - -The following discovery selectors are provided out of the box: - -|=== -| Java Type | API | Annotation | Console Launcher | Identifier - -| `{ClasspathResourceSelector}` | `{DiscoverySelectors_selectClasspathResource}` | `{SelectClasspathResource}` | `--select-resource /foo.csv` | `resource:/foo.csv` -| `{ClasspathRootSelector}` | `{DiscoverySelectors_selectClasspathRoots}` | -- | `--scan-classpath bin` | `classpath-root:bin` -| `{ClassSelector}` | `{DiscoverySelectors_selectClass}` | `{SelectClasses}` | `--select-class com.acme.Foo` | `class:com.acme.Foo` -| `{DirectorySelector}` | `{DiscoverySelectors_selectDirectory}` | `{SelectDirectories}` | `--select-directory foo/bar` | `directory:foo/bar` -| `{FileSelector}` | `{DiscoverySelectors_selectFile}` | `{SelectFile}` | `--select-file dir/foo.txt` | `file:dir/foo.txt` -| `{IterationSelector}` | `{DiscoverySelectors_selectIteration}` | `{Select}("")` | `--select-iteration method=com.acme.Foo#m[1..2]` | `iteration:method:com.acme.Foo#m[1..2]` -| `{MethodSelector}` | `{DiscoverySelectors_selectMethod}` | `{SelectMethod}` | `--select-method com.acme.Foo#m` | `method:com.acme.Foo#m` -| `{ModuleSelector}` | `{DiscoverySelectors_selectModule}` | `{SelectModules}` | `--select-module com.acme` | `module:com.acme` -| `{NestedClassSelector}` | `{DiscoverySelectors_selectNestedClass}` | `{Select}("")` | `--select ` | `nested-class:com.acme.Foo/Bar` -| `{NestedMethodSelector}` | `{DiscoverySelectors_selectNestedMethod}` | `{Select}("")` | `--select ` | `nested-method:com.acme.Foo/Bar#m` -| `{PackageSelector}` | `{DiscoverySelectors_selectPackage}` | `{SelectPackages}` | `--select-package com.acme.foo` | `package:com.acme.foo` -| `{UniqueIdSelector}` | `{DiscoverySelectors_selectUniqueId}` | `{Select}("")` | `--select-unique-id ` | `uid:[engine:Foo]/[segment:Bar]` -| `{UriSelector}` | `{DiscoverySelectors_selectUri}` | `{SelectUris}` | `--select-uri \file:///foo.txt` | `uri:file:///foo.txt` -|=== - - -[[running-tests-config-params]] -=== Configuration Parameters - -In addition to instructing the platform which test classes and test engines to include, -which packages to scan, etc., it is sometimes necessary to provide additional custom -configuration parameters that are specific to a particular test engine, listener, or -registered extension. For example, the JUnit Jupiter `TestEngine` supports _configuration -parameters_ for the following use cases. - -- <> -- <> -- <> -- <> - -_Configuration Parameters_ are text-based key-value pairs that can be supplied to test -engines running on the JUnit Platform via one of the following mechanisms. - -1. The `configurationParameter()` and `configurationParameters()` methods in - `LauncherDiscoveryRequestBuilder` which - is used to build a request supplied to the <>. - + - When running tests via one of the tools provided by the JUnit Platform you can specify - configuration parameters as follows: - * <>: use the `--config` command-line - option. - * <>: use the `systemProperty` or - `systemProperties` DSL. - * <>: use the - `configurationParameters` property. -2. The `configurationParametersResources()` method in `LauncherDiscoveryRequestBuilder`. - + - When running tests via the <> you can - specify custom configuration files using the `--config-resource` command-line option. -3. JVM system properties. -4. The JUnit Platform default configuration file: a file named `junit-platform.properties` - in the root of the class path that follows the syntax rules for Java `Properties` - files. - -NOTE: Configuration parameters are looked up in the exact order defined above. -Consequently, configuration parameters supplied directly to the `Launcher` take -precedence over those supplied via custom configuration files, system properties, and the -default configuration file. Similarly, configuration parameters supplied via system -properties take precedence over those supplied via the default configuration file. - -[[running-tests-config-params-deactivation-pattern]] -==== Pattern Matching Syntax - -This section describes the pattern matching syntax that is applied to the _configuration -parameters_ used for the following features. - -- <> -- <> -- <> -- <> - -If the value for the given _configuration parameter_ consists solely of an asterisk -(`+++*+++`), the pattern will match against all candidate classes. Otherwise, the value -will be treated as a comma-separated list of patterns where each pattern will be matched -against the fully qualified class name (_FQCN_) of each candidate class. Any dot (`.`) in -a pattern will match against a dot (`.`) or a dollar sign (`$`) in a FQCN. Any asterisk -(`+++*+++`) will match against one or more characters in a FQCN. All other characters in a -pattern will be matched one-to-one against a FQCN. - -Examples: - -- `+++*+++`: matches all candidate classes. -- `+++org.junit.*+++`: matches all candidate classes under the `org.junit` base package and - any of its subpackages. -- `+++*.MyCustomImpl+++`: matches every candidate class whose simple class name is exactly - `MyCustomImpl`. -- `+++*System*+++`: matches every candidate class whose FQCN contains `System`. -- `+++*System*+++, +++*Unit*+++`: matches every candidate class whose FQCN contains - `System` or `Unit`. -- `org.example.MyCustomImpl`: matches the candidate class whose FQCN is exactly - `org.example.MyCustomImpl`. -- `org.example.MyCustomImpl, org.example.TheirCustomImpl`: matches candidate classes whose - FQCN is exactly `org.example.MyCustomImpl` or `org.example.TheirCustomImpl`. - -[[running-tests-tags]] -=== Tags - -Tags are a JUnit Platform concept for marking and filtering tests. The programming model -for adding tags to containers and tests is defined by the testing framework. For example, -in JUnit Jupiter based tests, the `@Tag` annotation (see -<>) should be used. For JUnit 4 based tests, the -Vintage engine maps `@Category` annotations to tags (see -<>). Other testing frameworks may define their -own annotation or other means for users to specify tags. - -[[running-tests-tag-syntax-rules]] -==== Syntax Rules for Tags - -Regardless how a tag is specified, the JUnit Platform enforces the following rules: - -* A tag must not be `null` or _blank_. -* A _stripped_ tag must not contain whitespace. -* A _stripped_ tag must not contain ISO control characters. -* A _stripped_ tag must not contain any of the following _reserved characters_. -- `,`: _comma_ -- `(`: _left parenthesis_ -- `)`: _right parenthesis_ -- `&`: _ampersand_ -- `|`: _vertical bar_ -- `!`: _exclamation point_ - -NOTE: In the above context, "stripped" means that leading and trailing whitespace -characters have been removed using `java.lang.String.strip()`. - -[[running-tests-tag-expressions]] -==== Tag Expressions - -Tag expressions are boolean expressions with the operators `!`, `&` and `|`. In addition, -`(` and `)` can be used to adjust for operator precedence. - -Two special expressions are supported, `any()` and `none()`, which select all tests _with_ -any tags at all, and all tests _without_ any tags, respectively. -These special expressions may be combined with other expressions just like normal tags. - -.Operators (in descending order of precedence) -|=== -| Operator | Meaning | Associativity - -| `!` | not | right -| `&` | and | left -| `\|` | or | left -|=== - -If you are tagging your tests across multiple dimensions, tag expressions help you to -select which tests to execute. When tagging by test type (e.g., _micro_, _integration_, -_end-to-end_) and feature (e.g., *product*, *catalog*, *shipping*), the following tag -expressions can be useful. - -[%header,cols="40,60"] -|=== -| Tag Expression -| Selection - -| `+++product+++` -| all tests for *product* - -| `+++catalog \| shipping+++` -| all tests for *catalog* plus all tests for *shipping* - -| `+++catalog & shipping+++` -| all tests for the intersection between *catalog* and *shipping* - -| `+++product & !end-to-end+++` -| all tests for *product*, but not the _end-to-end_ tests - -| `+++(micro \| integration) & (product \| shipping)+++` -| all _micro_ or _integration_ tests for *product* or *shipping* -|=== - -[[running-tests-capturing-output]] -=== Capturing Standard Output/Error - -The JUnit Platform provides opt-in support for capturing output printed to `System.out` -and `System.err`. To enable it, set the `junit.platform.output.capture.stdout` and/or -`junit.platform.output.capture.stderr` <> to `true`. In addition, you may configure the maximum number of buffered bytes -to be used per executed test or container using `junit.platform.output.capture.maxBuffer`. - -If enabled, the JUnit Platform captures the corresponding output and publishes it as a -report entry using the `stdout` or `stderr` keys to all registered -`{TestExecutionListener}` instances immediately before reporting the test or container as -finished. - -Please note that the captured output will only contain output emitted by the thread that -was used to execute a container or test. Any output by other threads will be omitted -because particularly when -<> it would be impossible -to attribute it to a specific test or container. - -[[running-tests-listeners]] -=== Using Listeners and Interceptors - -The JUnit Platform provides the following listener APIs that allow JUnit, third parties, -and custom user code to react to events fired at various points during the discovery and -execution of a `TestPlan`. - -* `{LauncherSessionListener}`: receives events when a `{LauncherSession}` is opened and - closed. -* `{LauncherInterceptor}`: intercepts test discovery and execution in the context of a - `LauncherSession`. -* `{LauncherDiscoveryListener}`: receives events that occur during test discovery. -* `{TestExecutionListener}`: receives events that occur during test execution. - -The `LauncherSessionListener` API is typically implemented by build tools or IDEs and -registered automatically for you in order to support some feature of the build tool or IDE. - -The `LauncherDiscoveryListener` and `TestExecutionListener` APIs are often implemented in -order to produce some form of report or to display a graphical representation of the test -plan in an IDE. Such listeners may be implemented and automatically registered by a build -tool or IDE, or they may be included in a third-party library – potentially registered -for you automatically. You can also implement and register your own listeners. - -For details on registering and configuring listeners, see the following sections of this -guide. - -* <> -* <> -* <> -* <> -* <> -* <> - -The JUnit Platform provides the following listeners which you may wish to use with your -test suite. - -<> :: - `{LegacyXmlReportGeneratingListener}` can be used via the - <> or registered manually to generate XML reports - compatible with the de facto standard for JUnit 4 based test reports. -+ -`{OpenTestReportGeneratingListener}` generates an XML report in the event-based format -specified by {OpenTestReporting}. It is auto-registered and can be enabled and -configured via <>. -+ -See <> for details. - -<> :: - `FlightRecordingExecutionListener` and `FlightRecordingDiscoveryListener` that generate - Java Flight Recorder events during test discovery and execution. - -`{LoggingListener}` :: - `TestExecutionListener` for logging informational messages for all events via a - `BiConsumer` that consumes `Throwable` and `Supplier`. - -`{SummaryGeneratingListener}` :: - `TestExecutionListener` that generates a summary of the test execution which can be - printed via a `PrintWriter`. - -`{UniqueIdTrackingListener}` :: - `TestExecutionListener` that that tracks the unique IDs of all tests that were skipped - or executed during the execution of the `TestPlan` and generates a file containing the - unique IDs once execution of the `TestPlan` has finished. - -[[running-tests-listeners-flight-recorder]] -==== Flight Recorder Support - -The JUnit Platform provides opt-in support for generating Flight Recorder events. -https://openjdk.java.net/jeps/328[JEP 328] describes the Java Flight Recorder (JFR) as -follows. - -> Flight Recorder records events originating from applications, the JVM, and the OS. -Events are stored in a single file that can be attached to bug reports and examined by -support engineers, allowing after-the-fact analysis of issues in the period leading up -to a problem. - -In order to record Flight Recorder events generated while running tests, you need to -start flight recording when launching a test suite via the following java command line -option. - - -XX:StartFlightRecording:filename=... - -Please consult the manual of your build tool for the appropriate commands. - -To analyze the recorded events, use the -https://docs.oracle.com/en/java/javase/17/docs/specs/man/jfr.html[jfr] -command line tool shipped with recent JDKs or open the recording file with -https://jdk.java.net/jmc/[JDK Mission Control]. - -[[stacktrace-pruning]] -=== Stack Trace Pruning - -The JUnit Platform provides built-in support for pruning stack traces produced by failing -tests. This feature is enabled by default but can be disabled by setting the -`junit.platform.stacktrace.pruning.enabled` _configuration parameter_ to `false`. - -When enabled, all calls from the `org.junit`, `jdk.internal.reflect`, and `sun.reflect` -packages are removed from the stack trace, unless the calls occur after the test itself -or any of its ancestors. For that reason, calls to `{Assertions}` or `{Assumptions}` will -never be excluded. - -In addition, all elements prior to and including the first call from the JUnit Platform -`Launcher` will be removed. - -[[running-tests-discovery-issues]] -=== Discovery Issues - -Test engines may encounter issues during test discovery. For example, the declaration of a -test class or method may be invalid. To avoid such issues from going unnoticed, the JUnit -Platform provides a <> to -report them with different severity levels: - -INFO:: -Indicates that the engine encountered something that could be potentially problematic, but -could also happen due to a valid setup or configuration. - -WARNING:: -Indicates that the engine encountered something that is problematic and might lead to -unexpected behavior or will be removed or changed in a future release. - -ERROR:: -Indicates that the engine encountered something that is definitely problematic and will -lead to unexpected behavior. - -If an engine reports an issue with a severity equal to or higher than a configurable -_critical_ severity, its tests will not be executed. Instead, the engine will be reported -as failed during execution with a `{DiscoveryIssueException}` listing all critical issues. -Non-critical issues will be logged but will not prevent the engine from executing its -tests. The `junit.platform.discovery.issue.severity.critical` -<> can be used to set the critical -severity level. Currently, the default value is `ERROR` but it may be changed in a future -release. - -TIP: To surface all discovery issues in your project, it is recommended to set the -`junit.platform.discovery.issue.severity.critical` configuration parameter to `INFO`. - -In addition, registered `{LauncherDiscoveryListener}` implementations can receive -discovery issues via the `issueEncountered()` method. This allows IDEs and build tools to -report issues to the user in a more user-friendly way. For example, IDEs may choose to -display all issues in a list or table. diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc deleted file mode 100644 index 18eddfd55b43..000000000000 --- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc +++ /dev/null @@ -1,3940 +0,0 @@ -:testDir: ../../../../src/test/java -:testResourcesDir: ../../../../src/test/resources -:kotlinTestDir: ../../../../src/test/kotlin - -[[writing-tests]] -== Writing Tests - -The following example provides a glimpse at the minimum requirements for writing a test in -JUnit Jupiter. Subsequent sections of this chapter will provide further details on all -available features. - -[source,java,indent=0] -.A first test case ----- -include::{testDir}/example/MyFirstJUnitJupiterTests.java[tags=user_guide] ----- - -[[writing-tests-annotations]] -=== Annotations - -JUnit Jupiter supports the following annotations for configuring tests and extending the -framework. - -Unless otherwise stated, all core annotations are located in the `{api-package}` package -in the `junit-jupiter-api` module. - -`*@Test*`:: Denotes that a method is a test method. Unlike JUnit 4's `@Test` annotation, -this annotation does not declare any attributes, since test extensions in JUnit Jupiter -operate based on their own dedicated annotations. Such methods are inherited unless they -are overridden. - -`*@ParameterizedTest*`:: Denotes that a method is a -<>. Such methods are inherited -unless they are overridden. - -`*@RepeatedTest*`:: Denotes that a method is a test template for a -<>. Such methods are inherited unless they -are overridden. - -`*@TestFactory*`:: Denotes that a method is a test factory for -<>. Such methods are inherited unless they are -overridden. - -`*@TestTemplate*`:: Denotes that a method is a -<> designed to be invoked multiple -times depending on the number of invocation contexts returned by the registered -<>. Such methods are inherited unless they are -overridden. - -`*@TestClassOrder*`:: Used to configure the -<> for `@Nested` -test classes in the annotated test class. Such annotations are inherited. - -`*@TestMethodOrder*`:: Used to configure the -<> for the -annotated test class; similar to JUnit 4's `@FixMethodOrder`. Such annotations are -inherited. - -`*@TestInstance*`:: Used to configure the -<> for the annotated test -class. Such annotations are inherited. - -`*@DisplayName*`:: Declares a custom <> for the -test class or test method. Such annotations are not inherited. - -`*@DisplayNameGeneration*`:: Declares a custom -<> for the test class. Such -annotations are inherited. - -`*@BeforeEach*`:: Denotes that the annotated method should be executed _before_ *each* -`@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current -class; analogous to JUnit 4's `@Before`. Such methods are inherited unless they are -overridden. - -`*@AfterEach*`:: Denotes that the annotated method should be executed _after_ *each* -`@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current -class; analogous to JUnit 4's `@After`. Such methods are inherited unless they are -overridden. - -`*@BeforeAll*`:: Denotes that the annotated method should be executed _before_ *all* -`@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current -top-level or `@Nested` test class; analogous to JUnit 4's `@BeforeClass`. Such methods are -inherited unless they are overridden and must be `static` unless the "per-class" -<> is used. - -`*@AfterAll*`:: Denotes that the annotated method should be executed _after_ *all* -`@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current -top-level or `@Nested` test class; analogous to JUnit 4's `@AfterClass`. Such methods are -inherited unless they are overridden and must be `static` unless the "per-class" -<> is used. - -`*@ParameterizedClass*`:: Denotes that the annotated class is a -<>. Such annotations are -inherited. - -`*@BeforeParameterizedClassInvocation*`:: Denotes that the annotated method should be -executed once _before_ each invocation of a -<>. Such methods are inherited -unless they are overridden. - -`*@AfterParameterizedClassInvocation*`:: Denotes that the annotated method should be -executed once _after_ each invocation of a -<>. Such methods are inherited -unless they are overridden. - -`*@ClassTemplate*`:: Denotes that the annotated class is a -<> designed to be executed -multiple times depending on the number of invocation contexts returned by the registered -<>. Such annotations are inherited. - -`*@Nested*`:: Denotes that the annotated class is a non-static -<>. Such annotations are not inherited. - -`*@Tag*`:: Used to declare -<>, either at the class or -method level; analogous to test groups in TestNG or Categories in JUnit 4. Such -annotations are inherited at the class level but not at the method level. - -`*@Disabled*`:: Used to <> a test class or test method; -analogous to JUnit 4's `@Ignore`. Such annotations are not inherited. - -`*@AutoClose*`:: Denotes that the annotated field represents a resource that will be -<> after test -execution. Such fields are inherited. - -`*@Timeout*`:: Used to fail a test, test factory, test template, or lifecycle method if -its execution exceeds a given duration. Such annotations are inherited. - -`*@TempDir*`:: Used to supply a -<> via field -injection or parameter injection in a test class constructor, lifecycle method, or test -method; located in the `org.junit.jupiter.api.io` package. Such fields are inherited. - -`*@ExtendWith*`:: Used to -<>. Such -annotations are inherited. - -`*@RegisterExtension*`:: Used to -<> via fields. -Such fields are inherited. - -WARNING: Some annotations may currently be _experimental_. Consult the table in -<> for details. - -[[writing-tests-meta-annotations]] -==== Meta-Annotations and Composed Annotations - -JUnit Jupiter annotations can be used as _meta-annotations_. That means that you can -define your own _composed annotation_ that will automatically _inherit_ the semantics of -its meta-annotations. - -For example, instead of copying and pasting `@Tag("fast")` throughout your code base (see -<>), you can create a custom _composed annotation_ -named `@Fast` as follows. `@Fast` can then be used as a drop-in replacement for -`@Tag("fast")`. - -[source,java,indent=0] ----- -include::{testDir}/example/Fast.java[tags=user_guide] ----- - -The following `@Test` method demonstrates usage of the `@Fast` annotation. - -[source,java,indent=0] ----- -@Fast -@Test -void myFastTest() { - // ... -} ----- - -You can even take that one step further by introducing a custom `@FastTest` annotation -that can be used as a drop-in replacement for `@Tag("fast")` _and_ `@Test`. - -[source,java,indent=0] ----- -include::{testDir}/example/FastTest.java[tags=user_guide] ----- - -JUnit automatically recognizes the following as a `@Test` method that is tagged with -"fast". - -[source,java,indent=0] ----- -@FastTest -void myFastTest() { - // ... -} ----- - -[[writing-tests-definitions]] -=== Definitions - -.Platform Concepts -**** -Container:: -a node in the test tree that contains other containers or tests as its children (e.g. a _test class_). - -Test:: -a node in the test tree that verifies expected behavior when executed (e.g. a `@Test` method). -**** - -.Jupiter Concepts -**** -Lifecycle Method:: -any method that is directly annotated or meta-annotated with -`@BeforeAll`, `@AfterAll`, `@BeforeEach`, or `@AfterEach`. - -Test Class:: -any top-level class, `static` member class, or <> that contains at least one _test method_, i.e. a _container_. -Test classes must not be `abstract` and must have a single constructor. -Java `record` classes are supported as well. - -Test Method:: -any instance method that is directly annotated or meta-annotated with -`@Test`, `@RepeatedTest`, `@ParameterizedTest`, `@TestFactory`, or `@TestTemplate`. -With the exception of `@Test`, these create a _container_ in the test tree that groups -_tests_ or, potentially (for `@TestFactory`), other _containers_. -**** - -[[writing-tests-classes-and-methods]] -=== Test Classes and Methods - -Test methods and lifecycle methods may be declared locally within the current test class, -inherited from superclasses, or inherited from interfaces (see -<>). In addition, test methods and -lifecycle methods must not be `abstract` and must not return a value (except `@TestFactory` -methods which are required to return a value). - -[NOTE] -.Class and method visibility -==== -Test classes, test methods, and lifecycle methods are not required to be `public`, but -they must _not_ be `private`. - -It is generally recommended to omit the `public` modifier for test classes, test methods, -and lifecycle methods unless there is a technical reason for doing so – for example, when -a test class is extended by a test class in another package. Another technical reason for -making classes and methods `public` is to simplify testing on the module path when using -the Java Module System. -==== - -[NOTE] -.Field and method inheritance -==== -Fields in test classes are inherited. For example, a `@TempDir` field from a superclass -will always be applied in a subclass. - -Test methods and lifecycle methods are inherited unless they are overridden according to -the visibility rules of the Java language. For example, a `@Test` method from a superclass -will always be applied in a subclass unless the subclass explicitly overrides the method. -Similarly, if a package-private `@Test` method is declared in a superclass that resides in -a different package than the subclass, that `@Test` method will always be applied in the -subclass since the subclass cannot override a package-private method from a superclass in -a different package. - -See also: <> -==== - -The following test class demonstrates the use of `@Test` methods and all supported -lifecycle methods. For further information on runtime semantics, see -<> and -<>. - -[source,java,indent=0] -.A standard Java test class ----- -include::{testDir}/example/StandardTests.java[tags=user_guide] ----- - -It is also possible to use Java `record` classes as test classes as illustrated by the -following example. - -[source,java,indent=0] -.A test class written as a Java record ----- -include::{testDir}/example/MyFirstJUnitJupiterRecordTests.java[tags=user_guide] ----- - -Test and lifecycle methods may be written in Kotlin and may optionally use the `suspend` -keyword for testing code using coroutines. - -[source,kotlin] -.A test class written in Kotlin ----- -include::{kotlinTestDir}/example/KotlinCoroutinesDemo.kt[tags=user_guide] ----- - -NOTE: Using suspending functions as test or lifecycle methods requires -https://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-stdlib[`kotlin-stdlib`], -https://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-reflect[`kotlin-reflect`], -and -https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core[`kotlinx-coroutines-core`] -to be present on the classpath or module path. - -[[writing-tests-display-names]] -=== Display Names - -Test classes and test methods can declare custom display names via `@DisplayName` -- with -spaces, special characters, and even emojis -- that will be displayed in test reports and -by test runners and IDEs. - -[source,java,indent=0] ----- -include::{testDir}/example/DisplayNameDemo.java[tags=user_guide] ----- - -[NOTE] -==== -Control characters in text-based arguments in display names for parameterized tests are -escaped by default. See <> -for details. - -Any remaining ISO control characters in a display name will be replaced as follows. - -[cols="25%,15%,60%"] -|=== -| Original | Replacement | Description - -| ```\r``` -| `````` -| Textual representation of a carriage return - -| ```\n``` -| `````` -| Textual representation of a line feed - -| Other control character -| ```�``` -| Unicode replacement character (U+FFFD) -|=== -==== - -[[writing-tests-display-name-generator]] -==== Display Name Generators - -JUnit Jupiter supports custom display name generators that can be configured via the -`@DisplayNameGeneration` annotation. - -Generators can be created by implementing the `DisplayNameGenerator` API. The following -table lists the default display name generators available in Jupiter. - -[cols="20,80"] -|=== -| DisplayNameGenerator | Behavior - -| `Standard` | Matches the standard display name generation behavior in place since JUnit Jupiter was introduced. -| `Simple` | Extends the functionality of `Standard` by removing trailing parentheses for methods with no parameters. -| `ReplaceUnderscores` | Replaces underscores with spaces. -| `IndicativeSentences` | Generates complete sentences by concatenating the names of the test and the enclosing classes. -|=== - -NOTE: Values provided via `@DisplayName` annotations always take precedence over display -names generated by a `DisplayNameGenerator`. - -====== -The following example demonstrates the use of the `ReplaceUnderscores` display name -generator. - -[source,java,indent=0] ----- -include::{testDir}/example/DisplayNameGeneratorDemo.java[tags=user_guide_replace_underscores] ----- - -Running the above test class results in the following display names. - -``` -A year is not supported ✔ -├─ if it is zero ✔ -└─ A negative value for year is not supported by the leap year computation. ✔ - ├─ For example, year -1 is not supported. ✔ - └─ For example, year -4 is not supported. ✔ -``` -====== - -====== -With the `IndicativeSentences` display name generator, you can customize the separator and -the underlying generator by using `@IndicativeSentencesGeneration` as shown in the -following example. - -[source,java,indent=0] ----- -include::{testDir}/example/DisplayNameGeneratorDemo.java[tags=user_guide_indicative_sentences] ----- - -Running the above test class results in the following display names. - -``` -A year is a leap year ✔ -├─ A year is a leap year -> if it is divisible by 4 but not by 100 ✔ -└─ A year is a leap year -> if it is one of the following years ✔ - ├─ Year 2016 is a leap year. ✔ - ├─ Year 2020 is a leap year. ✔ - └─ Year 2048 is a leap year. ✔ -``` -====== - -====== -With `IndicativeSentences`, you can optionally specify custom sentence fragments via the -`@SentenceFragment` annotation as demonstrated in the following example. - -[source,java,indent=0] ----- -include::{testDir}/example/DisplayNameGeneratorDemo.java[tags=user_guide_custom_sentence_fragments] ----- - -Running the above test class results in the following display names. - -``` -A year is a leap year ✔ -├─ A year is a leap year, if it is divisible by 4 but not by 100 ✔ -└─ A year is a leap year, if it is one of the following years ✔ - ├─ 2016 ✔ - ├─ 2020 ✔ - └─ 2048 ✔ -``` -====== - - -[[writing-tests-display-name-generator-default]] -==== Setting the Default Display Name Generator - -You can use the `junit.jupiter.displayname.generator.default` -<> to specify the fully qualified -class name of the `DisplayNameGenerator` you would like to use by default. Just like for -display name generators configured via the `@DisplayNameGeneration` annotation, the -supplied class has to implement the `DisplayNameGenerator` interface. The default display -name generator will be used for all tests unless the `@DisplayNameGeneration` annotation -is present on an enclosing test class or test interface. Values provided via -`@DisplayName` annotations always take precedence over display names generated by a -`DisplayNameGenerator`. - -For example, to use the `ReplaceUnderscores` display name generator by default, you should -set the configuration parameter to the corresponding fully qualified class name (e.g., in -`src/test/resources/junit-platform.properties`): - -[source,properties,indent=0] ----- -junit.jupiter.displayname.generator.default = \ - org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores ----- - -Similarly, you can specify the fully qualified name of any custom class that implements -`DisplayNameGenerator`. - -[[writing-tests-display-name-generator-precedence-rules]] -In summary, the display name for a test class or method is determined according to the -following precedence rules: - -1. value of the `@DisplayName` annotation, if present -2. by calling the `DisplayNameGenerator` specified in the `@DisplayNameGeneration` - annotation, if present -3. by calling the default `DisplayNameGenerator` configured via the configuration - parameter, if present -4. by calling `org.junit.jupiter.api.DisplayNameGenerator.Standard` - -[[writing-tests-assertions]] -=== Assertions - -JUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds a few -that lend themselves well to being used with Java lambdas. All JUnit Jupiter assertions -are `static` methods in the `{Assertions}` class. - -Assertion methods optionally accept the assertion message as their third parameter, which -can be either a `String` or a `Supplier`. - -When using a `Supplier` (e.g., a lambda expression), the message is evaluated -lazily. This can provide a performance benefit, especially if message construction is -complex or time-consuming, as it is only evaluated when the assertion fails. - -[source,java,indent=0] ----- -include::{testDir}/example/AssertionsDemo.java[tags=user_guide] ----- - -[[writing-tests-assertions-preemptive-timeouts]] -[WARNING] -.Preemptive Timeouts with `assertTimeoutPreemptively()` -==== -The various `assertTimeoutPreemptively()` methods in the `Assertions` class execute -the provided `executable` or `supplier` in a different thread than that of the calling -code. This behavior can lead to undesirable side effects if the code that is executed -within the `executable` or `supplier` relies on `java.lang.ThreadLocal` storage. - -One common example of this is the transactional testing support in the Spring Framework. -Specifically, Spring's testing support binds transaction state to the current thread (via -a `ThreadLocal`) before a test method is invoked. Consequently, if an `executable` or -`supplier` provided to `assertTimeoutPreemptively()` invokes Spring-managed components -that participate in transactions, any actions taken by those components will not be rolled -back with the test-managed transaction. On the contrary, such actions will be committed to -the persistent store (e.g., relational database) even though the test-managed transaction -is rolled back. - -Similar side effects may be encountered with other frameworks that rely on -`ThreadLocal` storage. -==== - -[[writing-tests-assertions-kotlin]] -==== Kotlin Assertion Support - -JUnit Jupiter also comes with a few assertion methods that lend themselves well to being -used in https://kotlinlang.org/[Kotlin]. All JUnit Jupiter Kotlin assertions are top-level -functions in the `org.junit.jupiter.api` package. - -[source,kotlin,indent=0] ----- -include::{kotlinTestDir}/example/KotlinAssertionsDemo.kt[tags=user_guide] ----- - -[[writing-tests-assertions-third-party]] -==== Third-party Assertion Libraries - -Even though the assertion facilities provided by JUnit Jupiter are sufficient for many -testing scenarios, there are times when more power and additional functionality are -desired or required. In such cases, the JUnit team recommends the use of third-party -assertion libraries such as {AssertJ}, {Hamcrest}, {Truth}, etc. Developers are therefore -free to use the assertion library of their choice. - -For example, the following demonstrates how to use the `assertThat()` support from AssertJ -in a JUnit Jupiter test. As long as the AssertJ library has been added to the classpath, -you can statically import methods such as `assertThat()`, `assertThatException()`, etc. -from `org.assertj.core.api.Assertions` and then use them in tests like in the -`assertWithAssertJ()` method below. - -[source,java,indent=0] ----- -include::{testDir}/example/AssertJAssertionsDemo.java[tags=user_guide] ----- - -[TIP] -.Excluding Jupiter’s Assertions From a Project’s Classpath -==== -If you would like to enforce that all your tests use a certain third-party assertion -library instead of Jupiter's, you can set up a rule using {Checkstyle} or another static -analysis tool that fails the build if Jupiter's `Assertions` class is used. - -[source,xml] ----- - - - - - - - - - - - - - - ----- -==== - -[[writing-tests-assumptions]] -=== Assumptions - -Assumptions are typically used whenever it does not make sense to continue execution of a -given test — for example, if the test depends on something that does not exist in the -current runtime environment. - -* When an assumption is valid, the assumption method does not throw an exception, and - execution of the test continues as usual. -* When an assumption is invalid, the assumption method throws an exception of type - `org.opentest4j.TestAbortedException` to signal that the test should be aborted instead - of marked as a failure. - -JUnit Jupiter comes with a subset of the _assumption_ methods that JUnit 4 provides and -adds a few that lend themselves well to being used with Java lambda expressions and method -references. - -All JUnit Jupiter assumptions are static methods in the `{Assumptions}` class. - -[source,java,indent=0] ----- -include::{testDir}/example/AssumptionsDemo.java[tags=user_guide] ----- - -NOTE: It is also possible to use methods from JUnit 4's `org.junit.Assume` class for -assumptions. Specifically, JUnit Jupiter supports JUnit 4's `AssumptionViolatedException` -to signal that a test should be aborted instead of marked as a failure. - -TIP: If you use AssertJ for assertions, you may also wish to use AssertJ for assumptions. -To do so, you can statically import the `assumeThat()` method from -`org.assertj.core.api.Assumptions` and then use AssertJ's fluent API to specify your -assumptions. - -[[writing-tests-exceptions]] -=== Exception Handling - -JUnit Jupiter provides robust support for handling test exceptions. This includes the -built-in mechanisms for managing test failures due to exceptions, the role of exceptions -in implementing assertions and assumptions, and how to specifically assert non-throwing -conditions in code. - -[[writing-tests-exceptions-uncaught]] -==== Uncaught Exceptions - -In JUnit Jupiter, if an exception is thrown from a test method, a lifecycle method, or an -extension and not caught within that test method, lifecycle method, or extension, the -framework will mark the test or test class as failed. - -[TIP] -==== -Failed assumptions deviate from this general rule. - -In contrast to failed assertions, failed assumptions do not result in a test failure; -rather, a failed assumption results in a test being aborted. - -See <> for further details and examples. -==== - -In the following example, the `failsDueToUncaughtException()` method throws an -`ArithmeticException`. Since the exception is not caught within the test method, JUnit -Jupiter will mark the test as failed. - -[source,java,indent=0] ----- -include::{testDir}/example/exception/UncaughtExceptionHandlingDemo.java[tags=user_guide] ----- - -NOTE: It's important to note that specifying a `throws` clause in the test method has -no effect on the outcome of the test. JUnit Jupiter does not interpret a `throws` clause -as an expectation or assertion about what exceptions the test method should throw. A test -fails only if an exception is thrown unexpectedly or if an assertion fails. - -[[writing-tests-exceptions-failed-assertions]] -==== Failed Assertions - -Assertions in JUnit Jupiter are implemented using exceptions. The framework provides a set -of assertion methods in the `org.junit.jupiter.api.Assertions` class, which throw -`AssertionError` when an assertion fails. This mechanism is a core aspect of how JUnit -handles assertion failures as exceptions. See the <> section for -further information about JUnit Jupiter's assertion support. - -NOTE: Third-party assertion libraries may choose to throw an `AssertionError` to signal a -failed assertion; however, they may also choose to throw different types of exceptions to -signal failures. See also: <>. - -TIP: JUnit Jupiter itself does not differentiate between failed assertions -(`AssertionError`) and other types of exceptions. All uncaught exceptions lead to a test -failure. However, Integrated Development Environments (IDEs) and other tools may -distinguish between these two types of failures by checking whether the thrown exception -is an instance of `AssertionError`. - -In the following example, the `failsDueToUncaughtAssertionError()` method throws an -`AssertionError`. Since the exception is not caught within the test method, JUnit Jupiter -will mark the test as failed. - -[source,java,indent=0] ----- -include::{testDir}/example/exception/FailedAssertionDemo.java[tags=user_guide] ----- - -[[writing-tests-exceptions-expected]] -==== Asserting Expected Exceptions - -JUnit Jupiter offers specialized assertions for testing that specific exceptions are -thrown under expected conditions. The `assertThrows()` and `assertThrowsExactly()` -assertions are critical tools for validating that your code responds correctly to error -conditions by throwing the appropriate exceptions. - -[[writing-tests-exceptions-expected-assertThrows]] -===== Using `assertThrows()` - -The `assertThrows()` method is used to verify that a particular type of exception is -thrown during the execution of a provided executable block. It not only checks for the -type of the thrown exception but also its subclasses, making it suitable for more -generalized exception handling tests. The `assertThrows()` assertion method returns the -thrown exception object to allow performing additional assertions on it. - -[source,java,indent=0] ----- -include::{testDir}/example/exception/ExceptionAssertionDemo.java[tags=user_guide] ----- - -[[writing-tests-exceptions-expected-assertThrowsExactly]] -===== Using `assertThrowsExactly()` - -The `assertThrowsExactly()` method is used when you need to assert that the exception -thrown is exactly of a specific type, not allowing for subclasses of the expected -exception type. This is useful when precise exception handling behavior needs to be -validated. Similar to `assertThrows()`, the `assertThrowsExactly()` assertion method also -returns the thrown exception object to allow performing additional assertions on it. - -[source,java,indent=0] ----- -include::{testDir}/example/exception/ExceptionAssertionExactDemo.java[tags=user_guide] ----- - -[[writing-tests-exceptions-not-expected]] -==== Asserting That no Exception is Expected - -Although any exception thrown from a test method will cause the test to fail, there are -certain use cases where it can be beneficial to explicitly assert that an exception is -_not_ thrown for a given code block within a test method. The `assertDoesNotThrow()` -assertion can be used when you want to verify that a particular piece of code does not -throw any exceptions. - -[source,java,indent=0] ----- -include::{testDir}/example/exception/AssertDoesNotThrowExceptionDemo.java[tags=user_guide] ----- - -NOTE: Third-party assertion libraries often provide similar support. For example, AssertJ -has `assertThatNoException().isThrownBy(() -> ...)`. See also: -<>. - -[[writing-tests-disabling]] -=== Disabling Tests - -Entire test classes or individual test methods may be _disabled_ via the `{Disabled}` -annotation, via one of the annotations discussed in -<>, or via a custom <>. - -When `@Disabled` is applied at the class level, all test methods within that class are -automatically disabled as well. - -If a test method is disabled via `@Disabled`, that prevents execution of the test method -and method-level lifecycle callbacks such as `@BeforeEach` methods, `@AfterEach` methods, -and corresponding extension APIs. However, that does not prevent the test class from being -instantiated, and it does not prevent the execution of class-level lifecycle callbacks -such as `@BeforeAll` methods, `@AfterAll` methods, and corresponding extension APIs. - -Here's a `@Disabled` test class. - -[source,java,indent=0] ----- -include::{testDir}/example/DisabledClassDemo.java[tags=user_guide] ----- - -And here's a test class that contains a `@Disabled` test method. - -[source,java,indent=0] ----- -include::{testDir}/example/DisabledTestsDemo.java[tags=user_guide] ----- - -[TIP] -==== -`@Disabled` may be declared without providing a _reason_; however, the JUnit team -recommends that developers provide a short explanation for why a test class or test -method has been disabled. Consequently, the above examples both show the use of a reason --- for example, `@Disabled("Disabled until bug #42 has been resolved")`. Some development -teams even require the presence of issue tracking numbers in the _reason_ for automated -traceability, etc. -==== - -[NOTE] -==== -`@Disabled` is not `@Inherited`. Consequently, if you wish to disable a class whose -superclass is `@Disabled`, you must redeclare `@Disabled` on the subclass. -==== - - -[[writing-tests-conditional-execution]] -=== Conditional Test Execution - -The <> extension API in JUnit Jupiter allows -developers to either _enable_ or _disable_ a test class or test method based on certain -conditions _programmatically_. The simplest example of such a condition is the built-in -`{DisabledCondition}` which supports the `{Disabled}` annotation (see -<>). - -In addition to `@Disabled`, JUnit Jupiter also supports several other annotation-based -conditions in the `org.junit.jupiter.api.condition` package that allow developers to -enable or disable test classes and test methods _declaratively_. If you wish to provide -details about why they might be disabled, every annotation associated with these built-in -conditions has a `disabledReason` attribute available for that purpose. - -When multiple `ExecutionCondition` extensions are registered, a test class or test method -is disabled as soon as one of the conditions returns _disabled_. If a test class is -disabled, all test methods within that class are automatically disabled as well. If a test -method is disabled, that prevents execution of the test method and method-level lifecycle -callbacks such as `@BeforeEach` methods, `@AfterEach` methods, and corresponding extension -APIs. However, that does not prevent the test class from being instantiated, and it does -not prevent the execution of class-level lifecycle callbacks such as `@BeforeAll` methods, -`@AfterAll` methods, and corresponding extension APIs. - -See <> and the following sections for -details. - -[TIP] -.Composed Annotations -==== -Note that any of the _conditional_ annotations listed in the following sections may also -be used as a meta-annotation in order to create a custom _composed annotation_. For -example, the `@TestOnMac` annotation in the -<> shows how you can -combine `@Test` and `@EnabledOnOs` in a single, reusable annotation. -==== - -[NOTE] -==== -_Conditional_ annotations in JUnit Jupiter are not `@Inherited`. Consequently, if you wish -to apply the same semantics to subclasses, each conditional annotation must be redeclared -on each subclass. -==== - -[WARNING] -==== -Unless otherwise stated, each of the _conditional_ annotations listed in the following -sections can only be declared once on a given test interface, test class, or test method. -If a conditional annotation is directly present, indirectly present, or meta-present -multiple times on a given element, only the first such annotation discovered by JUnit will -be used; any additional declarations will be silently ignored. Note, however, that each -conditional annotation may be used in conjunction with other conditional annotations in -the `org.junit.jupiter.api.condition` package. -==== - -[[writing-tests-conditional-execution-os]] -==== Operating System and Architecture Conditions - -A container or test may be enabled or disabled on a particular operating system, -architecture, or combination of both via the `{EnabledOnOs}` and `{DisabledOnOs}` -annotations. - -[[writing-tests-conditional-execution-os-demo]] -[source,java,indent=0] -.Conditional execution based on operating system ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_os] ----- - -[[writing-tests-conditional-execution-architectures-demo]] -[source,java,indent=0] -.Conditional execution based on architecture ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_architecture] ----- - -[[writing-tests-conditional-execution-jre]] -==== Java Runtime Environment Conditions - -A container or test may be enabled or disabled on particular versions of the Java Runtime -Environment (JRE) via the `{EnabledOnJre}` and `{DisabledOnJre}` annotations or on a -particular range of versions of the JRE via the `{EnabledForJreRange}` and -`{DisabledForJreRange}` annotations. The range effectively defaults to `JRE.JAVA_8` as the -lower bound and `JRE.OTHER` as the upper bound, which allows usage of half open ranges. - -The following listing demonstrates the use of these annotations with predefined {JRE} enum -constants. - -[source,java,indent=0] ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_jre] ----- - -Since the enum constants defined in {JRE} are static for any given JUnit release, you -might find that you need to configure a Java version that is not supported by the `JRE` -enum. For example, when JUnit Jupiter 5.12 was released the `JRE` enum defined `JAVA_25` -as the highest supported Java version. However, you may wish to run your tests against -later versions of Java. To support such use cases, you can specify arbitrary Java versions -via the `versions` attributes in `@EnabledOnJre` and `@DisabledOnJre` and via the -`minVersion` and `maxVersion` attributes in `@EnabledForJreRange` and -`@DisabledForJreRange`. - -The following listing demonstrates the use of these annotations with arbitrary Java -versions. - -[source,java,indent=0] ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_jre_arbitrary_versions] ----- - -[[writing-tests-conditional-execution-native]] -==== Native Image Conditions - -A container or test may be enabled or disabled within a -https://www.graalvm.org/reference-manual/native-image/[GraalVM native image] via the -`{EnabledInNativeImage}` and `{DisabledInNativeImage}` annotations. These annotations are -typically used when running tests within a native image using the Gradle and Maven -plug-ins from the GraalVM https://graalvm.github.io/native-build-tools/latest/[Native -Build Tools] project. - -[source,java,indent=0] ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_native] ----- - -[[writing-tests-conditional-execution-system-properties]] -==== System Property Conditions - -A container or test may be enabled or disabled based on the value of the `named` JVM -system property via the `{EnabledIfSystemProperty}` and `{DisabledIfSystemProperty}` -annotations. The value supplied via the `matches` attribute will be interpreted as a -regular expression. - -[source,java,indent=0] ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_system_property] ----- - -[TIP] -==== -`{EnabledIfSystemProperty}` and `{DisabledIfSystemProperty}` are _repeatable annotations_. -Consequently, these annotations may be declared multiple times on a test interface, test -class, or test method. Specifically, these annotations will be found if they are directly -present, indirectly present, or meta-present on a given element. -==== - -[[writing-tests-conditional-execution-environment-variables]] -==== Environment Variable Conditions - -A container or test may be enabled or disabled based on the value of the `named` -environment variable from the underlying operating system via the -`{EnabledIfEnvironmentVariable}` and `{DisabledIfEnvironmentVariable}` annotations. The -value supplied via the `matches` attribute will be interpreted as a regular expression. - -[source,java,indent=0] ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_environment_variable] ----- - -[TIP] -==== -`{EnabledIfEnvironmentVariable}` and `{DisabledIfEnvironmentVariable}` are _repeatable -annotations_. Consequently, these annotations may be declared multiple times on a test -interface, test class, or test method. Specifically, these annotations will be found if -they are directly present, indirectly present, or meta-present on a given element. -==== - -[[writing-tests-conditional-execution-custom]] -==== Custom Conditions - -As an alternative to implementing an <>, a -container or test may be enabled or disabled based on a _condition method_ configured via -the `{EnabledIf}` and `{DisabledIf}` annotations. A condition method must have a `boolean` -return type and may accept either no arguments or a single `ExtensionContext` argument. - -The following test class demonstrates how to configure a local method named -`customCondition` via `@EnabledIf` and `@DisabledIf`. - -[source,java,indent=0] ----- -include::{testDir}/example/ConditionalTestExecutionDemo.java[tags=user_guide_custom] ----- - -Alternatively, the condition method can be located outside the test class. In this case, -it must be referenced by its _fully qualified name_ as demonstrated in the following -example. - -[source,java,indent=0] ----- -package example; - -include::{testDir}/example/ExternalCustomConditionDemo.java[tags=user_guide_external_custom_condition] ----- - -[NOTE] -==== -There are several cases where a condition method would need to be `static`: - -- when `@EnabledIf` or `@DisabledIf` is used at class level -- when `@EnabledIf` or `@DisabledIf` is used on a `@ParameterizedTest` or a - `@TestTemplate` method -- when the condition method is located in an external class - -In any other case, you can use either static methods or instance methods as condition -methods. -==== - -[TIP] -==== -It is often the case that you can use an existing static method in a utility class as a -custom condition. - -For example, `java.awt.GraphicsEnvironment` provides a `public static boolean isHeadless()` -method that can be used to determine if the current environment does not support a -graphical display. Thus, if you have a test that depends on graphical support you can -disable it when such support is unavailable as follows. - -[source,java,indent=0] ----- -@DisabledIf(value = "java.awt.GraphicsEnvironment#isHeadless", - disabledReason = "headless environment") ----- -==== - -[[writing-tests-tagging-and-filtering]] -=== Tagging and Filtering - -Test classes and methods can be tagged via the `@Tag` annotation. Those tags can later be -used to filter <>. Please refer to the -<> section for more information about tag support in the JUnit -Platform. - -[source,java,indent=0] ----- -include::{testDir}/example/TaggingDemo.java[tags=user_guide] ----- - -TIP: See <> for examples demonstrating how to create -custom annotations for tags. - -[[writing-tests-test-execution-order]] -=== Test Execution Order - -By default, test classes and methods will be ordered using an algorithm that is -deterministic but intentionally nonobvious. This ensures that subsequent runs of a test -suite execute test classes and test methods in the same order, thereby allowing for -repeatable builds. - -NOTE: See <> for a definition of _test method_ and _test class_. - -[[writing-tests-test-execution-order-methods]] -==== Method Order - -Although true _unit tests_ typically should not rely on the order in which they are -executed, there are times when it is necessary to enforce a specific test method execution -order -- for example, when writing _integration tests_ or _functional tests_ where the -sequence of the tests is important, especially in conjunction with -`@TestInstance(Lifecycle.PER_CLASS)`. - -To control the order in which test methods are executed, annotate your test class or test -interface with `{TestMethodOrder}` and specify the desired `{MethodOrderer}` -implementation. You can implement your own custom `MethodOrderer` or use one of the -following built-in `MethodOrderer` implementations. - -* `{MethodOrderer_DisplayName}`: sorts test methods _alphanumerically_ based on their - display names (see <>) -* `{MethodOrderer_MethodName}`: sorts test methods _alphanumerically_ based on their names - and formal parameter lists -* `{MethodOrderer_OrderAnnotation}`: sorts test methods _numerically_ based on values - specified via the `{Order}` annotation -* `{MethodOrderer_Random}`: orders test methods _pseudo-randomly_ and supports - configuration of a custom _seed_ - -The `MethodOrderer` configured on a test class is inherited by the `@Nested` test classes -it contains, recursively. If you want to avoid that a `@Nested` test class uses the same -`MethodOrderer` as its enclosing class, you can specify `{MethodOrderer_Default}` together -with `{TestMethodOrder}`. - -NOTE: See also: <> - -The following example demonstrates how to guarantee that test methods are executed in the -order specified via the `@Order` annotation. - -[source,java,indent=0] ----- -include::{testDir}/example/OrderedTestsDemo.java[tags=user_guide] ----- - -[[writing-tests-test-execution-order-methods-default]] -===== Setting the Default Method Orderer - -You can use the `junit.jupiter.testmethod.order.default` <> to specify the fully qualified class name of the -`{MethodOrderer}` you would like to use by default. Just like for the orderer configured -via the `{TestMethodOrder}` annotation, the supplied class has to implement the -`MethodOrderer` interface. The default orderer will be used for all tests unless the -`@TestMethodOrder` annotation is present on an enclosing test class or test interface. - -For example, to use the `{MethodOrderer_OrderAnnotation}` method orderer by default, you -should set the configuration parameter to the corresponding fully qualified class name -(e.g., in `src/test/resources/junit-platform.properties`): - -[source,properties,indent=0] ----- -junit.jupiter.testmethod.order.default = \ - org.junit.jupiter.api.MethodOrderer$OrderAnnotation ----- - -Similarly, you can specify the fully qualified name of any custom class that implements -`MethodOrderer`. - -[[writing-tests-test-execution-order-classes]] -==== Class Order - -Although test classes typically should not rely on the order in which they are executed, -there are times when it is desirable to enforce a specific test class execution order. You -may wish to execute test classes in a random order to ensure there are no accidental -dependencies between test classes, or you may wish to order test classes to optimize build -time as outlined in the following scenarios. - -* Run previously failing tests and faster tests first: "fail fast" mode -* With parallel execution enabled, schedule longer tests first: "shortest test plan - execution duration" mode -* Various other use cases - -To configure test class execution order _globally_ for the entire test suite, use the -`junit.jupiter.testclass.order.default` <> to specify the fully qualified class name of the `{ClassOrderer}` you would -like to use. The supplied class must implement the `ClassOrderer` interface. - -You can implement your own custom `ClassOrderer` or use one of the following built-in -`ClassOrderer` implementations. - -* `{ClassOrderer_ClassName}`: sorts test classes _alphanumerically_ based on their fully - qualified class names -* `{ClassOrderer_DisplayName}`: sorts test classes _alphanumerically_ based on their - display names (see <>) -* `{ClassOrderer_OrderAnnotation}`: sorts test classes _numerically_ based on values - specified via the `{Order}` annotation -* `{ClassOrderer_Random}`: orders test classes _pseudo-randomly_ and supports - configuration of a custom _seed_ - -For example, for the `@Order` annotation to be honored on _test classes_, you should -configure the `{ClassOrderer_OrderAnnotation}` class orderer using the configuration -parameter with the corresponding fully qualified class name (e.g., in -`src/test/resources/junit-platform.properties`): - -[source,properties,indent=0] ----- -junit.jupiter.testclass.order.default = \ - org.junit.jupiter.api.ClassOrderer$OrderAnnotation ----- - -The configured `ClassOrderer` will be applied to all top-level test classes (including -`static` nested test classes) and `@Nested` test classes. - -NOTE: Top-level test classes will be ordered relative to each other; whereas, `@Nested` -test classes will be ordered relative to other `@Nested` test classes sharing the same -_enclosing class_. - -To configure test class execution order _locally_ for `@Nested` test classes, declare the -`{TestClassOrder}` annotation on the enclosing class for the `@Nested` test classes you -want to order, and supply a class reference to the `ClassOrderer` implementation you would -like to use directly in the `@TestClassOrder` annotation. The configured `ClassOrderer` -will be applied recursively to `@Nested` test classes and their `@Nested` test classes. -If you want to avoid that a `@Nested` test class uses the same `ClassOrderer` as its -enclosing class, you can specify `{ClassOrderer_Default}` together with `@TestClassOrder`. -Note that a local `@TestClassOrder` declaration always overrides an inherited -`@TestClassOrder` declaration or a `ClassOrderer` configured globally via the -`junit.jupiter.testclass.order.default` configuration parameter. - -The following example demonstrates how to guarantee that `@Nested` test classes are -executed in the order specified via the `@Order` annotation. - -[source,java,indent=0] ----- -include::{testDir}/example/OrderedNestedTestClassesDemo.java[tags=user_guide] ----- - -[[writing-tests-test-instance-lifecycle]] -=== Test Instance Lifecycle - -In order to allow individual test methods to be executed in isolation and to avoid -unexpected side effects due to mutable test instance state, JUnit creates a new instance -of each test class before executing each _test method_ (see -<>). This "per-method" test instance lifecycle is the default -behavior in JUnit Jupiter and is analogous to all previous versions of JUnit. - -NOTE: Please note that the test class will still be instantiated if a given _test method_ -is _disabled_ via a <> (e.g., `@Disabled`, -`@DisabledOnOs`, etc.) even when the "per-method" test instance lifecycle mode is active. - -If you would prefer that JUnit Jupiter execute all test methods on the same test -instance, annotate your test class with `@TestInstance(Lifecycle.PER_CLASS)`. When using -this mode, a new test instance will be created once per test class. Thus, if your test -methods rely on state stored in instance variables, you may need to reset that state in -`@BeforeEach` or `@AfterEach` methods. - -The "per-class" mode has some additional benefits over the default "per-method" mode. -Specifically, with the "per-class" mode it becomes possible to declare `@BeforeAll` and -`@AfterAll` on non-static methods as well as on interface `default` methods. - -If you are authoring tests using the Kotlin programming language, you may also find it -easier to implement non-static `@BeforeAll` and `@AfterAll` lifecycle methods as well as -`@MethodSource` factory methods by switching to the "per-class" test instance lifecycle -mode. - -[[writing-tests-test-instance-lifecycle-changing-default]] -==== Changing the Default Test Instance Lifecycle - -If a test class or test interface is not annotated with `@TestInstance`, JUnit Jupiter -will use a _default_ lifecycle mode. The standard _default_ mode is `PER_METHOD`; -however, it is possible to change the _default_ for the execution of an entire test plan. -To change the default test instance lifecycle mode, set the -`junit.jupiter.testinstance.lifecycle.default` _configuration parameter_ to the name of -an enum constant defined in `TestInstance.Lifecycle`, ignoring case. This can be supplied -as a JVM system property, as a _configuration parameter_ in the -`LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform -configuration file (see <> for details). - -For example, to set the default test instance lifecycle mode to `Lifecycle.PER_CLASS`, -you can start your JVM with the following system property. - -`-Djunit.jupiter.testinstance.lifecycle.default=per_class` - -Note, however, that setting the default test instance lifecycle mode via the JUnit -Platform configuration file is a more robust solution since the configuration file can be -checked into a version control system along with your project and can therefore be used -within IDEs and your build software. - -To set the default test instance lifecycle mode to `Lifecycle.PER_CLASS` via the JUnit -Platform configuration file, create a file named `junit-platform.properties` in the root -of the class path (e.g., `src/test/resources`) with the following content. - -`junit.jupiter.testinstance.lifecycle.default = per_class` - -WARNING: Changing the _default_ test instance lifecycle mode can lead to unpredictable -results and fragile builds if not applied consistently. For example, if the build -configures "per-class" semantics as the default but tests in the IDE are executed using -"per-method" semantics, that can make it difficult to debug errors that occur on the -build server. It is therefore recommended to change the default in the JUnit Platform -configuration file instead of via a JVM system property. - -[[writing-tests-nested]] -=== Nested Tests - -`@Nested` tests give the test writer more capabilities to express the relationship among -several groups of tests. Such nested tests make use of Java's nested classes and -facilitate hierarchical thinking about the test structure. Here's an elaborate example, -both as source code and as a screenshot of the execution within an IDE. - -[source,java,indent=0] -.Nested test suite for testing a stack ----- -include::{testDir}/example/TestingAStackDemo.java[tags=user_guide] ----- - -When executing this example in an IDE, the test execution tree in the GUI will look -similar to the following image. - -image::writing-tests_nested_test_ide.png[caption='',title='Executing a nested test in an IDE'] - -In this example, preconditions from outer tests are used in inner tests by defining -hierarchical lifecycle methods for the setup code. For example, `createNewStack()` is a -`@BeforeEach` lifecycle method that is used in the test class in which it is defined and -in all levels in the nesting tree below the class in which it is defined. - -The fact that setup code from outer tests is run before inner tests are executed gives you -the ability to run all tests independently. You can even run inner tests alone without -running the outer tests, because the setup code from the outer tests is always executed. - -NOTE: _Only non-static nested classes_ (i.e. _inner classes_) can serve as `@Nested` test -classes. Nesting can be arbitrarily deep, and those inner classes are subject to full -lifecycle support, including `@BeforeAll` and `@AfterAll` methods on each level. - -[[writing-tests-nested-interoperability]] -==== Interoperability - -`@Nested` may be combined with -<> in which case the nested test -class is parameterized. - -The following example illustrates how to combine `@Nested` with `@ParameterizedClass` and -`@ParameterizedTest`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedClassDemo.java[tags=nested] ----- - -Executing the above test class yields the following output: - -.... -FruitTests ✔ -├─ [1] fruit = "apple" ✔ -│ └─ QuantityTests ✔ -│ ├─ [1] quantity = 23 ✔ -│ │ └─ test(Duration) ✔ -│ │ ├─ [1] duration = "PT1H" ✔ -│ │ └─ [2] duration = "PT2H" ✔ -│ └─ [2] quantity = 42 ✔ -│ └─ test(Duration) ✔ -│ ├─ [1] duration = "PT1H" ✔ -│ └─ [2] duration = "PT2H" ✔ -└─ [2] fruit = "banana" ✔ - └─ QuantityTests ✔ - ├─ [1] quantity = 23 ✔ - │ └─ test(Duration) ✔ - │ ├─ [1] duration = "PT1H" ✔ - │ └─ [2] duration = "PT2H" ✔ - └─ [2] quantity = 42 ✔ - └─ test(Duration) ✔ - ├─ [1] duration = "PT1H" ✔ - └─ [2] duration = "PT2H" ✔ -.... - -[[writing-tests-dependency-injection]] -=== Dependency Injection for Constructors and Methods - -In all prior JUnit versions, test constructors or methods were not allowed to have -parameters (at least not with the standard `Runner` implementations). As one of the major -changes in JUnit Jupiter, both test constructors and methods are now permitted to have -parameters. This allows for greater flexibility and enables _Dependency Injection_ for -constructors and methods. - -`{ParameterResolver}` defines the API for test extensions that wish to _dynamically_ -resolve parameters at runtime. If a _test class_ constructor, a _test method_, or a -_lifecycle method_ (see <>) accepts a parameter, the parameter -must be resolved at runtime by a registered `ParameterResolver`. - -There are currently three built-in resolvers that are registered automatically. - -* `{TestInfoParameterResolver}`: if a constructor or method parameter is of type - `{TestInfo}`, the `TestInfoParameterResolver` will supply an instance of `TestInfo` - corresponding to the current container or test as the value for the parameter. The - `TestInfo` can then be used to retrieve information about the current container or test - such as the display name, the test class, the test method, and associated tags. The - display name is either a technical name, such as the name of the test class or test - method, or a custom name configured via `@DisplayName`. -+ -`{TestInfo}` acts as a drop-in replacement for the `TestName` rule from JUnit 4. The -following demonstrates how to have `TestInfo` injected into a `@BeforeAll` method, test -class constructor, `@BeforeEach` method, and `@Test` method. - -[source,java,indent=0] ----- -include::{testDir}/example/TestInfoDemo.java[tags=user_guide] ----- - -* `{RepetitionExtension}`: if a method parameter in a `@RepeatedTest`, `@BeforeEach`, or - `@AfterEach` method is of type `{RepetitionInfo}`, the `RepetitionExtension` will supply - an instance of `RepetitionInfo`. `RepetitionInfo` can then be used to retrieve - information about the current repetition, the total number of repetitions, the number of - repetitions that have failed, and the failure threshold for the corresponding - `@RepeatedTest`. Note, however, that `RepetitionExtension` is not registered outside the - context of a `@RepeatedTest`. See <>. - -* `{TestReporterParameterResolver}`: if a constructor or method parameter is of type - `{TestReporter}`, the `TestReporterParameterResolver` will supply an instance of - `TestReporter`. The `TestReporter` can be used to publish additional data about the - current test run or attach files to it. The data can be consumed in a - `{TestExecutionListener}` via the `reportingEntryPublished()` or `fileEntryPublished()` - method, respectively. This allows them to be viewed in IDEs or included in reports. -+ -In JUnit Jupiter you should use `TestReporter` where you used to print information to -`stdout` or `stderr` in JUnit 4. Some IDEs print report entries to `stdout` or display -them in the user interface for test results. - -[source,java,indent=0] ----- -include::{testDir}/example/TestReporterDemo.java[tags=user_guide] ----- - -NOTE: Other parameter resolvers must be explicitly enabled by registering appropriate -<> via `@ExtendWith`. - -Check out the `{RandomParametersExtension}` for an example of a custom -`{ParameterResolver}`. While not intended to be production-ready, it demonstrates the -simplicity and expressiveness of both the extension model and the parameter resolution -process. `MyRandomParametersTest` demonstrates how to inject random values into `@Test` -methods. - -[source,java,indent=0] ----- -@ExtendWith(RandomParametersExtension.class) -class MyRandomParametersTest { - - @Test - void injectsInteger(@Random int i, @Random int j) { - assertNotEquals(i, j); - } - - @Test - void injectsDouble(@Random double d) { - assertEquals(0.0, d, 1.0); - } - -} ----- - -For real-world use cases, check out the source code for the `{MockitoExtension}` and the -`{SpringExtension}`. - -When the type of the parameter to inject is the only condition for your -`{ParameterResolver}`, you can use the generic `{TypeBasedParameterResolver}` base class. -The `supportsParameters` method is implemented behind the scenes and supports -parameterized types. - -[[writing-tests-test-interfaces-and-default-methods]] -=== Test Interfaces and Default Methods - -JUnit Jupiter allows `@Test`, `@RepeatedTest`, `@ParameterizedTest`, `@TestFactory`, -`@TestTemplate`, `@BeforeEach`, and `@AfterEach` to be declared on interface `default` -methods. `@BeforeAll` and `@AfterAll` can either be declared on `static` methods in a -test interface or on interface `default` methods _if_ the test interface or test class is -annotated with `@TestInstance(Lifecycle.PER_CLASS)` (see -<>). Here are some examples. - -[source,java] ----- -include::{testDir}/example/testinterface/TestLifecycleLogger.java[tags=user_guide] ----- - -[source,java] ----- -include::{testDir}/example/testinterface/TestInterfaceDynamicTestsDemo.java[tags=user_guide] ----- - -`@ExtendWith` and `@Tag` can be declared on a test interface so that classes that -implement the interface automatically inherit its tags and extensions. See -<> for the source code of the -<>. - -[source,java] ----- -include::{testDir}/example/testinterface/TimeExecutionLogger.java[tags=user_guide] ----- - -In your test class you can then implement these test interfaces to have them applied. - -[source,java] ----- -include::{testDir}/example/testinterface/TestInterfaceDemo.java[tags=user_guide] ----- - -Running the `TestInterfaceDemo` results in output similar to the following: - -.... -INFO example.TestLifecycleLogger - Before all tests -INFO example.TestLifecycleLogger - About to execute [dynamicTestsForPalindromes()] -INFO example.TimingExtension - Method [dynamicTestsForPalindromes] took 19 ms. -INFO example.TestLifecycleLogger - Finished executing [dynamicTestsForPalindromes()] -INFO example.TestLifecycleLogger - About to execute [isEqualValue()] -INFO example.TimingExtension - Method [isEqualValue] took 1 ms. -INFO example.TestLifecycleLogger - Finished executing [isEqualValue()] -INFO example.TestLifecycleLogger - After all tests -.... - -Another possible application of this feature is to write tests for interface contracts. -For example, you can write tests for how implementations of `Object.equals` or -`Comparable.compareTo` should behave as follows. - -[source,java] ----- -include::{testDir}/example/defaultmethods/Testable.java[tags=user_guide] ----- - -[source,java] ----- -include::{testDir}/example/defaultmethods/EqualsContract.java[tags=user_guide] ----- - -[source,java] ----- -include::{testDir}/example/defaultmethods/ComparableContract.java[tags=user_guide] ----- - -In your test class you can then implement both contract interfaces thereby inheriting the -corresponding tests. Of course you'll have to implement the abstract methods. - -[source,java] ----- -include::{testDir}/example/defaultmethods/StringTests.java[tags=user_guide] ----- - -NOTE: The above tests are merely meant as examples and therefore not complete. - - -[[writing-tests-repeated-tests]] -=== Repeated Tests - -JUnit Jupiter provides the ability to repeat a test a specified number of times by -annotating a method with `@RepeatedTest` and specifying the total number of repetitions -desired. Each invocation of a repeated test behaves like the execution of a regular -`@Test` method with full support for the same lifecycle callbacks and extensions. - -The following example demonstrates how to declare a test named `repeatedTest()` that -will be automatically repeated 10 times. - -[source,java] ----- -@RepeatedTest(10) -void repeatedTest() { - // ... -} ----- - -`@RepeatedTest` can be configured with a failure threshold which signifies the number of -failures after which remaining repetitions will be automatically skipped. Set the -`failureThreshold` attribute to a positive number less than the total number of -repetitions in order to skip the invocations of remaining repetitions after the specified -number of failures has been encountered. - -For example, if you are using `@RepeatedTest` to repeatedly invoke a test that you suspect -to be _flaky_, a single failure is sufficient to demonstrate that the test is flaky, and -there is no need to invoke the remaining repetitions. To support that specific use case, -set `failureThreshold = 1`. You can alternatively set the threshold to a number greater -than 1 depending on your use case. - -By default, the `failureThreshold` attribute is set to `Integer.MAX_VALUE`, signaling that -no failure threshold will be applied, which effectively means that the specified number of -repetitions will be invoked regardless of whether any repetitions fail. - -WARNING: If the repetitions of a `@RepeatedTest` method are executed in parallel, no -guarantees can be made regarding the failure threshold. It is therefore recommended that a -`@RepeatedTest` method be annotated with `@Execution(SAME_THREAD)` when parallel execution -is configured. See <> for further details. - -In addition to specifying the number of repetitions and failure threshold, a custom -display name can be configured for each repetition via the `name` attribute of the -`@RepeatedTest` annotation. Furthermore, the display name can be a pattern composed of a -combination of static text and dynamic placeholders. The following placeholders are -currently supported. - -- `+{displayName}+`: display name of the `@RepeatedTest` method -- `+{currentRepetition}+`: the current repetition count -- `+{totalRepetitions}+`: the total number of repetitions - -The default display name for a given repetition is generated based on the following -pattern: `"repetition +{currentRepetition}+ of +{totalRepetitions}+"`.Thus, the display -names for individual repetitions of the previous `repeatedTest()` example would be: -`repetition 1 of 10`, `repetition 2 of 10`, etc.If you would like the display name of -the `@RepeatedTest` method included in the name of each repetition, you can define your -own custom pattern or use the predefined `RepeatedTest.LONG_DISPLAY_NAME` pattern.The -latter is equal to `"+{displayName}+ :: repetition +{currentRepetition}+ of -+{totalRepetitions}+"` which results in display names for individual repetitions like -`repeatedTest() :: repetition 1 of 10`, `repeatedTest() :: repetition 2 of 10`, etc. - -In order to retrieve information about the current repetition, the total number of -repetitions, the number of repetitions that have failed, and the failure threshold, a -developer can choose to have an instance of `{RepetitionInfo}` injected into a -`@RepeatedTest`, `@BeforeEach`, or `@AfterEach` method. - -[[writing-tests-repeated-tests-examples]] -==== Repeated Test Examples - -The `RepeatedTestsDemo` class at the end of this section demonstrates several examples of -repeated tests. - -The `repeatedTest()` method is identical to the example from the previous section; whereas, -`repeatedTestWithRepetitionInfo()` demonstrates how to have an instance of -`RepetitionInfo` injected into a test to access the total number of repetitions for the -current repeated test. - -`repeatedTestWithFailureThreshold()` demonstrates how to set a failure threshold and -simulates an unexpected failure for every second repetition.The resulting behavior can be -viewed in the `ConsoleLauncher` output at the end of this section. - -The next two methods demonstrate how to include a custom `@DisplayName` for the -`@RepeatedTest` method in the display name of each repetition. `customDisplayName()` -combines a custom display name with a custom pattern and then uses `TestInfo` to verify -the format of the generated display name. `Repeat!` is the `+{displayName}+` which comes -from the `@DisplayName` declaration, and `1/1` comes from -`+{currentRepetition}+/+{totalRepetitions}+`.In contrast, -`customDisplayNameWithLongPattern()` uses the aforementioned predefined -`RepeatedTest.LONG_DISPLAY_NAME` pattern. - -`repeatedTestInGerman()` demonstrates the ability to translate display names of repeated -tests into foreign languages -- in this case German, resulting in names for individual -repetitions such as: `Wiederholung 1 von 5`, `Wiederholung 2 von 5`, etc. - -Since the `beforeEach()` method is annotated with `@BeforeEach` it will get executed -before each repetition of each repeated test. By having the `TestInfo` and -`RepetitionInfo` injected into the method, we see that it's possible to obtain -information about the currently executing repeated test. Executing `RepeatedTestsDemo` -with the `INFO` log level enabled results in the following output. - -.... -INFO: About to execute repetition 1 of 10 for repeatedTest -INFO: About to execute repetition 2 of 10 for repeatedTest -INFO: About to execute repetition 3 of 10 for repeatedTest -INFO: About to execute repetition 4 of 10 for repeatedTest -INFO: About to execute repetition 5 of 10 for repeatedTest -INFO: About to execute repetition 6 of 10 for repeatedTest -INFO: About to execute repetition 7 of 10 for repeatedTest -INFO: About to execute repetition 8 of 10 for repeatedTest -INFO: About to execute repetition 9 of 10 for repeatedTest -INFO: About to execute repetition 10 of 10 for repeatedTest -INFO: About to execute repetition 1 of 5 for repeatedTestWithRepetitionInfo -INFO: About to execute repetition 2 of 5 for repeatedTestWithRepetitionInfo -INFO: About to execute repetition 3 of 5 for repeatedTestWithRepetitionInfo -INFO: About to execute repetition 4 of 5 for repeatedTestWithRepetitionInfo -INFO: About to execute repetition 5 of 5 for repeatedTestWithRepetitionInfo -INFO: About to execute repetition 1 of 8 for repeatedTestWithFailureThreshold -INFO: About to execute repetition 2 of 8 for repeatedTestWithFailureThreshold -INFO: About to execute repetition 3 of 8 for repeatedTestWithFailureThreshold -INFO: About to execute repetition 4 of 8 for repeatedTestWithFailureThreshold -INFO: About to execute repetition 1 of 1 for customDisplayName -INFO: About to execute repetition 1 of 1 for customDisplayNameWithLongPattern -INFO: About to execute repetition 1 of 5 for repeatedTestInGerman -INFO: About to execute repetition 2 of 5 for repeatedTestInGerman -INFO: About to execute repetition 3 of 5 for repeatedTestInGerman -INFO: About to execute repetition 4 of 5 for repeatedTestInGerman -INFO: About to execute repetition 5 of 5 for repeatedTestInGerman -.... - -[source,java] ----- -include::{testDir}/example/RepeatedTestsDemo.java[tags=user_guide] ----- - -When using the `ConsoleLauncher` with the unicode theme enabled, execution of -`RepeatedTestsDemo` results in the following output to the console. - -.... -├─ RepeatedTestsDemo ✔ -│ ├─ repeatedTest() ✔ -│ │ ├─ repetition 1 of 10 ✔ -│ │ ├─ repetition 2 of 10 ✔ -│ │ ├─ repetition 3 of 10 ✔ -│ │ ├─ repetition 4 of 10 ✔ -│ │ ├─ repetition 5 of 10 ✔ -│ │ ├─ repetition 6 of 10 ✔ -│ │ ├─ repetition 7 of 10 ✔ -│ │ ├─ repetition 8 of 10 ✔ -│ │ ├─ repetition 9 of 10 ✔ -│ │ └─ repetition 10 of 10 ✔ -│ ├─ repeatedTestWithRepetitionInfo(RepetitionInfo) ✔ -│ │ ├─ repetition 1 of 5 ✔ -│ │ ├─ repetition 2 of 5 ✔ -│ │ ├─ repetition 3 of 5 ✔ -│ │ ├─ repetition 4 of 5 ✔ -│ │ └─ repetition 5 of 5 ✔ -│ ├─ repeatedTestWithFailureThreshold(RepetitionInfo) ✔ -│ │ ├─ repetition 1 of 8 ✔ -│ │ ├─ repetition 2 of 8 ✘ Boom! -│ │ ├─ repetition 3 of 8 ✔ -│ │ ├─ repetition 4 of 8 ✘ Boom! -│ │ ├─ repetition 5 of 8 ↷ Failure threshold [2] exceeded -│ │ ├─ repetition 6 of 8 ↷ Failure threshold [2] exceeded -│ │ ├─ repetition 7 of 8 ↷ Failure threshold [2] exceeded -│ │ └─ repetition 8 of 8 ↷ Failure threshold [2] exceeded -│ ├─ Repeat! ✔ -│ │ └─ Repeat! 1/1 ✔ -│ ├─ Details... ✔ -│ │ └─ Details... :: repetition 1 of 1 ✔ -│ └─ repeatedTestInGerman() ✔ -│ ├─ Wiederholung 1 von 5 ✔ -│ ├─ Wiederholung 2 von 5 ✔ -│ ├─ Wiederholung 3 von 5 ✔ -│ ├─ Wiederholung 4 von 5 ✔ -│ └─ Wiederholung 5 von 5 ✔ -.... - - -[[writing-tests-parameterized-tests]] -=== Parameterized Classes and Tests - -_Parameterized tests_ make it possible to run a test method multiple times with different -arguments. They are declared just like regular `@Test` methods but use the -`{ParameterizedTest}` annotation instead. - -_Parameterized classes_ make it possible to run _all_ tests in a test class, including -<>, multiple times with different arguments. They are declared just -like regular test classes and may contain any supported test method type (including -`@ParameterizedTest`) but annotated with the `{ParameterizedClass}` annotation. - -WARNING: _Parameterized classes_ are currently an _experimental_ feature. You're invited -to give it a try and provide feedback to the JUnit team so they can improve and eventually -<> this feature. - -Regardless of whether you are parameterizing a test method or a test class, you must -declare at least one <> that will -provide the arguments for each invocation and then -<> the arguments in the -parameterized method or class, respectively. - -The following example demonstrates a parameterized test that uses the `@ValueSource` -annotation to specify a `String` array as the source of arguments. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=first_example] ----- - -When executing the above parameterized test method, each invocation will be reported -separately. For instance, the `ConsoleLauncher` will print output similar to the -following. - -.... -palindromes(String) ✔ -├─ [1] candidate = "racecar" ✔ -├─ [2] candidate = "radar" ✔ -└─ [3] candidate = "able was I ere I saw elba" ✔ -.... - -The same `@ValueSource` annotation can be used to specify the source of arguments for a -`@ParameterizedClass`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedClassDemo.java[tags=first_example] ----- - -When executing the above parameterized test class, each invocation will be reported -separately. For instance, the `ConsoleLauncher` will print output similar to the -following. - -.... -PalindromeTests ✔ -├─ [1] candidate = "racecar" ✔ -│ ├─ palindrome() ✔ -│ └─ reversePalindrome() ✔ -├─ [2] candidate = "radar" ✔ -│ ├─ palindrome() ✔ -│ └─ reversePalindrome() ✔ -└─ [3] candidate = "able was I ere I saw elba" ✔ - ├─ palindrome() ✔ - └─ reversePalindrome() ✔ -.... - -[[writing-tests-parameterized-tests-setup]] -==== Required Setup - -In order to use parameterized classes or tests you need to add a dependency on the -`junit-jupiter-params` artifact. Please refer to <> for details. - -[[writing-tests-parameterized-tests-consuming-arguments]] -==== Consuming Arguments - -[[writing-tests-parameterized-tests-consuming-arguments-methods]] -===== Parameterized Tests - -Parameterized test methods _consume_ arguments directly from the configured source (see -<>) following a one-to-one correlation between -argument source index and method parameter index (see examples in -<>). However, a parameterized test -method may also choose to _aggregate_ arguments from the source into a single object -passed to the method (see <>). -Additional arguments may also be provided by a `ParameterResolver` (e.g., to obtain an -instance of `TestInfo`, `TestReporter`, etc.). Specifically, a parameterized test method -must declare formal parameters according to the following rules. - -* Zero or more _indexed parameters_ must be declared first. -* Zero or more _aggregators_ must be declared next. -* Zero or more arguments supplied by a `ParameterResolver` must be declared last. - -In this context, an _indexed parameter_ is an argument for a given index in the -`{Arguments}` provided by an `{ArgumentsProvider}` that is passed as an argument to the -parameterized method at the same index in the method's formal parameter list. An -_aggregator_ is any parameter of type `{ArgumentsAccessor}` or any parameter annotated -with `{AggregateWith}`. - -[[writing-tests-parameterized-tests-consuming-arguments-classes]] -===== Parameterized Classes - -Parameterized classes _consume_ arguments directly from the configured source (see -<>); either via their unique constructor or via -field injection. If a `{Parameter}`-annotated field is declared in the parameterized class -or one of its superclasses, field injection will be used. Otherwise, constructor injection -will be used. - -[[writing-tests-parameterized-tests-consuming-arguments-constructor-injection]] -====== Constructor Injection - -WARNING: Constructor injection can only be used with the (default) `PER_METHOD` -<> mode. Please use -<> -with the `PER_CLASS` mode instead. - -For constructor injection, the same rules apply as defined for -<> -above. In the following example, two arguments are injected into the constructor of the -test class. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedClassDemo.java[tags=constructor_injection] ----- - -You may use _records_ to implement parameterized classes that avoid the boilerplate code -of declaring a test class constructor. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedRecordDemo.java[tags=example] ----- - -[[writing-tests-parameterized-tests-consuming-arguments-field-injection]] -====== Field Injection - -For field injection, the following rules apply for fields annotated with `@Parameter`. - -* Zero or more _indexed parameters_ may be declared; each must have a unique index - specified in its `@Parameter(index)` annotation. The index may be omitted if there is - only one indexed parameter. If there are at least two indexed parameter declarations, - there must be declarations for all indexes from 0 to the largest declared index. -* Zero or more _aggregators_ may be declared; each without specifying an index in its - `@Parameter` annotation. -* Zero or more other fields may be declared as usual as long as they're not annotated with - `@Parameter`. - -In this context, an _indexed parameter_ is an argument for a given index in the -`{Arguments}` provided by an `{ArgumentsProvider}` that is injected into a field annotated -with `@Parameter(index)`. An _aggregator_ is any `@Parameter`-annotated field of type -{ArgumentsAccessor} or any field annotated with {AggregateWith}. - -The following example demonstrates how to use field injection to consume multiple -arguments in a parameterized class. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedClassDemo.java[tags=field_injection] ----- - -If field injection is used, no constructor parameters will be resolved with arguments from -the source. Other <> -may resolve constructor parameters as usual, though. - -[[writing-tests-parameterized-tests-consuming-arguments-lifecycle-method]] -====== Lifecycle Methods - -`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` can also -be used to consume arguments if their `injectArguments` attribute is set to `true` (the -default). If so, their method signatures must follow the same rules apply as defined for -<> and -additionally use the same parameter types as the _indexed parameters_ of the parameterized -test class. Please refer to the Javadoc of `{BeforeParameterizedClassInvocation}` and -`{AfterParameterizedClassInvocation}` for details and to the -<> section for an -example. - -[NOTE] -.AutoCloseable arguments -==== -Arguments that implement `java.lang.AutoCloseable` (or `java.io.Closeable` which extends -`java.lang.AutoCloseable`) will be automatically closed after the parameterized class or -test invocation. - -To prevent this from happening, set the `autoCloseArguments` attribute in -`@ParameterizedTest` to `false`. Specifically, if an argument that implements -`AutoCloseable` is reused for multiple invocations of the same parameterized class or test -method, you must specify the `autoCloseArguments = false` on the `{ParameterizedClass}` or -`{ParameterizedTest}` annotation to ensure that the argument is not closed between -invocations. -==== - -[[writing-tests-parameterized-tests-consuming-arguments-other-extensions]] -===== Other Extensions - -Other extensions can access the parameters and resolved arguments of a parameterized test -or class by retrieving a `{ParameterInfo}` object from the `{ExtensionContext_Store}`. -Please refer to the Javadoc of `{ParameterInfo}` for details. - -[[writing-tests-parameterized-tests-sources]] -==== Sources of Arguments - -Out of the box, JUnit Jupiter provides quite a few _source_ annotations. Each of the -following subsections provides a brief overview and an example for each of them. Please -refer to the Javadoc in the `{params-provider-package}` package for additional -information. - -TIP: All source annotations in this section are applicable to both `{ParameterizedClass}` -and `{ParameterizedTest}`. For the sake of brevity, the examples in this section will only -show how to use them with `{ParameterizedTest}` methods. - -[[writing-tests-parameterized-tests-sources-ValueSource]] -===== @ValueSource - -`@ValueSource` is one of the simplest possible sources. It lets you specify a single -array of literal values and can only be used for providing a single argument per -parameterized test invocation. - -The following types of literal values are supported by `@ValueSource`. - -- `short` -- `byte` -- `int` -- `long` -- `float` -- `double` -- `char` -- `boolean` -- `java.lang.String` -- `java.lang.Class` - -For example, the following `@ParameterizedTest` method will be invoked three times, with -the values `1`, `2`, and `3` respectively. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ValueSource_example] ----- - -[[writing-tests-parameterized-tests-sources-null-and-empty]] -===== Null and Empty Sources - -In order to check corner cases and verify proper behavior of our software when it is -supplied _bad input_, it can be useful to have `null` and _empty_ values supplied to our -parameterized tests. The following annotations serve as sources of `null` and empty values -for parameterized tests that accept a single argument. - -* `{NullSource}`: provides a single `null` argument to the annotated `@ParameterizedClass` - or `@ParameterizedTest`. - - `@NullSource` cannot be used for a parameter that has a primitive type. -* `{EmptySource}`: provides a single _empty_ argument to the annotated - `@ParameterizedClass` or `@ParameterizedTest` for parameters of the following types: - `java.lang.String`, `java.util.Collection` (and concrete subtypes with a `public` no-arg - constructor), `java.util.List`, `java.util.Set`, `java.util.SortedSet`, - `java.util.NavigableSet`, `java.util.Map` (and concrete subtypes with a `public` no-arg - constructor), `java.util.SortedMap`, `java.util.NavigableMap`, primitive arrays (e.g., - `int[]`, `char[][]`, etc.), object arrays (e.g., `String[]`, `Integer[][]`, etc.). -* `{NullAndEmptySource}`: a _composed annotation_ that combines the functionality of - `@NullSource` and `@EmptySource`. - -If you need to supply multiple varying types of _blank_ strings to a parameterized -class or test, you can achieve that using -<> -- for example, -`@ValueSource(strings = {"{nbsp}", "{nbsp}{nbsp}{nbsp}", "\t", "\n"})`. - -You can also combine `@NullSource`, `@EmptySource`, and `@ValueSource` to test a wider -range of `null`, _empty_, and _blank_ input. The following example demonstrates how to -achieve this for strings. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example1] ----- - -Making use of the composed `@NullAndEmptySource` annotation simplifies the above as -follows. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example2] ----- - -NOTE: Both variants of the `nullEmptyAndBlankStrings(String)` parameterized test method -result in six invocations: 1 for `null`, 1 for the empty string, and 4 for the explicit -blank strings supplied via `@ValueSource`. - -[[writing-tests-parameterized-tests-sources-EnumSource]] -===== @EnumSource - -`@EnumSource` provides a convenient way to use `Enum` constants. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=EnumSource_example] ----- - -The annotation's `value` attribute is optional. When omitted, the declared type of the -first parameter is used. The test will fail if it does not reference an enum type. -Thus, the `value` attribute is required in the above example because the method parameter -is declared as `TemporalUnit`, i.e. the interface implemented by `ChronoUnit`, which isn't -an enum type. Changing the method parameter type to `ChronoUnit` allows you to omit the -explicit enum type from the annotation as follows. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=EnumSource_example_autodetection] ----- - -The annotation provides an optional `names` attribute that lets you specify which -constants shall be used, like in the following example. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=EnumSource_include_example] ----- - -In addition to `names`, you can use the `from` and `to` attributes to specify a range of -constants. The range starts from the constant specified in the `from` attribute and -includes all subsequent constants up to and including the one specified in the `to` -attribute, based on the natural order of the enum constants. - -If `from` and `to` attributes are omitted, they default to the first and last constants -in the enum type, respectively. If all `names`, `from`, and `to` attributes are omitted, -all constants will be used. The following example demonstrates how to specify a range of -constants. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=EnumSource_range_example] ----- - -The `@EnumSource` annotation also provides an optional `mode` attribute that enables -fine-grained control over which constants are passed to the test method. For example, you -can exclude names from the enum constant pool or specify regular expressions as in the -following examples. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=EnumSource_exclude_example] ----- - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=EnumSource_regex_example] ----- - -You can also combine `mode` with the `from`, `to` and `names` attributes to define a -range of constants while excluding specific values from that range as shown below. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=EnumSource_range_exclude_example] ----- - -[[writing-tests-parameterized-tests-sources-MethodSource]] -===== @MethodSource - -`{MethodSource}` allows you to refer to one or more _factory_ methods of the test class -or external classes. - -Factory methods within the test class must be `static` unless the test class is annotated -with `@TestInstance(Lifecycle.PER_CLASS)`; whereas, factory methods in external classes -must always be `static`. - -Each factory method must generate a _stream_ of _arguments_, and each set of arguments -within the stream will be provided as the physical arguments for individual invocations -of the annotated `@ParameterizedClass` or `@ParameterizedTest`. Generally speaking this -translates to a `Stream` of `Arguments` (i.e., `Stream`); however, the actual -concrete return type can take on many forms. In this context, a "stream" is anything that -JUnit can reliably convert into a `Stream`, such as `Stream`, `DoubleStream`, -`LongStream`, `IntStream`, `Collection`, `Iterator`, `Iterable`, an array of objects or -primitives, or any type that provides an `iterator(): Iterator` method (such as, for -example, a `kotlin.sequences.Sequence`). The "arguments" within the stream can be supplied -as an instance of `Arguments`, an array of objects (e.g., `Object[]`), or a single value -if the parameterized class or test method accepts a single argument. - -If the return type is `Stream` or one of the primitive streams, -JUnit will properly close it by calling `BaseStream.close()`, -making it safe to use a resource such as `Files.lines()`. - -If you only need a single parameter, you can return a `Stream` of instances of the -parameter type as demonstrated in the following example. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=simple_MethodSource_example] ----- - -For a `@ParameterizedClass`, providing a factory method name via `@MethodSource` is -mandatory. For a `@ParameterizedTest`, if you do not explicitly provide a factory method -name, JUnit Jupiter will search for a _factory_ method with the same name as the current -`@ParameterizedTest` method by convention. This is demonstrated in the following example. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=simple_MethodSource_without_value_example] ----- - -Streams for primitive types (`DoubleStream`, `IntStream`, and `LongStream`) are also -supported as demonstrated by the following example. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=primitive_MethodSource_example] ----- - -If a parameterized class or test method declares multiple parameters, you need to return a -collection, stream, or array of `Arguments` instances or object arrays as shown below (see -the Javadoc for `{MethodSource}` for further details on supported return types). Note that -`arguments(Object...)` is a static factory method defined in the `Arguments` interface. In -addition, `Arguments.of(Object...)` may be used as an alternative to -`arguments(Object...)`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=multi_arg_MethodSource_example] ----- - -An external, `static` _factory_ method can be referenced by providing its _fully qualified -method name_ as demonstrated in the following example. - -[source,java,indent=0] ----- -package example; - -include::{testDir}/example/ExternalMethodSourceDemo.java[tags=external_MethodSource_example] ----- - -Factory methods can declare parameters, which will be provided by registered -implementations of the `ParameterResolver` extension API. In the following example, the -factory method is referenced by its name since there is only one such method in the test -class. If there are several local methods with the same name, parameters can also be -provided to differentiate them – for example, `@MethodSource("factoryMethod()")` or -`@MethodSource("factoryMethod(java.lang.String)")`. Alternatively, the factory method -can be referenced by its fully qualified method name, e.g. -`@MethodSource("example.MyTests#factoryMethod(java.lang.String)")`. - -[source,java,indent=0] ----- -include::{testDir}/example/MethodSourceParameterResolutionDemo.java[tags=parameter_resolution_MethodSource_example] ----- - -[[writing-tests-parameterized-tests-sources-FieldSource]] -===== @FieldSource - -`{FieldSource}` allows you to refer to one or more fields of the test class or external -classes. - -Fields within the test class must be `static` unless the test class is annotated with -`@TestInstance(Lifecycle.PER_CLASS)`; whereas, fields in external classes must always be -`static`. - -Each field must be able to supply a _stream_ of arguments, and each set of "arguments" -within the "stream" will be provided as the physical arguments for individual invocations -of the annotated `@ParameterizedClass` or `@ParameterizedTest`. - -In this context, a "stream" is anything that JUnit can reliably convert to a `Stream`; -however, the actual concrete field type can take on many forms. Generally speaking this -translates to a `Collection`, an `Iterable`, a `Supplier` of a stream (`Stream`, -`DoubleStream`, `LongStream`, or `IntStream`), a `Supplier` of an `Iterator`, an array of -objects or primitives, or any type that provides an `iterator(): Iterator` method (such -as, for example, a `kotlin.sequences.Sequence`). Each set of "arguments" within the -"stream" can be supplied as an instance of `Arguments`, an array of objects (for example, -`Object[]`, `String[]`, etc.), or a single value if the parameterized class or test method accepts -a single argument. - -[WARNING] -==== -In contrast to the supported return types for -<> factory -methods, the value of a `@FieldSource` field cannot be an instance of `Stream`, -`DoubleStream`, `LongStream`, `IntStream`, or `Iterator`, since the values of such types -are _consumed_ the first time they are processed. However, if you wish to use one of -these types, you can wrap it in a `Supplier` — for example, `Supplier`. -==== - -If the `Supplier` return type is `Stream` or one of the primitive streams, -JUnit will properly close it by calling `BaseStream.close()`, -making it safe to use a resource such as `Files.lines()`. - -Please note that a one-dimensional array of objects supplied as a set of "arguments" will -be handled differently than other types of arguments. Specifically, all the elements of a -one-dimensional array of objects will be passed as individual physical arguments to the -`@ParameterizedClass` or `@ParameterizedTest`. See the Javadoc for `{FieldSource}` for -further details. - -For a `@ParameterizedClass`, providing a field name via `@FieldSource` is mandatory. For a -`@ParameterizedTest`, if you do not explicitly provide a field name, JUnit Jupiter will -search in the test class for a field that has the same name as the current -`@ParameterizedTest` method by convention. This is demonstrated in the following example. -This parameterized test method will be invoked twice: with the values `"apple"` and -`"banana"`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=default_field_FieldSource_example] ----- - -The following example demonstrates how to provide a single explicit field name via -`@FieldSource`. This parameterized test method will be invoked twice: with the values -`"apple"` and `"banana"`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=explicit_field_FieldSource_example] ----- - -The following example demonstrates how to provide multiple explicit field names via -`@FieldSource`. This example uses the `listOfFruits` field from the previous example as -well as the `additionalFruits` field. Consequently, this parameterized test method will -be invoked four times: with the values `"apple"`, `"banana"`, `"cherry"`, and -`"dewberry"`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=multiple_fields_FieldSource_example] ----- - -It is also possible to provide a `Stream`, `DoubleStream`, `IntStream`, `LongStream`, or -`Iterator` as the source of arguments via a `@FieldSource` field as long as the stream or -iterator is wrapped in a `java.util.function.Supplier`. The following example demonstrates -how to provide a `Supplier` of a `Stream` of named arguments. This parameterized test -method will be invoked twice: with the values `"apple"` and `"banana"` and with display -names `"Apple"` and `"Banana"`, respectively. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=named_arguments_FieldSource_example] ----- - -[NOTE] -==== -Note that `arguments(Object...)` is a static factory method defined in the -`org.junit.jupiter.params.provider.Arguments` interface. - -Similarly, `named(String, Object)` is a static factory method defined in the -`org.junit.jupiter.api.Named` interface. -==== - -If a parameterized class or test method declares multiple parameters, the corresponding -`@FieldSource` field must be able to provide a collection, stream supplier, or array of -`Arguments` instances or object arrays as shown below (see the Javadoc for `{FieldSource}` -for further details on supported types). - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=multi_arg_FieldSource_example] ----- - -[NOTE] -==== -Note that `arguments(Object...)` is a static factory method defined in the -`org.junit.jupiter.params.provider.Arguments` interface. -==== - -An external, `static` `@FieldSource` field can be referenced by providing its -_fully qualified field name_ as demonstrated in the following example. - -[source,java,indent=0] ----- -include::{testDir}/example/ExternalFieldSourceDemo.java[tags=external_field_FieldSource_example] ----- - -[[writing-tests-parameterized-tests-sources-CsvSource]] -===== @CsvSource - -`@CsvSource` allows you to express argument lists as comma-separated values (i.e., CSV -`String` literals). Each string provided via the `value` attribute in `@CsvSource` -represents a CSV record and results in one invocation of the parameterized class or -test. The first record may optionally be used to supply CSV headers (see the Javadoc for -the `useHeadersInDisplayName` attribute for details and an example). - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=CsvSource_example] ----- - -The default delimiter is a comma (`,`), but you can use another character by setting the -`delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a -`String` delimiter instead of a single character. However, both delimiter attributes -cannot be set simultaneously. - -By default, `@CsvSource` uses a single quote (`'`) as its quote character, but this can be -changed via the `quoteCharacter` attribute. See the `'lemon, lime'` value in the example -above and in the table below. An empty, quoted value (`''`) results in an empty `String` -unless the `emptyValue` attribute is set; whereas, an entirely _empty_ value is -interpreted as a `null` reference. By specifying one or more `nullValues`, a custom value -can be interpreted as a `null` reference (see the `NIL` example in the table below). An -`ArgumentConversionException` is thrown if the target type of a `null` reference is a -primitive type. - -NOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless -of any custom values configured via the `nullValues` attribute. - -Except within a quoted string, leading and trailing whitespace in a CSV column is trimmed -by default. This behavior can be changed by setting the -`ignoreLeadingAndTrailingWhitespace` attribute to `true`. - -[cols="50,50"] -|=== -| Example Input | Resulting Argument List - -| `@CsvSource({ "apple, banana" })` | `"apple"`, `"banana"` -| `@CsvSource({ "apple, 'lemon, lime'" })` | `"apple"`, `"lemon, lime"` -| `@CsvSource({ "apple, ''" })` | `"apple"`, `""` -| `@CsvSource({ "apple, " })` | `"apple"`, `null` -| `@CsvSource(value = { "apple, banana, NIL" }, nullValues = "NIL")` | `"apple"`, `"banana"`, `null` -| `@CsvSource(value = { " apple , banana" }, ignoreLeadingAndTrailingWhitespace = false)` | `" apple "`, `" banana"` -|=== - -If the programming language you are using supports Java _text blocks_ or equivalent -multi-line string literals, you can alternatively use the `textBlock` attribute of -`@CsvSource`. Each record within a text block represents a CSV record and results in one -invocation of the parameterized class or test. The first record may optionally be used to -supply CSV headers by setting the `useHeadersInDisplayName` attribute to `true` as in the -example below. - -Using a text block, the previous example can be implemented as follows. - -[source,java,indent=0] ----- -@ParameterizedTest -@CsvSource(useHeadersInDisplayName = true, textBlock = """ - FRUIT, RANK - apple, 1 - banana, 2 - 'lemon, lime', 0xF1 - strawberry, 700_000 - """) -void testWithCsvSource(String fruit, int rank) { - // ... -} ----- - -The generated display names for the previous example include the CSV header names. - ----- -[1] FRUIT = "apple", RANK = "1" -[2] FRUIT = "banana", RANK = "2" -[3] FRUIT = "lemon, lime", RANK = "0xF1" -[4] FRUIT = "strawberry", RANK = "700_000" ----- - -In contrast to CSV records supplied via the `value` attribute, a text block can contain -comments. Any line beginning with the value of the `commentCharacter` attribute (`+++#+++` -by default) will be treated as a comment and ignored. Note that there is one exception -to this rule: if the comment character appears within a quoted field, it loses -its special meaning. - -The comment character must be the first character on the line without any leading -whitespace. It is therefore recommended that the closing text block delimiter (`"""`) -be placed either at the end of the last line of input or on the following line, -left aligned with the rest of the input (as can be seen in the example below which -demonstrates formatting similar to a table). - -[source,java,indent=0] ----- -@ParameterizedTest -@CsvSource(delimiter = '|', quoteCharacter = '"', textBlock = """ - #----------------------------- - # FRUIT | RANK - #----------------------------- - apple | 1 - #----------------------------- - banana | 2 - #----------------------------- - "lemon lime" | 0xF1 - #----------------------------- - strawberry | 700_000 - #----------------------------- - """) -void testWithCsvSource(String fruit, int rank) { - // ... -} ----- - -[NOTE] -==== -Java's https://docs.oracle.com/en/java/javase/17/text-blocks/index.html[text block] -feature automatically removes _incidental whitespace_ when the code is compiled. -However other JVM languages such as Groovy and Kotlin do not. Thus, if you are using a -programming language other than Java and your text block contains comments or new lines -within quoted strings, you will need to ensure that there is no leading whitespace within -your text block. -==== - -[[writing-tests-parameterized-tests-sources-CsvFileSource]] -===== @CsvFileSource - -`@CsvFileSource` lets you use comma-separated value (CSV) files from the classpath or the -local file system. Each record from a CSV file results in one invocation of the -parameterized class or test. The first record may optionally be used to supply CSV -headers. You can instruct JUnit to ignore the headers via the `numLinesToSkip` attribute. -If you would like for the headers to be used in the display names, you can set the -`useHeadersInDisplayName` attribute to `true`. The examples below demonstrate the use of -`numLinesToSkip` and `useHeadersInDisplayName`. - -The default delimiter is a comma (`,`), but you can use another character by setting the -`delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a -`String` delimiter instead of a single character. However, both delimiter attributes -cannot be set simultaneously. - -.Comments in CSV files -NOTE: Any line beginning with the value of the `commentCharacter` attribute (`+++#+++` -by default) will be interpreted as a comment and will be ignored. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=CsvFileSource_example] ----- - -[source,csv,indent=0] -.two-column.csv ----- -include::{testResourcesDir}/two-column.csv[] ----- - -The following listing shows the generated display names for the first two parameterized -test methods above. - ----- -[1] country = "Sweden", reference = "1" -[2] country = "Poland", reference = "2" -[3] country = "United States of America", reference = "3" -[4] country = "France", reference = "700_000" ----- - -The following listing shows the generated display names for the last parameterized test -method above that uses CSV header names. - ----- -[1] COUNTRY = "Sweden", REFERENCE = "1" -[2] COUNTRY = "Poland", REFERENCE = "2" -[3] COUNTRY = "United States of America", REFERENCE = "3" -[4] COUNTRY = "France", REFERENCE = "700_000" ----- - -In contrast to the default syntax used in `@CsvSource`, `@CsvFileSource` uses a double -quote (`+++"+++`) as the quote character by default, but this can be changed via the -`quoteCharacter` attribute. See the `"United States of America"` value in the example -above. An empty, quoted value (`+++""+++`) results in an empty `String` unless the -`emptyValue` attribute is set; whereas, an entirely _empty_ value is interpreted as a -`null` reference. By specifying one or more `nullValues`, a custom value can be -interpreted as a `null` reference. An `ArgumentConversionException` is thrown if the -target type of a `null` reference is a primitive type. - -NOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless -of any custom values configured via the `nullValues` attribute. - -Except within a quoted string, leading and trailing whitespace in a CSV column is trimmed -by default. This behavior can be changed by setting the -`ignoreLeadingAndTrailingWhitespace` attribute to `true`. - -[[writing-tests-parameterized-tests-sources-ArgumentsSource]] -===== @ArgumentsSource - -`@ArgumentsSource` can be used to specify a custom, reusable `ArgumentsProvider`. Note -that an implementation of `ArgumentsProvider` must be declared as either a top-level -class or as a `static` nested class. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsSource_example] ----- - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsProvider_example] ----- - -If you wish to implement a custom `ArgumentsProvider` that also consumes an annotation -(like built-in providers such as `{ValueArgumentsProvider}` or `{CsvArgumentsProvider}`), -you have the possibility to extend the `{AnnotationBasedArgumentsProvider}` class. - -Moreover, `ArgumentsProvider` implementations may declare constructor parameters in case -they need to be resolved by a registered `ParameterResolver` as demonstrated in the -following example. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsProviderWithConstructorInjection_example] ----- - -[[writing-tests-parameterized-repeatable-sources]] -===== Multiple sources using repeatable annotations - -Repeatable annotations provide a convenient way to specify multiple sources from -different providers. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=repeatable_annotations] ----- - -Following the above parameterized test, a test case will run for each argument: - ----- -[1] foo -[2] bar ----- - -The following annotations are repeatable: - -* `@ValueSource` -* `@EnumSource` -* `@MethodSource` -* `@FieldSource` -* `@CsvSource` -* `@CsvFileSource` -* `@ArgumentsSource` - -[[writing-tests-parameterized-tests-argument-count-validation]] -==== Argument Count Validation - -By default, when an arguments source provides more arguments than the test method needs, -those additional arguments are ignored and the test executes as usual. -This can lead to bugs where arguments are never passed to the parameterized class or -method. - -To prevent this, you can set argument count validation to 'strict'. -Then, any additional arguments will cause an error instead. - -To change this behavior for all tests, set the -`junit.jupiter.params.argumentCountValidation` -<> to `strict`. -To change this behavior for a single parameterized class or test method, -use the `argumentCountValidation` attribute of the `@ParameterizedClass` or -`@ParameterizedTest` annotation: - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=argument_count_validation] ----- - -[[writing-tests-parameterized-tests-argument-conversion]] -==== Argument Conversion - -[[writing-tests-parameterized-tests-argument-conversion-widening]] -===== Widening Conversion - -JUnit Jupiter supports -https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2[Widening Primitive -Conversion] for arguments supplied to a `@ParameterizedClass` or `@ParameterizedTest`. -For example, a parameterized class or test method annotated with -`@ValueSource(ints = { 1, 2, 3 })` can be declared to accept not only an argument of type -`int` but also an argument of type `long`, `float`, or `double`. - -[[writing-tests-parameterized-tests-argument-conversion-implicit]] -===== Implicit Conversion - -To support use cases like `@CsvSource`, JUnit Jupiter provides a number of built-in -implicit type converters. The conversion process depends on the declared type of each -method parameter. - -For example, if a `@ParameterizedClass` or `@ParameterizedTest` declares a parameter -of type `TimeUnit` and the actual type supplied by the declared source is a `String`, the -string will be automatically converted into the corresponding `TimeUnit` enum constant. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=implicit_conversion_example] ----- - -`String` instances are implicitly converted to the following target types. - -NOTE: Decimal, hexadecimal, and octal `String` literals will be converted to their -integral types: `byte`, `short`, `int`, `long`, and their boxed counterparts. - -[[writing-tests-parameterized-tests-argument-conversion-implicit-table]] -[cols="10,90"] -|=== -| Target Type | Example - -| `boolean`/`Boolean` | `"true"` -> `true` _(only accepts values 'true' or 'false', case-insensitive)_ -| `byte`/`Byte` | `"15"`, `"0xF"`, or `"017"` -> `(byte) 15` -| `char`/`Character` | `"o"` -> `'o'` -| `short`/`Short` | `"15"`, `"0xF"`, or `"017"` -> `(short) 15` -| `int`/`Integer` | `"15"`, `"0xF"`, or `"017"` -> `15` -| `long`/`Long` | `"15"`, `"0xF"`, or `"017"` -> `15L` -| `float`/`Float` | `"1.0"` -> `1.0f` -| `double`/`Double` | `"1.0"` -> `1.0d` -| `Enum` subclass | `"SECONDS"` -> `TimeUnit.SECONDS` -| `java.io.File` | `"/path/to/file"` -> `new File("/path/to/file")` -| `java.lang.Class` | `"java.lang.Integer"` -> `java.lang.Integer.class` _(use `$` for nested classes, e.g. `"java.lang.Thread$State"`)_ -| `java.lang.Class` | `"byte"` -> `byte.class` _(primitive types are supported)_ -| `java.lang.Class` | `"char[]"` -> `char[].class` _(array types are supported)_ -| `java.math.BigDecimal` | `"123.456e789"` -> `new BigDecimal("123.456e789")` -| `java.math.BigInteger` | `"1234567890123456789"` -> `new BigInteger("1234567890123456789")` -| `java.net.URI` | `"https://junit.org/"` -> `URI.create("https://junit.org/")` -| `java.net.URL` | `"https://junit.org/"` -> `URI.create("https://junit.org/").toURL()` -| `java.nio.charset.Charset` | `"UTF-8"` -> `Charset.forName("UTF-8")` -| `java.nio.file.Path` | `"/path/to/file"` -> `Paths.get("/path/to/file")` -| `java.time.Duration` | `"PT3S"` -> `Duration.ofSeconds(3)` -| `java.time.Instant` | `"1970-01-01T00:00:00Z"` -> `Instant.ofEpochMilli(0)` -| `java.time.LocalDateTime` | `"2017-03-14T12:34:56.789"` -> `LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000)` -| `java.time.LocalDate` | `"2017-03-14"` -> `LocalDate.of(2017, 3, 14)` -| `java.time.LocalTime` | `"12:34:56.789"` -> `LocalTime.of(12, 34, 56, 789_000_000)` -| `java.time.MonthDay` | `"--03-14"` -> `MonthDay.of(3, 14)` -| `java.time.OffsetDateTime` | `"2017-03-14T12:34:56.789Z"` -> `OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)` -| `java.time.OffsetTime` | `"12:34:56.789Z"` -> `OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC)` -| `java.time.Period` | `"P2M6D"` -> `Period.of(0, 2, 6)` -| `java.time.YearMonth` | `"2017-03"` -> `YearMonth.of(2017, 3)` -| `java.time.Year` | `"2017"` -> `Year.of(2017)` -| `java.time.ZonedDateTime` | `"2017-03-14T12:34:56.789Z"` -> `ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)` -| `java.time.ZoneId` | `"Europe/Berlin"` -> `ZoneId.of("Europe/Berlin")` -| `java.time.ZoneOffset` | `"+02:30"` -> `ZoneOffset.ofHoursMinutes(2, 30)` -| `java.util.Currency` | `"JPY"` -> `Currency.getInstance("JPY")` -| `java.util.Locale` | `"en-US"` -> `Locale.forLanguageTag("en-US")` -| `java.util.UUID` | `"d043e930-7b3b-48e3-bdbe-5a3ccfb833db"` -> `UUID.fromString("d043e930-7b3b-48e3-bdbe-5a3ccfb833db")` -|=== - -[[writing-tests-parameterized-tests-argument-conversion-implicit-fallback]] -====== Fallback String-to-Object Conversion - -In addition to implicit conversion from strings to the target types listed in the above -table, JUnit Jupiter also provides a fallback mechanism for automatic conversion from a -`String` to a given target type if the target type declares exactly one suitable _factory -method_ or a _factory constructor_ as defined below. - -- __factory method__: a non-private, `static` method declared in the target type that - accepts either a single `String` argument or a single `CharSequence` argument and - returns an instance of the target type. The name of the method can be arbitrary and need - not follow any particular convention. -- __factory constructor__: a non-private constructor in the target type that accepts a - either a single `String` argument or a single `CharSequence` argument. Note that the - target type must be declared as either a top-level class or as a `static` nested class. - -NOTE: If multiple _factory methods_ are discovered, they will be ignored. If a _factory -method_ and a _factory constructor_ are discovered, the factory method will be used -instead of the constructor. - -For example, in the following `@ParameterizedTest` method, the `Book` argument will be -created by invoking the `Book.fromTitle(String)` factory method and passing `"42 Cats"` -as the title of the book. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example] ----- - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example_Book] ----- - -[[writing-tests-parameterized-tests-argument-conversion-explicit]] -===== Explicit Conversion - -Instead of relying on implicit argument conversion, you may explicitly specify an -`ArgumentConverter` to use for a certain parameter using the `@ConvertWith` annotation -like in the following example. Note that an implementation of `ArgumentConverter` must be -declared as either a top-level class or as a `static` nested class. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=explicit_conversion_example] ----- - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_ToStringArgumentConverter] ----- - -If the converter is only meant to convert one type to another, you can extend -`TypedArgumentConverter` to avoid boilerplate type checks. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_TypedArgumentConverter] ----- - -Explicit argument converters are meant to be implemented by test and extension authors. -Thus, `junit-jupiter-params` only provides a single explicit argument converter that may -also serve as a reference implementation: `JavaTimeArgumentConverter`. It is used via the -composed annotation `JavaTimeConversionPattern`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=explicit_java_time_converter] ----- - -If you wish to implement a custom `ArgumentConverter` that also consumes an annotation -(like `JavaTimeArgumentConverter`), you have the possibility to extend the -`{AnnotationBasedArgumentConverter}` class. - -[[writing-tests-parameterized-tests-argument-aggregation]] -==== Argument Aggregation - -By default, each _argument_ provided to a `@ParameterizedClass` or `@ParameterizedTest` -corresponds to a single method parameter. Consequently, argument sources which are -expected to supply a large number of arguments can lead to large constructor or method -signatures, respectively. - -In such cases, an `{ArgumentsAccessor}` can be used instead of multiple parameters. Using -this API, you can access the provided arguments through a single argument passed to your -test method. In addition, type conversion is supported as discussed in -<>. - -Besides, you can retrieve the current test invocation index with -`ArgumentsAccessor.getInvocationIndex()`. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsAccessor_example] ----- - -_An instance of `ArgumentsAccessor` is automatically injected into any parameter of type -`ArgumentsAccessor`._ - -[[writing-tests-parameterized-tests-argument-aggregation-custom]] -===== Custom Aggregators - -Apart from direct access to the arguments of a `@ParameterizedClass` or -`@ParameterizedTest` using an `ArgumentsAccessor`, JUnit Jupiter also supports the usage -of custom, reusable _aggregators_. - -To use a custom aggregator, implement the `{ArgumentsAggregator}` interface and register -it via the `@AggregateWith` annotation on a compatible parameter of the -`@ParameterizedClass` or `@ParameterizedTest`. The result of the aggregation will then be -provided as an argument for the corresponding parameter when the parameterized test is -invoked. Note that an implementation of `ArgumentsAggregator` must be declared as either a -top-level class or as a `static` nested class. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example] ----- - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example_PersonAggregator] ----- - -If you find yourself repeatedly declaring `@AggregateWith(MyTypeAggregator.class)` for -multiple parameterized classes or methods across your codebase, you may wish to create a -custom _composed annotation_ such as `@CsvToMyType` that is meta-annotated with -`@AggregateWith(MyTypeAggregator.class)`. The following example demonstrates this in -action with a custom `@CsvToPerson` annotation. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example] ----- - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example_CsvToPerson] ----- - - -[[writing-tests-parameterized-tests-display-names]] -==== Customizing Display Names - -By default, the display name of a parameterized class or test invocation contains the -invocation index and a comma-separated list of the `String` representations of all -arguments for that specific invocation. If parameter names are present in the bytecode, -each argument will be preceded by its parameter name and an equals sign (unless the -argument is only available via an `ArgumentsAccessor` or `ArgumentAggregator`) – for -example, `firstName = "Jane"`. - -[TIP] -==== -To ensure that parameter names are present in the bytecode, test code must be compiled -with the `-parameters` compiler flag for Java or with the `-java-parameters` compiler flag -for Kotlin. -==== - -However, you can customize invocation display names via the `name` attribute of the -`@ParameterizedClass` or `@ParameterizedTest` annotation as in the following example. - -====== -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=custom_display_names] ----- - -When executing the above method using the `ConsoleLauncher` you will see output similar to -the following. - -.... -Display name of container ✔ -├─ 1 ==> the rank of "apple" is "1" ✔ -├─ 2 ==> the rank of "banana" is "2" ✔ -└─ 3 ==> the rank of "lemon, lime" is "3" ✔ -.... -====== - -[NOTE] -==== -Please note that `name` is a `MessageFormat` pattern. Thus, a single quote (`'`) needs to -be represented as a doubled single quote (`''`) in order to be displayed. -==== - -The following placeholders are supported within custom display names. - -[cols="20,80"] -|=== -| Placeholder | Description - -| `\{displayName}` | the display name of the method -| `\{index}` | the current invocation index (1-based) -| `\{arguments}` | the complete, comma-separated arguments list -| `\{argumentsWithNames}` | the complete, comma-separated arguments list with parameter names -| `\{argumentSetName}` | the name of the argument set -| `\{argumentSetNameOrArgumentsWithNames}` | `\{argumentSetName}` or `\{argumentsWithNames}`, depending on how the arguments are supplied -| `\{0}`, `\{1}`, ... | an individual argument -|=== - -NOTE: When including arguments in display names, their string representations are truncated -if they exceed the configured maximum length. The limit is configurable via the -`junit.jupiter.params.displayname.argument.maxlength` configuration parameter and defaults -to 512 characters. - -When using `@MethodSource`, `@FieldSource`, or `@ArgumentsSource`, you can provide custom -names for individual arguments or custom names for entire sets of arguments. - -Use the `{Named}` API to provide a custom name for an individual argument, and the custom -name will be used if the argument is included in the invocation display name, like in the -example below. - -====== -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=named_arguments] ----- - -When executing the above method using the `ConsoleLauncher` you will see output similar to -the following. - -.... -A parameterized test with named arguments ✔ -├─ 1: An important file ✔ -└─ 2: Another file ✔ -.... -====== - -[NOTE] -==== -Note that `arguments(Object...)` is a static factory method defined in the -`org.junit.jupiter.params.provider.Arguments` interface. - -Similarly, `named(String, Object)` is a static factory method defined in the -`org.junit.jupiter.api.Named` interface. -==== - -Use the `ArgumentSet` API to provide a custom name for the entire set of arguments, and -the custom name will be used as the display name, like in the example below. - -====== -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=named_argument_set] ----- - -When executing the above method using the `ConsoleLauncher` you will see output similar to -the following. - -.... -A parameterized test with named argument sets ✔ -├─ [1] Important files ✔ -└─ [2] Other files ✔ -.... -====== - -[NOTE] -==== -Note that `argumentSet(String, Object...)` is a static factory method defined in the -`org.junit.jupiter.params.provider.Arguments` interface. -==== - -[[writing-tests-parameterized-tests-display-names-quoted-text]] -===== Quoted Text-based Arguments - -As of JUnit Jupiter 6.0, text-based arguments in display names for parameterized tests are -quoted by default. In this context, any `CharSequence` (such as a `String`) or `Character` -is considered text. A `CharSequence` is wrapped in double quotes (`"`), and a `Character` -is wrapped in single quotes (`'`). - -Special characters will be escaped in the quoted text. For example, carriage returns and -line feeds will be escaped as `\\r` and `\\n`, respectively. - -[TIP] -==== -This feature can be disabled by setting the `quoteTextArguments` attributes in -`@ParameterizedClass` and `@ParameterizedTest` to `false`. -==== - -For example, given a string argument `"line 1\nline 2"`, the physical representation in -the display name will be `"\"line 1\\nline 2\""` which is printed as `"line 1\nline 2"`. -Similarly, given a string argument `"\t"`, the physical representation in the display name -will be `"\"\\t\""` which is printed as `"\t"` instead of a blank string or invisible tab -character. The same applies for a character argument `'\t'`, whose physical representation -in the display name would be `"'\\t'"` which is printed as `'\t'`. - -For a concrete example, if you run the first `nullEmptyAndBlankStrings(String text)` -parameterized test method from the -<> section above, the following -display names are generated. - ----- -[1] text = null -[2] text = "" -[3] text = " " -[4] text = " " -[5] text = "\t" -[6] text = "\n" ----- - -If you run the first `testWithCsvSource(String fruit, int rank)` parameterized test method -from the <> section above, the -following display names are generated. - ----- -[1] fruit = "apple", rank = "1" -[2] fruit = "banana", rank = "2" -[3] fruit = "lemon, lime", rank = "0xF1" -[4] fruit = "strawberry", rank = "700_000" ----- - -[NOTE] -==== -The original source arguments are quoted when generating a display name, and this occurs -before any implicit or explicit argument conversion is performed. - -For example, if a parameterized test accepts `3.14` as a `float` argument that was -converted from `"3.14"` as an input string, `"3.14"` will be present in the display name -instead of `3.14`. You can see the effect of this with the `rank` values in the above -example. -==== - -[[writing-tests-parameterized-tests-display-names-default-pattern]] -===== Default Display Name Pattern - -If you'd like to set a default name pattern for all parameterized classes and tests in -your project, you can declare the `junit.jupiter.params.displayname.default` configuration -parameter in the `junit-platform.properties` file as demonstrated in the following example (see -<> for other options). - -[source,properties,indent=0] ----- -junit.jupiter.params.displayname.default = {index} ----- - -[[writing-tests-parameterized-tests-display-names-precedence-rules]] -===== Precedence Rules - -The display name for a parameterized class or test is determined according to the -following precedence rules: - -1. `name` attribute in `@ParameterizedClass` or `@ParameterizedTest`, if present -2. value of the `junit.jupiter.params.displayname.default` configuration parameter, if present -3. `DEFAULT_DISPLAY_NAME` constant defined in - `org.junit.jupiter.params.ParameterizedInvocationConstants` - -[[writing-tests-parameterized-tests-lifecycle-interop]] -==== Lifecycle and Interoperability - -[[writing-tests-parameterized-tests-lifecycle-interop-methods]] -===== Parameterized Tests - -Each invocation of a parameterized test has the same lifecycle as a regular `@Test` -method. For example, `@BeforeEach` methods will be executed before each invocation. -Similar to <>, invocations will appear one by one in the -test tree of an IDE. You may at will mix regular `@Test` methods and `@ParameterizedTest` -methods within the same test class. - -You may use `ParameterResolver` extensions with `@ParameterizedTest` methods. However, -method parameters that are resolved by argument sources need to come first in the -parameter list. Since a test class may contain regular tests as well as parameterized -tests with different parameter lists, values from argument sources are not resolved for -lifecycle methods (e.g. `@BeforeEach`) and test class constructors. - -[source,java,indent=0] ----- -include::{testDir}/example/ParameterizedTestDemo.java[tags=ParameterResolver_example] ----- - -[[writing-tests-parameterized-tests-lifecycle-interop-classes]] -===== Parameterized Classes - -Each invocation of a parameterized class has the same lifecycle as a regular test class. -For example, `@BeforeAll` methods will be executed _once_ before all invocations and -`@BeforeEach` methods will be executed before each _test method_ invocation. Similar to -<>, invocations will appear one by one in the test tree of an -IDE. - -You may use `ParameterResolver` extensions with `@ParameterizedClass` constructors. -However, if constructor injection is used, constructor parameters that are resolved by -argument sources need to come first in the parameter list. Values from argument sources -are not resolved for regular lifecycle methods (e.g. `@BeforeEach`). - -In addition to regular lifecycle methods, parameterized classes may declare -`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` lifecycle -methods that are called once before/after each invocation of the parameterized class. -These methods must be `static` unless the parameterized class is configured to use -`@TestInstance(Lifecycle.PER_CLASS)` (see <>). - -These lifecycle methods may optionally declare parameters that are resolved depending on -the setting of the `injectArguments` annotation attribute. If it is set to `false`, the -parameters must be resolved by other registered {ParameterResolver} extensions. If the -attribute is set to `true` (the default), the method may declare parameters that match the -arguments of the parameterized class (see the Javadoc of -`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` for -details). This may, for example, be used to initialize the used arguments as demonstrated -by the following example. - -[source,java,indent=0] -.Using parameterized class lifecycle methods ----- -include::{testDir}/example/ParameterizedLifecycleDemo.java[tags=example] ----- -<1> Initialization of the argument _before_ each invocation of the parameterized class -<2> Usage of the previously initialized argument in a test method -<3> Validation and cleanup of the argument _after_ each invocation of the parameterized - class - -[[writing-tests-class-templates]] -=== Class Templates - -A `{ClassTemplate}` is not a regular test class but rather a template for the contained -test cases. As such, it is designed to be invoked multiple times depending on invocation -contexts returned by the registered providers. Thus, it must be used in conjunction with a -registered `{ClassTemplateInvocationContextProvider}` extension. -Each invocation of a class template behaves like the execution of a regular test class -with full support for the same lifecycle callbacks and extensions. Please refer to -<> for usage examples. - -NOTE: <> are a built-in -specialization of class templates. - -[[writing-tests-test-templates]] -=== Test Templates - -A `{TestTemplate}` method is not a regular test case but rather a template for a test -case. As such, it is designed to be invoked multiple times depending on the number of -invocation contexts returned by the registered providers. Thus, it must be used in -conjunction with a registered `{TestTemplateInvocationContextProvider}` extension. Each -invocation of a test template method behaves like the execution of a regular `@Test` -method with full support for the same lifecycle callbacks and extensions. Please refer to -<> for usage examples. - -NOTE: <> and -<> are built-in specializations of -test templates. - -[[writing-tests-dynamic-tests]] -=== Dynamic Tests - -The standard `@Test` annotation in JUnit Jupiter described in -<> is very similar to the `@Test` annotation in JUnit 4. Both -describe methods that implement test cases. These test cases are static in the sense that -they are fully specified at compile time, and their behavior cannot be changed by -anything happening at runtime. _Assumptions provide a basic form of dynamic behavior but -are intentionally rather limited in their expressiveness._ - -In addition to these standard tests a completely new kind of test programming model has -been introduced in JUnit Jupiter. This new kind of test is a _dynamic test_ which is -generated at runtime by a factory method that is annotated with `@TestFactory`. - -In contrast to `@Test` methods, a `@TestFactory` method is not itself a test case but -rather a factory for test cases. Thus, a dynamic test is the product of a factory. -Technically speaking, a `@TestFactory` method must return a single `DynamicNode` or a -_stream_ of `DynamicNode` instances or any of its subclasses. In this context, a "stream" -is anything that JUnit can reliably convert into a `Stream`, such as `Stream`, -`Collection`, `Iterator`, `Iterable`, an array of objects, or any type that provides an -`iterator(): Iterator` method (such as, for example, a `kotlin.sequences.Sequence`). - -Instantiable subclasses of `DynamicNode` are `DynamicContainer` and `DynamicTest`. -`DynamicContainer` instances are composed of a _display name_ and a list of dynamic child -nodes, enabling the creation of arbitrarily nested hierarchies of dynamic nodes. -`DynamicTest` instances will be executed lazily, enabling dynamic and even -non-deterministic generation of test cases. - -Any `Stream` returned by a `@TestFactory` will be properly closed by calling -`stream.close()`, making it safe to use a resource such as `Files.lines()`. - -As with `@Test` methods, `@TestFactory` methods must not be `private` or `static` and may -optionally declare parameters to be resolved by `ParameterResolvers`. - -A `DynamicTest` is a test case generated at runtime. It is composed of a _display name_ -and an `Executable`. `Executable` is a `@FunctionalInterface` which means that the -implementations of dynamic tests can be provided as _lambda expressions_ or _method -references_. - -.Dynamic Test Lifecycle -WARNING: The execution lifecycle of a dynamic test is quite different than it is for a -standard `@Test` case. Specifically, there are no lifecycle callbacks for individual -dynamic tests. This means that `@BeforeEach` and `@AfterEach` methods and their -corresponding extension callbacks are executed for the `@TestFactory` method but not for -each _dynamic test_. In other words, if you access fields from the test instance within a -lambda expression for a dynamic test, those fields will not be reset by callback methods -or extensions between the execution of individual dynamic tests generated by the same -`@TestFactory` method. - -[[writing-tests-dynamic-tests-examples]] -==== Dynamic Test Examples - -The following `DynamicTestsDemo` class demonstrates several examples of test factories -and dynamic tests. - -The first method returns an invalid return type and will cause a warning to be reported by -JUnit during test discovery. Such methods are not executed. - -The next six methods demonstrate the generation of a `Collection`, `Iterable`, `Iterator`, -array, or `Stream` of `DynamicTest` instances. Most of these examples do not really -exhibit dynamic behavior but merely demonstrate the supported return types in principle. -However, `dynamicTestsFromStream()` and `dynamicTestsFromIntStream()` demonstrate how to -generate dynamic tests for a given set of strings or a range of input numbers. - -The next method is truly dynamic in nature. `generateRandomNumberOfTests()` implements an -`Iterator` that generates random numbers, a display name generator, and a test executor -and then provides all three to `DynamicTest.stream()`. Although the non-deterministic -behavior of `generateRandomNumberOfTests()` is of course in conflict with test -repeatability and should thus be used with care, it serves to demonstrate the -expressiveness and power of dynamic tests. - -The next method is similar to `generateRandomNumberOfTests()` in terms of flexibility; -however, `dynamicTestsFromStreamFactoryMethod()` generates a stream of dynamic tests from -an existing `Stream` via the `DynamicTest.stream()` factory method. - -For demonstration purposes, the `dynamicNodeSingleTest()` method generates a single -`DynamicTest` instead of a stream, and the `dynamicNodeSingleContainer()` method generates -a nested hierarchy of dynamic tests utilizing `DynamicContainer`. - -[source,java] ----- -include::{testDir}/example/DynamicTestsDemo.java[tags=user_guide] ----- - -[[writing-tests-dynamic-tests-named-support]] -==== Dynamic Tests and Named - -In some cases, it can be more natural to specify inputs together with a descriptive name -using the {Named} API and the corresponding `stream()` factory methods on `DynamicTest` as -shown in the first example below. The second example takes it one step further and allows -to provide the code block that should be executed by implementing the `Executable` -interface along with `Named` via the `NamedExecutable` base class. - -[source,java] ----- -include::{testDir}/example/DynamicTestsNamedDemo.java[tags=user_guide] ----- - -[[writing-tests-dynamic-tests-uri-test-source]] -==== URI Test Sources for Dynamic Tests - -The JUnit Platform provides `TestSource`, a representation of the source of a test or -container used to navigate to its location by IDEs and build tools. - -The `TestSource` for a dynamic test or dynamic container can be constructed from a -`java.net.URI` which can be supplied via the `DynamicTest.dynamicTest(String, URI, -Executable)` or `DynamicContainer.dynamicContainer(String, URI, Stream)` factory method, -respectively. The `URI` will be converted to one of the following `TestSource` -implementations. - -`ClasspathResourceSource` :: - If the `URI` contains the `classpath` scheme -- for example, - `classpath:/test/foo.xml?line=20,column=2`. - -`DirectorySource` :: - If the `URI` represents a directory present in the file system. - -`FileSource` :: - If the `URI` represents a file present in the file system. - -`MethodSource` :: - If the `URI` contains the `method` scheme and the fully qualified method name (FQMN) -- - for example, `method:org.junit.Foo#bar(java.lang.String, java.lang.String[])`. Please - refer to the Javadoc for `{DiscoverySelectors}.{DiscoverySelectors_selectMethod}` for the - supported formats for a FQMN. - -`ClassSource` :: - If the `URI` contains the `class` scheme and the fully qualified class name -- - for example, `class:org.junit.Foo?line=42`. - -`UriSource` :: - If none of the above `TestSource` implementations are applicable. - -[[writing-tests-dynamic-tests-parallel-execution]] -==== Parallel Execution - -Dynamic tests and containers support -<>. You can configure their -`ExecutionMode` by using the `dynamicTest(Consumer)` and `dynamicContainer(Consumer)` -factory methods as illustrated by the following example. - -[source,java,indent=0] ----- -include::{testDir}/example/DynamicTestsDemo.java[tags=execution_mode] ----- - -Executing the above test factory method results in the following test tree and execution -modes: - -* dynamicTestsWithConfiguredExecutionMode() -- `CONCURRENT` (from `@Execution` annotation) -** Container A -- `CONCURRENT` (from `@Execution` annotation) -*** not null -- `SAME_THREAD` (from `executionMode(...)` call) -*** properties -- `CONCURRENT` (from `@Execution` annotation) -**** length > 0 -- `CONCURRENT` (from `executionMode(...)` call) -**** not empty -- `SAME_THREAD` (from `childExecutionMode(...)` call) -** ... (same for "Container B" and "Container C") - -[[writing-tests-declarative-timeouts]] -=== Timeouts - -The `@Timeout` annotation allows one to declare that a test, test factory, test template, -or lifecycle method should fail if its execution time exceeds a given duration. The time -unit for the duration defaults to seconds but is configurable. - -The following example shows how `@Timeout` is applied to lifecycle and test methods. - -[source,java] ----- -include::{testDir}/example/TimeoutDemo.java[tags=user_guide] ----- - -To apply the same timeout to all test methods within a test class and all of its `@Nested` -classes, you can declare the `@Timeout` annotation at the class level. It will then be -applied to all test, test factory, and test template methods within that class and its -`@Nested` classes unless overridden by a `@Timeout` annotation on a specific method or -`@Nested` class. Please note that `@Timeout` annotations declared at the class level are -not applied to lifecycle methods. - -Declaring `@Timeout` on a `@TestFactory` method checks that the factory method returns -within the specified duration but does not verify the execution time of each individual -`DynamicTest` generated by the factory. Please use -`assertTimeout()` or `assertTimeoutPreemptively()` for that purpose. - -If `@Timeout` is present on a `@TestTemplate` method — for example, a `@RepeatedTest` or -`@ParameterizedTest` — each invocation will have the given timeout applied to it. - -[[writing-tests-declarative-timeouts-thread-mode]] -==== Thread mode - -The timeout can be applied using one of the following three thread modes: `SAME_THREAD`, -`SEPARATE_THREAD`, or `INFERRED`. - -When `SAME_THREAD` is used, the execution of the annotated method proceeds in the main -thread of the test. If the timeout is exceeded, the main thread is interrupted from -another thread. This is done to ensure interoperability with frameworks such as Spring -that make use of mechanisms that are sensitive to the currently running thread — for -example, `ThreadLocal` transaction management. - -On the contrary when `SEPARATE_THREAD` is used, like the `assertTimeoutPreemptively()` -assertion, the execution of the annotated method proceeds in a separate thread, this -can lead to undesirable side effects, see <>. - -When `INFERRED` (default) thread mode is used, the thread mode is resolved via the -`junit.jupiter.execution.timeout.thread.mode.default` configuration parameter. If the -provided configuration parameter is invalid or not present then `SAME_THREAD` is used as -fallback. - -[[writing-tests-declarative-timeouts-default-timeouts]] -==== Default Timeouts - -The following <> can be used to -specify default timeouts for all methods of a certain category unless they or an enclosing -test class is annotated with `@Timeout`: - -`junit.jupiter.execution.timeout.default`:: - Default timeout for all testable and lifecycle methods -`junit.jupiter.execution.timeout.testable.method.default`:: - Default timeout for all testable methods -`junit.jupiter.execution.timeout.test.method.default`:: - Default timeout for `@Test` methods -`junit.jupiter.execution.timeout.testtemplate.method.default`:: - Default timeout for `@TestTemplate` methods -`junit.jupiter.execution.timeout.testfactory.method.default`:: - Default timeout for `@TestFactory` methods -`junit.jupiter.execution.timeout.lifecycle.method.default`:: - Default timeout for all lifecycle methods -`junit.jupiter.execution.timeout.beforeall.method.default`:: - Default timeout for `@BeforeAll` methods -`junit.jupiter.execution.timeout.beforeeach.method.default`:: - Default timeout for `@BeforeEach` methods -`junit.jupiter.execution.timeout.aftereach.method.default`:: - Default timeout for `@AfterEach` methods -`junit.jupiter.execution.timeout.afterall.method.default`:: - Default timeout for `@AfterAll` methods - -More specific configuration parameters override less specific ones. For example, -`junit.jupiter.execution.timeout.test.method.default` overrides -`junit.jupiter.execution.timeout.testable.method.default` which overrides -`junit.jupiter.execution.timeout.default`. - -The values of such configuration parameters must be in the following, case-insensitive -format: ` [ns|μs|ms|s|m|h|d]`. The space between the number and the unit may be -omitted. Specifying no unit is equivalent to using seconds. - -.Example timeout configuration parameter values -[cols="20,80"] -|=== -| Parameter value | Equivalent annotation - -| `42` | `@Timeout(42)` -| `42 ns` | `@Timeout(value = 42, unit = NANOSECONDS)` -| `42 μs` | `@Timeout(value = 42, unit = MICROSECONDS)` -| `42 ms` | `@Timeout(value = 42, unit = MILLISECONDS)` -| `42 s` | `@Timeout(value = 42, unit = SECONDS)` -| `42 m` | `@Timeout(value = 42, unit = MINUTES)` -| `42 h` | `@Timeout(value = 42, unit = HOURS)` -| `42 d` | `@Timeout(value = 42, unit = DAYS)` -|=== - - -[[writing-tests-declarative-timeouts-polling]] -==== Using @Timeout for Polling Tests - -When dealing with asynchronous code, it is common to write tests that poll while waiting -for something to happen before performing any assertions. In some cases you can rewrite -the logic to use a `CountDownLatch` or another synchronization mechanism, but sometimes -that is not possible — for example, if the subject under test sends a message to a channel -in an external message broker and assertions cannot be performed until the message has -been successfully sent through the channel. Asynchronous tests like these require some -form of timeout to ensure they don't hang the test suite by executing indefinitely, as -would be the case if an asynchronous message never gets successfully delivered. - -By configuring a timeout for an asynchronous test that polls, you can ensure that the test -does not execute indefinitely. The following example demonstrates how to achieve this with -JUnit Jupiter's `@Timeout` annotation. This technique can be used to implement "poll -until" logic very easily. - -[source,java] ----- -include::{testDir}/example/PollingTimeoutDemo.java[tags=user_guide,indent=0] ----- - -NOTE: If you need more control over polling intervals and greater flexibility with -asynchronous tests, consider using a dedicated library such as -link:https://github.com/awaitility/awaitility[Awaitility]. - - -[[writing-tests-declarative-timeouts-debugging]] -==== Debugging Timeouts - -Registered <> extensions are called prior to invoking -`Thread.interrupt()` on the thread that is executing the timed out method. This allows to -inspect the application state and output additional information that might be helpful for -diagnosing the cause of a timeout. - - -[[writing-tests-declarative-timeouts-debugging-thread-dump]] -===== Thread Dump on Timeout - -JUnit registers a default implementation of the <> -extension point that dumps the stacks of all threads to `System.out` if enabled by setting -the `junit.jupiter.execution.timeout.threaddump.enabled` -<> to `true`. - - -[[writing-tests-declarative-timeouts-mode]] -==== Disable @Timeout Globally - -When stepping through your code in a debug session, a fixed timeout limit may influence -the result of the test, e.g. mark the test as failed although all assertions were met. - -JUnit Jupiter supports the `junit.jupiter.execution.timeout.mode` configuration parameter -to configure when timeouts are applied. There are three modes: `enabled`, `disabled`, -and `disabled_on_debug`. The default mode is `enabled`. -A VM runtime is considered to run in debug mode when one of its input parameters starts -with `-agentlib:jdwp` or `-Xrunjdwp`. -This heuristic is queried by the `disabled_on_debug` mode. - - -[[writing-tests-parallel-execution]] -=== Parallel Execution - -By default, JUnit Jupiter tests are run sequentially in a single thread; however, running -tests in parallel -- for example, to speed up execution -- is available as an opt-in -feature. To enable parallel execution, set the `junit.jupiter.execution.parallel.enabled` -configuration parameter to `true` -- for example, in `junit-platform.properties` (see -<> for other options). - -Please note that enabling this property is only the first step required to execute tests -in parallel. If enabled, test classes and methods will still be executed sequentially by -default. Whether or not a node in the test tree is executed concurrently is controlled by -its execution mode. The following two modes are available. - -`SAME_THREAD`:: - Force execution in the same thread used by the parent. For example, when used on a test - method, the test method will be executed in the same thread as any `@BeforeAll` or - `@AfterAll` methods of the containing test class. - -`CONCURRENT`:: - Execute concurrently unless a resource lock forces execution in the same thread. - -By default, nodes in the test tree use the `SAME_THREAD` execution mode. You can change -the default by setting the `junit.jupiter.execution.parallel.mode.default` configuration -parameter. Alternatively, you can use the `{Execution}` annotation to change the -execution mode for the annotated element and its subelements (if any) which allows you to -activate parallel execution for individual test classes, one by one. - -[source,properties] -.Configuration parameters to execute all tests in parallel ----- -junit.jupiter.execution.parallel.enabled = true -junit.jupiter.execution.parallel.mode.default = concurrent ----- - -The default execution mode is applied to all nodes of the test tree with a few notable -exceptions, namely test classes that use the `Lifecycle.PER_CLASS` mode or a -`{MethodOrderer}`. In the former case, test authors have to ensure that the test class is -thread-safe; in the latter, concurrent execution might conflict with the configured -execution order. Thus, in both cases, test methods in such test classes are only executed -concurrently if the `@Execution(CONCURRENT)` annotation is present on the test class or -method. - -You can use the `@Execution` annotation to explicitly configure the execution mode for a -test class or method: - -[source,java] ----- -include::{testDir}/example/ExplicitExecutionModeDemo.java[tags=user_guide] ----- - -This allows test classes or methods to opt in or out of concurrent execution regardless of -the globally configured default. - -When parallel execution is enabled and a default `{ClassOrderer}` is registered (see -<> for details), top-level test classes will -initially be sorted accordingly and scheduled in that order. However, they are not -guaranteed to be started in exactly that order since the threads they are executed on are -not controlled directly by JUnit. - -All nodes of the test tree that are configured with the `CONCURRENT` execution mode will -be executed fully in parallel according to the provided -<> while observing the -declarative <> -mechanism. Please note that <> needs to be enabled -separately. - -In addition, you can configure the default execution mode for top-level classes by setting -the `junit.jupiter.execution.parallel.mode.classes.default` configuration parameter. By -combining both configuration parameters, you can configure classes to run in parallel but -their methods in the same thread: - -[source,properties] -.Configuration parameters to execute top-level classes in parallel but methods in same thread ----- -junit.jupiter.execution.parallel.enabled = true -junit.jupiter.execution.parallel.mode.default = same_thread -junit.jupiter.execution.parallel.mode.classes.default = concurrent ----- - -The opposite combination will run all methods within one class in parallel, but top-level -classes will run sequentially: - -[source,properties] -.Configuration parameters to execute top-level classes sequentially but their methods in parallel ----- -junit.jupiter.execution.parallel.enabled = true -junit.jupiter.execution.parallel.mode.default = concurrent -junit.jupiter.execution.parallel.mode.classes.default = same_thread ----- - -The following diagram illustrates how the execution of two top-level test classes `A` and -`B` with two test methods per class behaves for all four combinations of -`junit.jupiter.execution.parallel.mode.default` and -`junit.jupiter.execution.parallel.mode.classes.default` (see labels in first column). - -//// -Source: https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqFlE1u2zAQha9CEChio7IQKfVGXfUH_QEatICyKAIBwYQaW0QkUiDHhV3X2x4gvWFPUlKUbTmpEq2kN2-GHx403HKhS-QZn81mhSqlbWvYXDopY0I3LQgqVFcq1BIUuS_mnhIIP2jTALHvQYG1tL3ywgaJpLj7rAjND6hZsteoRvb39x9GlUEoLfvltMZL9_4M77EoSGrFJhYavAm-iA0-psH3Jia0lEymLANrk4idR_tjQintS2nEYOE4WLClwfP22H7b6QeP818MPWnvOcwJ_ldPAwutxMoYVPQ_XjHOKwa8YoT3tP0EUwww-_YHmEey52IV47EKH8dDhEAnBmmKR4mnvScdeNLnMJ8MU4yHKcQ45XiGgy4e8Qbdby1LtyNbby04VdhgwTP3qnBFBuqCR6EUdsSVtmFqwWtc0DcoS6mWXk_TebQv3YL5CK1Xk_ODuDSy_CIV5gRm2DiwuL5PKJdVd9DFUV9oRbn82aElc6_uogHxuzwP0DGBvbvCtcs17tO-6vZyy_yI2QIaWW8ydva1RcVyUPbsdahYNz1L5u2a7VjsSVnst5yRG-a6--sjU1rhqSNTVM1EJetykqqXyfSRueCF2rmwYUU63yjBMzIrjPiq9XfNewlLAw3PFlBbp2IpSZvLcHN1F1jEW1DXWu89u3-YPX1X - ---- -displayMode: compact ---- - -gantt - dateFormat X - axisFormat %s - tickInterval 1 - title ↓ threads | time → - - section (same_thread, same_thread) - A.test1() :ass1, 0, 1 - A.test2() :ass2, after ass1, 2 - B.test1() :bss1, after ass2, 3 - B.test2() :bss2, after bss1, 4 - - section (same_thread, concurrent) - A.test1() :asc1, 0, 1 - A.test2() :asc2, after asc1, 2 - B.test1() :bsc1, 0, 1 - B.test2() :bsc2, after bsc1, 2 - - section (concurrent, same_thread) - A.test1() :acs1, 0, 1 - A.test2() :acs2, 0, 1 - B.test1() :bcs1, after acs1, 2 - B.test2() :bcs2, after acs2, 2 - - section (concurrent, concurrent) - A.test1() :acc1, 0, 1 - A.test2() :acc2, 0, 1 - B.test1() :bcc1, 0, 1 - B.test2() :bcc2, 0, 1 - -//// -image::writing-tests_execution_mode.svg[caption='',title='Default execution mode configuration combinations'] - -If the `junit.jupiter.execution.parallel.mode.classes.default` configuration parameter is -not explicitly set, the value for `junit.jupiter.execution.parallel.mode.default` will be -used instead. - -[[writing-tests-parallel-execution-config]] -==== Configuration - -[[writing-tests-parallel-execution-config-executor-service]] -===== Executor Service - -If parallel execution is enabled, a thread pool is used behind the scenes to execute tests -concurrently. You can configure which implementation of `HierarchicalTestExecutorService` -is used be setting the `junit.jupiter.execution.parallel.config.executor-service` -configuration parameter to one of the following options: - -`fork_join_pool` (default):: -Use an executor service that is backed by a `ForkJoinPool` from the JDK. This will cause -tests to be executed in a `ForkJoinWorkerThread`. In some cases, usages of -`ForkJoinPool` in test or production code or calls to blocking JDK APIs may cause the -number of concurrently executing tests to increase. To avoid this situation, please use -`worker_thread_pool`. - -`worker_thread_pool` (experimental):: -Use an executor service that is backed by a regular thread pool and does not create -additional threads if test or production code uses `ForkJoinPool` or calls a blocking -API in the JDK. - -WARNING: Using `worker_thread_pool` is currently an _experimental_ feature. You're invited -to give it a try and provide feedback to the JUnit team so they can improve and eventually -<> this feature. - -[[writing-tests-parallel-execution-config-strategies]] -===== Strategies - -Properties such as the desired parallelism and the maximum pool size can be configured -using a `{ParallelExecutionConfigurationStrategy}`. The JUnit Platform provides two -implementations out of the box: `dynamic` and `fixed`. Alternatively, you may implement a -`custom` strategy. - -To select a strategy, set the `junit.jupiter.execution.parallel.config.strategy` -configuration parameter to one of the following options. - -`dynamic`:: - Computes the desired parallelism based on the number of available processors/cores - multiplied by the `junit.jupiter.execution.parallel.config.dynamic.factor` - configuration parameter (defaults to `1`). - The optional `junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor` - configuration parameter can be used to limit the maximum number of threads. - -`fixed`:: - Uses the mandatory `junit.jupiter.execution.parallel.config.fixed.parallelism` - configuration parameter as the desired parallelism. - The optional `junit.jupiter.execution.parallel.config.fixed.max-pool-size` - configuration parameter can be used to limit the maximum number of threads. - -`custom`:: - Allows you to specify a custom `{ParallelExecutionConfigurationStrategy}` - implementation via the mandatory `junit.jupiter.execution.parallel.config.custom.class` - configuration parameter to determine the desired configuration. - -If no configuration strategy is set, JUnit Jupiter uses the `dynamic` configuration -strategy with a factor of `1`. Consequently, the desired parallelism will be equal to the -number of available processors/cores. - -.Parallelism alone does not imply maximum number of concurrent threads -NOTE: By default, JUnit Jupiter does not guarantee that the number of threads used to -execute test will not exceed the configured parallelism. For example, when using one -of the synchronization mechanisms described in the next section, the executor service -implementation may spawn additional threads to ensure execution continues with sufficient -parallelism. If you require such guarantees, it is possible to limit the maximum number of -threads by configuring the maximum pool size of the `dynamic`, `fixed` and `custom` -strategies. - -[[writing-tests-parallel-execution-config-properties]] -===== Relevant properties - -The following table lists relevant properties for configuring parallel execution. See -<> for details on how to set such properties. - -====== General - -`junit.jupiter.execution.parallel.enabled=true|false`:: - Enable/disable parallel test execution (defaults to `false`). - -`junit.jupiter.execution.parallel.mode.default=concurrent|same_thread`:: - Default execution mode of nodes in the test tree (defaults to `same_thread`). - -`junit.jupiter.execution.parallel.mode.classes.default=concurrent|same_thread`:: - Default execution mode of top-level classes (defaults to `same_thread`). - -`junit.jupiter.execution.parallel.config.executor-service=fork_join_pool|worker_thread_pool`:: - Type of `HierarchicalTestExecutorService` to use for parallel execution (defaults to - `fork_join_pool`). - -`junit.jupiter.execution.parallel.config.strategy=dynamic|fixed|custom`:: - Execution strategy for desired parallelism, maximum pool size, etc. (defaults to `dynamic`). - -====== Dynamic strategy - -`junit.jupiter.execution.parallel.config.dynamic.factor=decimal`:: - Factor to be multiplied by the number of available processors/cores to determine the - desired parallelism for the ```dynamic``` configuration strategy. - Must be a positive decimal number (defaults to `1.0`). - -`junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor=decimal`:: - Factor to be multiplied by the number of available processors/cores and the value of - `junit.jupiter.execution.parallel.config.dynamic.factor` to determine the desired - parallelism for the ```dynamic``` configuration strategy. - Must be a positive decimal number greater than or equal to `1.0` (defaults to 256 plus - the value of `junit.jupiter.execution.parallel.config.dynamic.factor` multiplied by the - number of available processors/cores) - -`junit.jupiter.execution.parallel.config.dynamic.saturate=true|false`:: - Enable/disable saturation of the underlying `ForkJoinPool` for the ```dynamic``` - configuration strategy (defaults to `true`). Only used if - `junit.jupiter.execution.parallel.config.executor-service` is set to `fork_join_pool`. - -====== Fixed strategy - -`junit.jupiter.execution.parallel.config.fixed.parallelism=integer`:: - Desired parallelism for the ```fixed``` configuration strategy (no default value). Must - be a positive integer. - -`junit.jupiter.execution.parallel.config.fixed.max-pool-size=integer`:: - Desired maximum pool size of the underlying fork-join pool for the ```fixed``` - configuration strategy. Must be a positive integer greater than or equal to - `junit.jupiter.execution.parallel.config.fixed.parallelism` (defaults to 256 plus the - value of `junit.jupiter.execution.parallel.config.fixed.parallelism`). - -`junit.jupiter.execution.parallel.config.fixed.saturate=true|false`:: - Enable/disable saturation of the underlying `ForkJoinPool` for the ```fixed``` - configuration strategy (defaults to `true`). Only used if - `junit.jupiter.execution.parallel.config.executor-service` is set to `fork_join_pool`. - -====== Custom strategy - -`junit.jupiter.execution.parallel.config.custom.class=classname`:: - Fully qualified class name of the `ParallelExecutionConfigurationStrategy` to be used - for the ```custom``` configuration strategy (no default value). - -[[writing-tests-parallel-execution-synchronization]] -==== Synchronization - -In addition to controlling the execution mode using the `{Execution}` annotation, JUnit -Jupiter provides another annotation-based declarative synchronization mechanism. The -`{ResourceLock}` annotation allows you to declare that a test class or method uses a -specific shared resource that requires synchronized access to ensure reliable test -execution. The shared resource is identified by a unique name which is a `String`. The -name can be user-defined or one of the predefined constants in `{Resources}`: -`SYSTEM_PROPERTIES`, `SYSTEM_OUT`, `SYSTEM_ERR`, `LOCALE`, or `TIME_ZONE`. - -In addition to declaring these shared resources statically, the `{ResourceLock}` -annotation has a `providers` attribute that allows registering implementations of the -`{ResourceLocksProvider}` interface that can add shared resources dynamically at runtime. -Note that resources declared statically with `{ResourceLock}` annotation are combined with -resources added dynamically by `{ResourceLocksProvider}` implementations. - -If the tests in the following example were run in parallel _without_ the use of -`{ResourceLock}`, they would be _flaky_. Sometimes they would pass, and at other times they -would fail due to the inherent race condition of writing and then reading the same JVM -System Property. - -When access to shared resources is declared using the `{ResourceLock}` annotation, the -JUnit Jupiter engine uses this information to ensure that no conflicting tests are run in -parallel. This guarantee extends to lifecycle methods of a test class or method. For -example, if a test method is annotated with a `{ResourceLock}` annotation, the "lock" will -be acquired before any `@BeforeEach` methods are executed and released after all -`@AfterEach` methods have been executed. - -[NOTE] -.Running tests in isolation -==== -If most of your test classes can be run in parallel without any synchronization but you -have some test classes that need to run in isolation, you can mark the latter with the -`{Isolated}` annotation. Tests in such classes are executed sequentially without any other -tests running at the same time. -==== - -In addition to the `String` that uniquely identifies the shared resource, you may specify -an access mode. Two tests that require `READ` access to a shared resource may run in -parallel with each other but not while any other test that requires `READ_WRITE` access -to the same shared resource is running. - -[source,java] -.Declaring shared resources "statically" with `{ResourceLock}` annotation ----- -include::{testDir}/example/sharedresources/StaticSharedResourcesDemo.java[tags=user_guide] ----- - -[source,java] -.Adding shared resources "dynamically" with `{ResourceLocksProvider}` implementation ----- -include::{testDir}/example/sharedresources/DynamicSharedResourcesDemo.java[tags=user_guide] ----- - -Also, "static" shared resources can be declared for _direct_ child nodes via the `target` -attribute in the `{ResourceLock}` annotation, the attribute accepts a value from -the `{ResourceLockTarget}` enum. - -Specifying `target = CHILDREN` in a class-level `{ResourceLock}` annotation -has the same semantics as adding an annotation with the same `value` and `mode` -to each test method and nested test class declared in this class. - -This may improve parallelization when a test class declares a `READ` lock, -but only a few methods hold a `READ_WRITE` lock. - -Tests in the following example would run in the `SAME_THREAD` if the `{ResourceLock}` -didn't have `target = CHILDREN`. This is because the test class declares a `READ` -shared resource, but one test method holds a `READ_WRITE` lock, -which would force the `SAME_THREAD` execution mode for all the test methods. - -[source,java] -.Declaring shared resources for child nodes with `target` attribute ----- -include::{testDir}/example/sharedresources/ChildrenSharedResourcesDemo.java[tags=user_guide] ----- - - -[[writing-tests-built-in-extensions]] -=== Built-in Extensions - -While the JUnit team encourages reusable extensions to be packaged and maintained in -separate libraries, JUnit Jupiter includes a few user-facing extension implementations -that are considered so generally useful that users shouldn't have to add another -dependency. - -[[writing-tests-built-in-extensions-TempDirectory]] -==== The @TempDir Extension - -The built-in `{TempDirectory}` extension is used to create and clean up a temporary -directory for an individual test or all tests in a test class. It is registered by -default. To use it, annotate a non-final, unassigned field of type `java.nio.file.Path` or -`java.io.File` with `{TempDir}` or add a parameter of type `java.nio.file.Path` or -`java.io.File` annotated with `@TempDir` to a test class constructor, lifecycle method, or -test method. - -For example, the following test declares a parameter annotated with `@TempDir` for a -single test method, creates and writes to a file in the temporary directory, and checks -its content. - -[source,java,indent=0] -.A test method that requires a temporary directory ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_parameter_injection] ----- - -You can inject multiple temporary directories by specifying multiple annotated parameters. - -[source,java,indent=0] -.A test method that requires multiple temporary directories ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_multiple_directories] ----- - -The following example stores a _shared_ temporary directory in a `static` field. This -allows the same `sharedTempDir` to be used in all lifecycle methods and test methods of -the test class. For better isolation, you should use an instance field or constructor -injection so that each test method uses a separate directory. - -[source,java,indent=0] -.A test class that shares a temporary directory across test methods ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_field_injection] ----- - -The `@TempDir` annotation has an optional `cleanup` attribute that can be set to either -`NEVER`, `ON_SUCCESS`, or `ALWAYS`. If the cleanup mode is set to `NEVER`, the temporary -directory will not be deleted after the test completes. If it is set to `ON_SUCCESS`, the -temporary directory will only be deleted after the test if the test completed successfully. - -The default cleanup mode is `ALWAYS`. You can use the -`junit.jupiter.tempdir.cleanup.mode.default` -<> to override this default. - -[source,java,indent=0] -.A test class with a temporary directory that doesn't get cleaned up ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_cleanup_mode] ----- - -`@TempDir` supports the programmatic creation of temporary directories via the optional -`factory` attribute. This is typically used to gain control over the temporary directory -creation, like defining the parent directory or the file system that should be used. - -Factories can be created by implementing `TempDirFactory`. Implementations must provide a -no-args constructor and should not make any assumptions regarding when and how many times -they are instantiated, but they can assume that their `createTempDirectory(...)` and -`close()` methods will both be called once per instance, in this order, and from the same -thread. - -The default implementation available in Jupiter delegates directory creation to -`java.nio.file.Files::createTempDirectory` which uses the default file system and the -system's temporary directory as the parent directory. It passes `junit-` as the prefix -string of the generated directory name to help identify it as a created by JUnit. - -The following example defines a factory that uses the test name as the directory name -prefix instead of the `junit` constant value. - -[source,java,indent=0] -.A test class with a temporary directory having the test name as the directory name prefix ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_factory_name_prefix] ----- - -It is also possible to use an in-memory file system like `{Jimfs}` for the creation of the -temporary directory. The following example demonstrates how to achieve that. - -[source,java,indent=0] -.A test class with a temporary directory created with the Jimfs in-memory file system ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_factory_jimfs] ----- - -`@TempDir` can also be used as a <> to -reduce repetition. The following code listing shows how to create a custom `@JimfsTempDir` -annotation that can be used as a drop-in replacement for -`@TempDir(factory = JimfsTempDirFactory.class)`. - -[source,java,indent=0] -.A custom annotation meta-annotated with `@TempDir` ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation] ----- - -The following example demonstrates how to use the custom `@JimfsTempDir` annotation. - -[source,java,indent=0] -.A test class using the custom annotation ----- -include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation_usage] ----- - -Meta-annotations or additional annotations on the field or parameter the `TempDir` -annotation is declared on might expose additional attributes to configure the factory. -Such annotations and related attributes can be accessed via the `AnnotatedElementContext` -parameter of the `createTempDirectory(...)` method. - -You can use the `junit.jupiter.tempdir.factory.default` <> to specify the fully qualified class name of the -`TempDirFactory` you would like to use by default. Just like for factories configured via -the `factory` attribute of the `@TempDir` annotation, the supplied class has to implement -the `TempDirFactory` interface. The default factory will be used for all `@TempDir` -annotations unless the `factory` attribute of the annotation specifies a different factory. - -In summary, the factory for a temporary directory is determined according to the following -precedence rules: - -1. The `factory` attribute of the `@TempDir` annotation, if present -2. The default `TempDirFactory` configured via the configuration -parameter, if present -3. Otherwise, `org.junit.jupiter.api.io.TempDirFactory$Standard` will be used. - -[[writing-tests-built-in-extensions-AutoClose]] -==== The @AutoClose Extension - -The built-in `{AutoCloseExtension}` automatically closes resources associated with fields. -It is registered by default. To use it, annotate a field in a test class with -`{AutoClose}`. - -`@AutoClose` fields may be either `static` or non-static. If the value of an `@AutoClose` -field is `null` when it is evaluated the field will be ignored, but a warning message will -be logged to inform you. - -By default, `@AutoClose` expects the value of the annotated field to implement a `close()` -method that will be invoked to close the resource. However, developers can customize the -name of the close method via the `value` attribute. For example, `@AutoClose("shutdown")` -instructs JUnit to look for a `shutdown()` method to close the resource. - -`@AutoClose` fields are inherited from superclasses. Furthermore, `@AutoClose` fields from -subclasses will be closed before `@AutoClose` fields in superclasses. - -When multiple `@AutoClose` fields exist within a given test class, the order in which the -resources are closed depends on an algorithm that is deterministic but intentionally -nonobvious. This ensures that subsequent runs of a test suite close resources in the same -order, thereby allowing for repeatable builds. - -The `AutoCloseExtension` implements the `AfterAllCallback` and -`TestInstancePreDestroyCallback` extension APIs. Consequently, a `static` `@AutoClose` -field will be closed after all tests in the current test class have completed, effectively -after `@AfterAll` methods have executed for the test class. A non-static `@AutoClose` -field will be closed before the current test class instance is destroyed. Specifically, if -the test class is configured with `@TestInstance(Lifecycle.PER_METHOD)` semantics, a -non-static `@AutoClose` field will be closed after the execution of each test method, test -factory method, or test template method. However, if the test class is configured with -`@TestInstance(Lifecycle.PER_CLASS)` semantics, a non-static `@AutoClose` field will not -be closed until the current test class instance is no longer needed, which means after -`@AfterAll` methods and after all `static` `@AutoClose` fields have been closed. - -The following example demonstrates how to annotate an instance field with `@AutoClose` so -that the resource is automatically closed after test execution. In this example, we assume -that the default `@TestInstance(Lifecycle.PER_METHOD)` semantics apply. - -[source,java,indent=0] -.A test class using `@AutoClose` to close a resource ----- -include::{testDir}/example/AutoCloseDemo.java[tags=user_guide_example] ----- -<1> Annotate an instance field with `@AutoClose`. -<2> `WebClient` implements `java.lang.AutoCloseable` which defines a `close()` method that - will be invoked after each `@Test` method. - -[[writing-tests-built-in-extensions-DefaultLocaleAndTimeZone]] -==== The @DefaultLocale and @DefaultTimeZone Extensions - -The `{DefaultLocale}` and `{DefaultTimeZone}` annotations can be used to change the values -returned from `Locale.getDefault()` and `TimeZone.getDefault()`, respectively, which are -often used implicitly when no specific locale or time zone is chosen. Both annotations -work on the test class level and on the test method level, and are inherited from -higher-level containers. After the annotated element has been executed, the initial -default value is restored. - -[[writing-tests-built-in-extensions-DefaultLocale]] -===== @DefaultLocale - -The default `Locale` can be specified using an -{jdk-javadoc-base-url}/java.base/java/util/Locale.html#forLanguageTag-java.lang.String-[IETF BCP 47 language tag string]. - -[source,java,indent=0] ----- -include::{testDir}/example/DefaultLocaleTimezoneExtensionDemo.java[tags=default_locale_language] ----- - -Alternatively, the default `Locale` can be created using the following attributes from -which a {jdk-javadoc-base-url}/java.base/java/util/Locale.Builder.html[`Locale.Builder`] -can create an instance: - -* `language` or -* `language` and `country` or -* `language`, `country`, and `variant` - -NOTE: The variant needs to be a string which follows the -https://www.rfc-editor.org/rfc/rfc5646.html[IETF BCP 47 / RFC 5646] syntax - -[source,java,indent=0] ----- -include::{testDir}/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_language_alternatives] ----- - -Mixing language tag configuration (via the annotation's `value` attributed) and -attributed-based configuration will cause an exception to be thrown. Furthermore, a -`variant` can only be specified if `country` is also specified. Otherwise, an exception -will be thrown. - -Any method-level `@DefaultLocale` configurations will override class-level configurations. - -[source,java,indent=0] ----- -include::{testDir}/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_class_level] ----- - -NOTE: A class-level configuration means that the specified locale is set before and reset -after each individual test in the annotated class. - -If your use case is not covered, you can implement the `{LocaleProvider}` interface. - -[source,java,indent=0] ----- -include::{testDir}/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_with_provider] ----- - -NOTE: The provider implementation must have a no-args (or the default) constructor. - -[[writing-tests-built-in-extensions-DefaultTimeZone]] -===== @DefaultTimeZone - -The default `TimeZone` is specified according to the -{jdk-javadoc-base-url}/java.base/java/util/TimeZone.html#getTimeZone(java.lang.String)[TimeZone.getTimeZone(String)] -method. - -[source,java,indent=0] ----- -include::{testDir}/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_zone] ----- - -Any method level `@DefaultTimeZone` configurations will override class level configurations: - -[source,java,indent=0] ----- -include::{testDir}/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_class_level] ----- - -NOTE: A class-level configuration means that the specified time zone is set before and -reset after each individual test in the annotated class. - -If your use case is not covered, you can implement the `{TimeZoneProvider}` interface. - -[source,java,indent=0] ----- -include::{testDir}/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_time_zone_with_provider] ----- - -NOTE: The provider implementation must have a no-args (or the default) constructor. - -===== Thread Safety - -Since the default locale and time zone are global state, reading and writing them during -<> can lead to unpredictable -results and flaky tests. The `@DefaultLocale` and `@DefaultTimeZone` extensions are -prepared for that and tests annotated with them will never execute in parallel (thanks to -`{ResourceLock}`) to guarantee correct test results. - -However, this does not cover all possible cases. Tested code that reads or writes default -locale and time zone _independently_ of the extensions can still run in parallel to them -and may thus behave erratically when, for example, it unexpectedly reads a locale set by -the extension in another thread. Tests that cover code that reads or writes the default -locale or time zone need to be annotated with the respective annotation: - -* `{ReadsDefaultLocale}` -* `{ReadsDefaultTimeZone}` -* `{WritesDefaultLocale}` -* `{WritesDefaultTimeZone}` - -Tests annotated in this way will never execute in parallel with tests annotated with -`@DefaultLocale` or `@DefaultTimeZone`. diff --git a/documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java index 5d2526b03f49..1943b3e7865f 100644 --- a/documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java @@ -60,12 +60,12 @@ protected void printDeclarationSection(Set statuses, Status status, List return; } declarationsByModule.forEach((moduleName, moduleDeclarations) -> { - out.println(h4("Module " + moduleName)); + out.println(h3("Module " + moduleName)); out.println(); moduleDeclarations.stream() // .collect(groupingBy(Declaration::packageName, TreeMap::new, toList())) // .forEach((packageName, packageDeclarations) -> { - out.println(h5("Package " + packageName)); + out.println(h4("Package " + packageName)); out.println(); printDeclarationTableHeader(out); packageDeclarations.forEach(it -> printDeclarationTableRow(it, out)); @@ -92,9 +92,9 @@ protected void printDeclarationSectionHeader(Set statuses, Status status protected abstract String h2(String header); - protected abstract String h4(String header); + protected abstract String h3(String header); - protected abstract String h5(String header); + protected abstract String h4(String header); protected abstract String code(String element); diff --git a/documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java index 0a285d3ffdea..59fe54b90154 100644 --- a/documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java @@ -34,13 +34,13 @@ protected String h2(String header) { } @Override - protected String h4(String header) { - return "[discrete]%n==== %s".formatted(header); + protected String h3(String header) { + return "%n=== %s".formatted(header); } @Override - protected String h5(String header) { - return "[discrete]%n===== %s".formatted(header); + protected String h4(String header) { + return "%n==== %s".formatted(header); } @Override diff --git a/documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java index c0f5a237b00a..8b2cfc5870ea 100644 --- a/documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java @@ -35,13 +35,13 @@ protected String h2(String header) { } @Override - protected String h4(String header) { - return "

" + header + "

"; + protected String h3(String header) { + return "

" + header + "

"; } @Override - protected String h5(String header) { - return "
" + header + "
"; + protected String h4(String header) { + return "

" + header + "

"; } @Override diff --git a/documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java b/documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java index 458d6c721e8a..26ff3c38e4d5 100644 --- a/documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java +++ b/documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java @@ -35,13 +35,13 @@ protected String h2(String header) { } @Override - protected String h4(String header) { - return "#### " + header; + protected String h3(String header) { + return "### " + header; } @Override - protected String h5(String header) { - return "##### " + header; + protected String h4(String header) { + return "#### " + header; } @Override diff --git a/gradle/config/checkstyle/suppressions.xml b/gradle/config/checkstyle/suppressions.xml index 7572f8b1a4d6..092a2a2dadc9 100644 --- a/gradle/config/checkstyle/suppressions.xml +++ b/gradle/config/checkstyle/suppressions.xml @@ -3,5 +3,5 @@ + files="documentation[\\/]modules[\\/]ROOT[\\/]images[\\/]extensions_StoreHierarchy.svg"/> diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9a9c2814afe1..6f06005720c2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,6 @@ [versions] ant = "1.10.15" apiguardian = "1.1.2" -asciidoctorj-pdf = "2.3.23" -asciidoctor-plugins = "4.0.5" # Check if workaround in documentation.gradle.kts can be removed when upgrading assertj = "3.27.6" bnd = "7.1.0" checkstyle = "12.2.0" @@ -10,7 +8,6 @@ eclipse = "4.37.0" jackson = "2.20.1" jacoco = "0.8.14" jmh = "1.37" -jruby = "9.4.14.0" junit4 = "4.13.2" junit4Min = "4.12" ktlint = "1.8.0" @@ -79,10 +76,8 @@ testingAnnotations = { module = "com.gradle:develocity-testing-annotations", ver woodstox = { module = "com.fasterxml.woodstox:woodstox-core", version = "7.1.1" } # Only declared here so Dependabot knows when to update the referenced versions -asciidoctorj-pdf = { module = "org.asciidoctor:asciidoctorj-pdf", version.ref = "asciidoctorj-pdf" } eclipse-platform = { module = "org.eclipse.platform:org.eclipse.platform", version.ref = "eclipse" } jacoco = { module = "org.jacoco:jacoco", version.ref = "jacoco" } -jruby = { module = "org.jruby:jruby", version.ref = "jruby" } junit4-latest = { module = "junit:junit", version.ref = "junit4" } junit4-bundle = { module = "org.apache.servicemix.bundles:org.apache.servicemix.bundles.junit", version = "4.13.2_1" } ktlint-cli = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint" } @@ -93,8 +88,6 @@ log4j = ["log4j-core", "log4j-jul"] xmlunit = ["xmlunit-assertj", "xmlunit-placeholders", "xmlunit-jakarta-jaxb-impl", "jaxb-api", "jaxb-runtime"] [plugins] -asciidoctorConvert = { id = "org.asciidoctor.jvm.convert", version.ref = "asciidoctor-plugins" } -asciidoctorPdf = { id = "org.asciidoctor.jvm.pdf", version.ref = "asciidoctor-plugins" } bnd = { id = "biz.aQute.bnd", version.ref = "bnd" } buildParameters = { id = "org.gradlex.build-parameters", version = "1.4.4" } commonCustomUserData = { id = "com.gradle.common-custom-user-data-gradle-plugin", version = "2.4.0" } @@ -102,12 +95,13 @@ develocity = { id = "com.gradle.develocity", version = "4.2.2" } download = { id = "de.undercouch.download", version = "5.6.0" } errorProne = { id = "net.ltgt.errorprone", version = "4.3.0" } foojayResolver = { id = "org.gradle.toolchains.foojay-resolver", version = "1.0.0" } -gitPublish = { id = "org.ajoberstar.git-publish", version = "5.1.3" } jmh = { id = "me.champeau.jmh", version = "0.7.3" } jreleaser = { id = "org.jreleaser", version = "1.21.0" } # check if workaround in gradle.properties can be removed when updating kotlin = { id = "org.jetbrains.kotlin.jvm", version = "2.2.21" } +node = { id = "com.github.node-gradle.node", version = "7.1.0" } nullaway = { id = "net.ltgt.nullaway", version = "2.3.0" } plantuml = { id = "io.freefair.plantuml", version = "9.1.0" } shadow = { id = "com.gradleup.shadow", version = "9.3.0" } spotless = { id = "com.diffplug.spotless", version = "8.1.0" } +spring-antora = { id = "io.spring.antora.generate-antora-yml", version = "0.0.1" } diff --git a/gradle/plugins/antora/build.gradle.kts b/gradle/plugins/antora/build.gradle.kts new file mode 100644 index 000000000000..1e1dab1109c6 --- /dev/null +++ b/gradle/plugins/antora/build.gradle.kts @@ -0,0 +1,23 @@ +import junitbuild.extensions.markerCoordinates +import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21 + +plugins { + `kotlin-dsl` +} + +dependencies { + implementation(projects.buildParameters) + implementation(libs.plugins.node.markerCoordinates) + implementation(libs.plugins.spring.antora.markerCoordinates) +} + +tasks.compileJava { + options.release = 21 +} + +kotlin { + compilerOptions { + jvmTarget = JVM_21 + freeCompilerArgs.add("-Xjdk-release=21") + } +} diff --git a/gradle/plugins/antora/src/main/kotlin/junitbuild.antora-conventions.gradle.kts b/gradle/plugins/antora/src/main/kotlin/junitbuild.antora-conventions.gradle.kts new file mode 100644 index 000000000000..6cf648dcfef3 --- /dev/null +++ b/gradle/plugins/antora/src/main/kotlin/junitbuild.antora-conventions.gradle.kts @@ -0,0 +1,74 @@ +import com.github.gradle.node.npm.task.NpxTask + +plugins { + id("com.github.node-gradle.node") + id("io.spring.antora.generate-antora-yml") + id("junitbuild.build-parameters") +} + +repositories { + // Redefined here because the Node.js plugin adds a repo + mavenCentral() +} + +tasks.register("generateAntoraResources") { + dependsOn("generateAntoraYml") +} + +val generateAntoraPlaybook by tasks.registering(Copy::class) { + + val gitRepoRoot = providers.exec { + commandLine("git", "worktree", "list", "--porcelain", "-z") + }.standardOutput.asText.map { it.substringBefore('\u0000').substringAfter(' ') } + + val gitBranchName = providers.exec { + commandLine("git", "rev-parse", "--abbrev-ref", "HEAD") + }.standardOutput.asText.map { it.trim() } + + from(layout.projectDirectory.file("antora-playbook.yml").asFile) + filter { line -> + var result = line + if (line.contains("@GIT_REPO_ROOT@")) { + result = result.replace("@GIT_REPO_ROOT@", gitRepoRoot.get()) + } + if (line.contains("@GIT_BRANCH_NAME@")) { + result = result.replace("@GIT_BRANCH_NAME@", gitBranchName.get()) + } + return@filter result + } + into(layout.buildDirectory.dir("antora-playbook")) +} + +node { + download = buildParameters.antora.downloadNode + version = providers.fileContents(layout.projectDirectory.file(".tool-versions")).asText.map { + it.substringAfter("nodejs").trim() + } +} + +tasks.npmInstall { + args.addAll("--no-audit", "--no-package-lock", "--no-fund") +} + +tasks.register("antora") { + dependsOn(tasks.npmInstall) + description = "Runs Antora to generate a documentation site described by the playbook file." + + command = "antora" + args.addAll("--clean", "--stacktrace", "--fetch") + + args.add("--to-dir") + val outputDir = layout.buildDirectory.dir("antora-site") + args.add(outputDir.map { it.asFile.toRelativeString(layout.projectDirectory.asFile) }) + outputs.dir(outputDir) + + outputs.upToDateWhen { false } // not all inputs are tracked + + val playbook = generateAntoraPlaybook.map { it.rootSpec.destinationDir.resolve("antora-playbook.yml") } + args.add(playbook.map { it.toRelativeString(layout.projectDirectory.asFile) }) + inputs.file(playbook) + + doLast { + println("Antora site built in ${outputDir.get().asFile.absoluteFile}") + } +} diff --git a/gradle/plugins/build-parameters/build.gradle.kts b/gradle/plugins/build-parameters/build.gradle.kts index 8cdab340d4c3..da78534d2750 100644 --- a/gradle/plugins/build-parameters/build.gradle.kts +++ b/gradle/plugins/build-parameters/build.gradle.kts @@ -114,4 +114,10 @@ buildParameters { description = "Overrides the value of the 'Created-By' jar manifest entry" } } + group("antora") { + bool("downloadNode") { + description = "Whether to download Node.js from the Gradle build" + defaultValue = true + } + } } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.checkstyle-nohttp.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.checkstyle-nohttp.gradle.kts index 74af245a3d47..2a2dc620c443 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.checkstyle-nohttp.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.checkstyle-nohttp.gradle.kts @@ -37,5 +37,6 @@ tasks.register("checkstyleNohttp") { exclude("**/*.jks") exclude("**/build/**") exclude("**/.kotlin") + exclude("**/node_modules/**") } } diff --git a/gradle/plugins/settings.gradle.kts b/gradle/plugins/settings.gradle.kts index b2ae97139041..2399fe456afc 100644 --- a/gradle/plugins/settings.gradle.kts +++ b/gradle/plugins/settings.gradle.kts @@ -19,6 +19,7 @@ dependencyResolutionManagement { rootProject.name = "plugins" +include("antora") include("backward-compatibility") include("build-parameters") include("common") diff --git a/settings.gradle.kts b/settings.gradle.kts index 22cff4977cde..590103aa62c4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,7 +17,6 @@ dependencyResolutionManagement { repositories { mavenCentral() } - repositoriesMode = FAIL_ON_PROJECT_REPOS } val buildParameters = the()