loading...

Handling System Properties in JUnit 5

beatngu1101 profile image Daniel Kraus ・3 min read

In Java, properties are configuration values that are represented as key-value pairs, usually managed inside a Properties object. System properties are basically the Properties object of the System class, which "describes the configuration of the current working environment". This includes information about the current user, the OS or the Java runtime. The System class offers various methods to interact with its properties, however, the ones most used are:

  • clearProperty(String key): Removes the system property indicated by the specified key.
  • getProperty(String key): Gets the system property indicated by the specified key.
  • setProperty(String key, String value): Sets the system property indicated by the specified key.

Sometimes you need to control system properties when you test code that somehow uses System. When doing so, you shouldn't just wildly mutate their values, otherwise tests might inadvertently depend on possible side effects. Then, changes in your test code, or even changing the execution order, may lead to test failures that are hard to debug. Therefore, you want to have a proper restore mechanism in place that cleans up the environment after each test.

When it comes to JUnit 4, there are already off-the-shelf libraries for this such as the fantastic System Rules project. But for JUnit 5, you had to come up with a custom solution—at least until now.

There is JUnit Pioneer, a semi-official extension pack for JUnit 5. It offers various neat extensions, and as of version 0.5.0 also a mechanism to handle system properties in a safe manner. The @ClearSystemProperty and @SetSystemProperty annotation respectively, can be used to clear and set the values of system properties for a test execution. Both annotations work on the test method and class level, are repeatable as well as combinable. After the annotated method has been executed, the properties mentioned in the annotation will be restored to their original value or will be cleared if they didn't have one before. Other system properties that are changed during the test, are not restored.

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

@Test
@ClearSystemProperty(key = "some property")
void test() {
    assertNull(System.getProperty("some property"));
}

And setting a system property for a test execution:

@Test
@SetSystemProperty(key = "some property", value = "new value")
void test() {
    assertEquals("new value", System.getProperty("some property"));
}

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 test() {
    assertNull(System.getProperty("1st property"));
    assertNull(System.getProperty("2nd property"));
    assertEquals("new value", System.getProperty("3rd property"));
}

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 test() {
        assertEquals("new value", System.getProperty("some property"));
    }
}

Sometimes, you might also need to set a system property to a value that is not a constant expression, which is required for annotation values. In this case, you can still leverage the restore mechanism:

@ParameterizedTest
@ValueSource(strings = { "foo", "bar" })
@ClearSystemProperty(key = "some property")
void test(String value) {
    System.setProperty("some property", value);
}

As you can see, both annotations are quite powerful and applicable in many situations. So, become a JUnit Pioneer stargazer on GitHub and take back control of your system properties!

Posted on by:

beatngu1101 profile

Daniel Kraus

@beatngu1101

Software engineer at L-Bank, Karlsruhe. Into Java, software testing and web services.

Discussion

markdown guide