@ClearSystemProperty
and @SetSystemProperty
The @ClearSystemProperty
and @SetSystemProperty
annotations can be used to clear and set, respectively, the values of system properties for a test execution.
Both annotations work on the test method and class level, are repeatable, combinable, and inherited from higher-level containers.
After the annotated method has been executed, the properties mentioned in the annotation will be restored to their original value or the value of the higher-level container, or will be cleared if they didn’t have one before.
Other system properties that are changed during the test, are not restored (unless the @RestoreSystemProperties
is used).
For example, clearing a system property for a test execution can be done as follows:
@Test
@ClearSystemProperty(key = "some property")
void testClearingProperty() {
assertThat(System.getProperty("some property")).isNull();
}
And setting a system property for a test execution:
@Test
@SetSystemProperty(key = "some property", value = "new value")
void testSettingProperty() {
assertThat(System.getProperty("some property")).isEqualTo("new value");
}
As mentioned before, both annotations are repeatable and they can also be combined:
@Test
@ClearSystemProperty(key = "1st property")
@ClearSystemProperty(key = "2nd property")
@SetSystemProperty(key = "3rd property", value = "new value")
void testClearingAndSettingProperty() {
assertThat(System.getProperty("1st property")).isNull();
assertThat(System.getProperty("2nd property")).isNull();
assertThat(System.getProperty("3rd property")).isEqualTo("new value");
}
Note that class-level configurations are overwritten by method-level configurations:
@ClearSystemProperty(key = "some property")
class MySystemPropertyTest {
@Test
@SetSystemProperty(key = "some property", value = "new value")
void clearedAtClasslevel() {
assertThat(System.getProperty("some property")).isEqualTo("new value");
}
}
Note
|
Method-level configurations are visible in both Since v1.7.0, a class-level configuration means that the specified system properties are cleared/set before and reset after each individual test in the annotated class. |
@RestoreSystemProperties
@RestoreSystemProperties
can be used to restore changes to system properties made directly in code.
While @ClearSystemProperty
and @SetSystemProperty
set or clear specific properties and values, they don’t allow property values to be calculated or parameterized, 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 a test or test class is complete.
In this example, @RestoreSystemProperties
is used on a test method, ensuring any changes made in that method are restored:
@ParameterizedTest
@ValueSource(strings = { "foo", "bar" })
@RestoreSystemProperties
void parameterizedTest(String value) {
System.setProperty("some parameterized property", value);
System.setProperty("some other dynamic property", "my code calculates somehow");
}
When @RestoreSystemProperties
is used on a test class, any system properties changes made during the entire lifecycle of the test class, including test methods, @BeforeAll
, @BeforeEach
and 'after' methods, are restored after the test class' lifecycle is complete.
In addition, the annotation is inherited by each test method just as if each one was 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
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: This ensures that JUnit does not schedule the class to run during any test known to modify system properties (see Thread-Safety).
@RestoreSystemProperties
class MySystemPropertyRestoreTest {
@BeforeAll
void beforeAll() {
System.setProperty("A", "A value");
}
@BeforeEach
void beforeEach() {
System.setProperty("B", "B value");
}
@Test
void isolatedTest1() {
System.setProperty("C", "C value");
}
@Test
void isolatedTest2() {
assertThat(System.getProperty("A")).isEqualTo("A value");
assertThat(System.getProperty("B")).isEqualTo("B value");
// Class-level @RestoreSystemProperties restores "C" to original state
assertThat(System.getProperty("C")).isNull();
}
}
Some other test class, running later:
@ReadsSystemProperty
class SomeOtherTestClass {
@Test
void isolatedTest() {
assertThat(System.getProperty("A")).isNull();
assertThat(System.getProperty("B")).isNull();
assertThat(System.getProperty("C")).isNull();
}
}
Using @ClearSystemProperty
, @SetSystemProperty
, and @RestoreSystemProperties
together
All three annotations can be combined, which could be used when some system properties are parameterized (i.e. need to be set in code) and others are not.
For instance, imagine testing an image generation utility that takes configuration from system properties.
Basic configuration can be specified using Set
and Clear
and the image size parameterized:
@ParameterizedTest
@IntRangeSource(from = 0, to = 10000, step = 500)
@RestoreSystemProperties
@SetSystemProperty(key = "DISABLE_CACHE", value = "TRUE")
@ClearSystemProperty(key = "COPYWRITE_OVERLAY_TEXT")
void imageGenerationTest(int imageSize) {
System.setProperty("IMAGE_SIZE", String.valueOf(imageSize)); // Requires restore
// Test your image generation utility with the current system properties
}
Note
|
Using |
@RestoreSystemProperties
Limitations
The system Properties
object is normally just a hashmap of strings, however, it is technically possible to store non-string values and create nested Properties
with inherited default values.
@RestoreSystemProperties
restores the original Properties
object with all of its potential richness after the annotated scope is complete.
However, for use during the test within the test scope it provides a cloned Properties
object with these limitations:
-
Properties with non-string values are removed
-
Nested
Properties
are flattened into a non-nested instance that has the same effective values, but not necessarily the same structure
Thread-Safety
Since system properties are global state, reading and writing them during parallel test 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 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
.