Skip to content

Latest commit

 

History

History
103 lines (77 loc) · 5.3 KB

environment-variables.adoc

File metadata and controls

103 lines (77 loc) · 5.3 KB

The @ClearEnvironmentVariable and @SetEnvironmentVariable annotations can be used to clear and set, respectively, the values of environment variables 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 variables 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 environment variables that are changed during the test, are not restored.

Warning

Java considers environment variables to be immutable, so this extension uses reflection to change them. This requires that the SecurityManager allows modifications and can potentially break on different operating systems and Java versions. Be aware that this is a fragile solution and consider finding a better one for your specific situation. For more details, see Warnings for Reflective Access.

For example, clearing a environment variable for a test execution can be done as follows:

link:../src/demo/java/org/junitpioneer/jupiter/EnvironmentVariablesExtensionDemo.java[role=include]

And setting a environment variable for a test execution:

link:../src/demo/java/org/junitpioneer/jupiter/EnvironmentVariablesExtensionDemo.java[role=include]

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

link:../src/demo/java/org/junitpioneer/jupiter/EnvironmentVariablesExtensionDemo.java[role=include]

Note that class-level configurations are overwritten by method-level configurations:

link:../src/demo/java/org/junitpioneer/jupiter/EnvironmentVariablesExtensionDemo.java[role=include]
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 environment variables are cleared/set before and reset after each individual test in the annotated class.

Warnings for Reflective Access

As explained above, this extension uses reflective access to change the otherwise immutable environment variables. On Java 9 to 16, this leads to a warning like the following:

[ERROR] WARNING: An illegal reflective access operation has occurred
[ERROR] WARNING: Illegal reflective access by org.junitpioneer.jupiter.EnvironmentVariableUtils [...] to field [...]
[ERROR] WARNING: Please consider reporting this to the maintainers of org.junitpioneer.jupiter.EnvironmentVariableUtils
[ERROR] WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
[ERROR] WARNING: All illegal access operations will be denied in a future release

On Java 17 and later, you get this error instead:

java.lang.reflect.InaccessibleObjectException: Unable to make field [...] accessible:
module java.base does not "opens java.lang" to unnamed module [...]

The best way to prevent these warnings/errors, is to change the code under test, so this extension is no longer needed. The next best thing is to allow access to that specific package:

--add-opens java.base/java.util=$TARGET_MODULE
--add-opens java.base/java.lang=$TARGET_MODULE

Where $TARGET_MODULE equals ALL-UNNAMED if you place JUnit Pioneer on the class path, or org.junitpioneer if you place JUnit Pioneer on the module path. These command line options need to be added to the JVM that executes the tests:

Thread-Safety

Since environment variables are global state, reading and writing them during parallel test execution can lead to unpredictable results and flaky tests. The environment variable extension is prepared for that and tests annotated with @ClearEnvironmentVariable or @SetEnvironmentVariable 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 environment variables independently of the extension can still run in parallel to it and may thus behave erratically when, for example, it unexpectedly reads a variable set by the extension in another thread. Tests that cover code that reads or writes environment variables need to be annotated with the respective annotation:

  • @ReadsEnvironmentVariable

  • @WritesEnvironmentVariable

Tests annotated in this way will never execute in parallel with tests annotated with @ClearEnvironmentVariable or @SetEnvironmentVariable.