= 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 `{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.

.A test method that requires a temporary directory
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_parameter_injection]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_parameter_injection]
----
--
====

You can inject multiple temporary directories by specifying multiple annotated parameters.

.A test method that requires multiple temporary directories
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_multiple_directories]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[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.

.A test class that shares a temporary directory across test methods
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_field_injection]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[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.

.A test class with a temporary directory that doesn't get cleaned up
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_cleanup_mode]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_cleanup_mode]
----
--
====

[[TempDirFactory]]
=== Factories

`@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.

.A test class with a temporary directory having the test name as the directory name prefix
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_factory_name_prefix]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[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.

.A test class with a temporary directory created with the Jimfs in-memory file system
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_factory_jimfs]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[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)`.

.A custom annotation meta-annotated with `@TempDir`
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_composed_annotation]
----
--
====

The following example demonstrates how to use the custom `@JimfsTempDir` annotation.

.A test class using the custom annotation
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation_usage]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[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.

[[TempDirDeletionStrategy]]
=== Deletion

`@TempDir` supports the programmatic deletion of temporary directories via the optional
`deletionStrategy` attribute. This is typically used to gain control over what happens
when deletion of a file or directory fails.

Deletion strategies can be created by implementing `{TempDirDeletionStrategy}`.
Implementations must provide a no-args constructor.

Jupiter ships with two built-in deletion strategies:

* `{TempDirDeletionStrategyStandard}` (the default): attempts to delete all files and
  directories recursively, retrying with permission resets on failure. Paths that still
  cannot be deleted are scheduled for deletion on JVM exit, if possible. Additionally,
  the test is failed.
* `{TempDirDeletionStrategyIgnoreFailures}`: delegates to `{TempDirDeletionStrategyStandard}`
  but suppresses deletion failures by logging a warning instead of failing the test.

The following example uses `{TempDirDeletionStrategyIgnoreFailures}` so that any deletion
failures are only logged.

.A test class with a temporary directory that ignores deletion failures
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/TempDirectoryDemo.java[tags=user_guide_deletion_strategy]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_deletion_strategy]
----
--
====

You can use the `junit.jupiter.tempdir.deletion.strategy.default`
xref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the
fully qualified class name of the `{TempDirDeletionStrategy}` you would like to use by
default. Just like for strategies configured via the `deletionStrategy` attribute of the
`@TempDir` annotation, the supplied class has to implement the `{TempDirDeletionStrategy}`
interface. The default strategy will be used for all `@TempDir` annotations unless the
`deletionStrategy` attribute of the annotation specifies a different strategy.

In summary, the deletion strategy for a temporary directory is determined according to the
following precedence rules:

1. The `deletionStrategy` attribute of the `@TempDir` annotation, if present
2. The default `{TempDirDeletionStrategy}` configured via the configuration parameter,
if present
3. Otherwise, `org.junit.jupiter.api.io.TempDirDeletionStrategy$Standard` will be used.

[[AutoClose]]
== The @AutoClose Extension

The `{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.

.A test class using `@AutoClose` to close a resource
[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/AutoCloseDemo.java[tags=user_guide_example]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/AutoCloseDemo.kt[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].

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tags=default_locale_language]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[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`
* `language` and `country`
* `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.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_language_alternatives]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_locale_language_alternatives]
----
--
====

Mixing language tag configuration (via the annotation's `value` attribute) and
attribute-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.

Method-level `@DefaultLocale` configuration overrides class-level configuration.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_class_level]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_locale_class_level]
----
--
====

NOTE: With class-level configuration, 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.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_with_provider]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_locale_with_provider]
----
--
====

NOTE: The provider implementation must have a no-args (or 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.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_zone]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_timezone_zone]
----
--
====

Method-level `@DefaultTimeZone` configuration overrides class-level configuration.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_class_level]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_timezone_class_level]
----
--
====

NOTE: With class-level configuration, 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.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_time_zone_with_provider]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_time_zone_with_provider]
----
--
====

NOTE: The provider implementation must have a no-args (or 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 the
default locale or time zone _independently_ of the extensions can still run in parallel
and may thus behave erratically when, for example, such code unexpectedly reads a locale
set by the extension in another thread. Consequently, tests that cover code that reads or
writes the default locale or time zone need to be annotated with one of the following
respective annotations.

* `{ReadsDefaultLocale}`
* `{ReadsDefaultTimeZone}`
* `{WritesDefaultLocale}`
* `{WritesDefaultTimeZone}`

Tests annotated with one of the above annotations will never execute in parallel with
tests annotated with `@DefaultLocale` or `@DefaultTimeZone`.

[[system-properties]]
== The System Properties Extension

The system properties extension supports a set of annotations that work together to clear,
set, and restore JVM system properties.

[[system-properties-clear-and-set]]
=== @ClearSystemProperty and @SetSystemProperty

The `{ClearSystemProperty}` and `{SetSystemProperty}` annotations can be used to clear and
set, respectively, the values of JVM system properties for test execution. Both
annotations work on the test method and class level and are repeatable, combinable, and
inherited from higher-level containers. After the annotated method has been executed, the
properties configured in the annotation will be restored to their original value or the
value of the higher-level container, or will be cleared if they did not previously have a
value. Other system properties that are changed during the test are _not_ restored (unless
restoration is <<system-properties-restore, explicitly enabled>> via
`{RestoreSystemProperties}`).

For example, clearing a system property for test execution can be done as follows.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_clear_simple]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_clear_simple]
----
--
====

