@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 @BeforeEach setup methods and @AfterEach teardown methods (see user code and extension code execution order).

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 is not necessary to restore system properties modified via @ClearSystemProperty or @SetSystemProperty - they each automatically restore the referenced properties. 'Restore', is only needed if system properties are modified in some way other than Clear and Set during a test.

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