The following demonstrates how to set a system property for test execution.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_set_simple]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_set_simple]
----
--
====

As mentioned before, both annotations are repeatable, and they can also be combined.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_using_set_and_clear]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_using_set_and_clear]
----
--
====

Note that class-level configuration is overridden by method-level configuration.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_using_at_class_level]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_using_at_class_level]
----
--
====

[NOTE]
====
Method-level configuration is visible in both `@BeforeEach` methods and `@AfterEach`
methods (see
xref:extensions/relative-execution-order-of-user-code-and-extensions.adoc#overview[user
code and extension code execution order]).

With class-level configuration, the specified system properties are cleared or set before
and reset after each individual test in the annotated class.
====

[[system-properties-restore]]
=== @RestoreSystemProperties

The `{RestoreSystemProperties}` annotation can be used to restore changes to system
properties made directly in the test or in the code being tested. Although
`@ClearSystemProperty` and `@SetSystemProperty` clear or set properties and values that
are statically declared, they do not allow property values to be calculated dynamically.
Thus, there are times you may want to directly set properties in your test code.
`@RestoreSystemProperties` can be placed on test methods or test classes and will
completely restore all system properties to their original state after the test or test
class has finished.

[NOTE]
====
During the execution of the annotated scope, the JVM system properties are set to a clone
of the original `Properties` object. However, the clone does not include
{jdk-javadoc-base-url}/java.base/java/util/Properties.html#defaults[the defaults] from the
original.

Consequently, the extension will perform a best effort attempt to detect default properties
and fail if any were detected. For classes that extend `Properties` it is assumed that
`clone()` is implemented with sufficient fidelity.
====

In the following example, `@RestoreSystemProperties` is used on a test method, ensuring
any changes made in that method are restored.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_restore_test]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_restore_test]
----
--
====

When `@RestoreSystemProperties` is used on a test class, any changes to system properties
during the entire lifecycle of the test class, including test methods, `@BeforeAll`,
`@BeforeEach`, and 'after' methods, are restored after the lifecycle of the test class is
complete. In addition, the annotation is inherited by each test method just as if each one
were annotated with `@RestoreSystemProperties`.

In the following example, both test methods see the system property changes made in
`@BeforeAll` and `@BeforeEach`; however, the test methods are isolated from each other
(`isolatedTest2` does not _see_ changes made in `isolatedTest1`). As shown in the second
example below, the class-level `@RestoreSystemProperties` annotation ensures that system
property changes made within the annotated class are completely restored after the class's
lifecycle, ensuring that changes are not visible to `SomeOtherTestClass`. Note that
`SomeOtherTestClass` uses the `@ReadsSystemProperty` annotation, which ensures that JUnit
does not schedule the class to run during any test known to modify system properties (see
<<system-properties-thread-safety>>).

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_class_restore_setup]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_class_restore_setup]
----
--
====

Some other test class, running later:

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_class_restore_isolated_class]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_class_restore_isolated_class]
----
--
====

[[system-properties-combined-usage]]
=== Combining @ClearSystemProperty, @SetSystemProperty, and @RestoreSystemProperties

The three system property annotations can be combined, which can be useful when some
system properties are set dynamically in code and others are not. For instance, imagine
you need to test an image generation utility that takes configuration from system
properties. Basic configuration can be specified declaratively using the `Clear` and `Set`
annotations, and the image size could be set programmatically.

[tabs]
====
Java::
+
--
[source,java,indent=0]
----
include::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_method_combine_all_test]
----
--

Kotlin::
+
--
[source,kotlin,indent=0]
----
include::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_method_combine_all_test]
----
--
====

[NOTE]
====
Using `@RestoreSystemProperties` is not necessary to restore system properties modified
via `@ClearSystemProperty` or `@SetSystemProperty` since they both automatically restore
the referenced properties. `@RestoreSystemProperties` is only needed if system properties
are modified during a test in some way _other than_ via the `Clear` and `Set` annotations.
====

[[system-properties-thread-safety]]
=== Thread Safety

Since system properties are global state, reading and writing them during
xref:writing-tests/parallel-execution.adoc[parallel execution] can lead to unpredictable
results and flaky tests. The system property extension is prepared for that and tests
annotated with `@ClearSystemProperty`, `@SetSystemProperty`, or `@RestoreSystemProperties`
will never execute in parallel (thanks to
xref:writing-tests/parallel-execution.adoc#synchronization[resource locks]) to guarantee
correct test results.

However, this does not cover all possible cases. Tested code that reads or writes system
properties _independently_ of the extension can still run in parallel to it and may thus
behave erratically when, for example, it unexpectedly reads a property set by the
extension in another thread. Tests that cover code that reads or writes system properties
need to be annotated with the respective annotation:

* `{ReadsSystemProperty}`
* `{WritesSystemProperty}` (though consider using `@RestoreSystemProperties` instead)

Tests annotated in this way will never execute in parallel with tests annotated with
`@ClearSystemProperty`, `@SetSystemProperty`, or `@RestoreSystemProperties`.
