JUnit : Handling System and Environment properties in unit testing
It can be hard to automate unit testing when the code depends on the environment or system properties.
In this blog post, I will show you how to test the code which depends on the System or Environment properties using 2 test packages.
Handling Environment Variables
I am going to use the following sample program to show the handling of environment variables in Junit5 testing. Following program reads 3 environment variables (DB_URL, USER, PASS), and tries to connect to database.
public Connection getDbConnection() {
Connection conn = null;
try{
// STEP 1: Register JDBC driver
Class.forName("org.h2.Driver");
conn = DriverManager.getConnection(System.getenv("DB_URL"),System.getenv("USER"),System.getenv("PASS"));
} catch(Exception e) {
System.out.println("Exception while connecting to DB"+e);
throw new RuntimeException(e);
}
return conn;
}
Code language: Java (java)
System Stubs
System Stubs is used to test code which depends on methods in java.lang.System
.
Using System Stubs, you can set up the environment variable in 2 different ways. Before looking at ways to setup the env variables, lets first add the dependency in the pom.xml file. As we are using JUnit 5 for our test environment, I am adding JUnit 5 related jar.
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-jupiter</artifactId>
<version>1.1.0</version>
</dependency>
Code language: Java (java)
Method 1
You can set the environment variables in a method using EnvironmentVariables
class and use the lambda expression to access them.
@Test
public void testGetDbConnection2() throws Exception {
EnvironmentVariables env = new EnvironmentVariables();
env.set("DB_URL", "jdbc:h2:~/test");
env.set("USER", "sa");
env.set("PASS", "");
env.execute(() -> {
Connection connection = h2JdbcEnvDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
);
}
Code language: Java (java)
You can also set up environment variables at global level and access them in each method like below
EnvironmentVariables env = new EnvironmentVariables();
@BeforeEach
public void setUp() {
h2JdbcEnvDemo = new H2JdbcEnvDemo();
env.set("DB_URL", "jdbc:h2:~/test");
env.set("USER", "sa");
env.set("PASS", "");
}
@Test
public void testGetDbConnection2() throws Exception {
env.execute(() -> {
Connection connection = h2JdbcEnvDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
);
}
Code language: Java (java)
Instead of hard-coding environment variables in classes, you can also read from the properties file.
EnvironmentVariables env = new EnvironmentVariables(PropertySource.fromResource("testenv.properties"));
Code language: Java (java)
@Test
public void testReadFromResourceGetDbConnection() throws Exception {
new EnvironmentVariables().set(PropertySource.fromResource("testenv.properties")).execute(() -> {
Connection connection = h2JdbcEnvDemo.getDbConnection();
Assertions.assertNotNull(connection);
});
}
Code language: Java (java)
Method 2
Instead of using lambda, you can also define environment properties using @SystemStub
annotations. To use the @SystemStub,
you have to use the JUnit 5, extention class with SystemStubsExtension.class
@ExtendWith(SystemStubsExtension.class)
public class H2JdbcDemoEnvTest
{
@SystemStub
private static EnvironmentVariables testVariables = new EnvironmentVariables("DB_URL", "jdbc:h2:~/test","USER", "sa", "PASS","");
H2JdbcEnvDemo h2JdbcEnvDemo;
@BeforeEach
public void setUp() {
h2JdbcEnvDemo = new H2JdbcEnvDemo();
}
@Test
public void testGetDbConnection() {
Connection connection = h2JdbcEnvDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
}
Code language: Java (java)
Junit Pioneer
This is the another JUnit extension which can used to set the environment variables during unit testing.
To use the JUnit pioneer first we need to add the dependency in the pom.xml
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
Code language: HTML, XML (xml)
It has 2 annotations @ClearEnvironmentVariable
and @SetEnvironmentVariable
. These annotations can be used to clear, respectively, set the values of environment variables for a test execution. These annotations can be used at either method level or class level. After the annotated method has been executed, the variables mentioned in the annotation will be restored to their original value or will be cleared if they didn’t have one before
The Below example shows setting environment variables at method level.
@Test
@SetEnvironmentVariable(key = "DB_URL",value = "jdbc:h2:~/test")
@SetEnvironmentVariable(key = "USER",value = "sa")
@SetEnvironmentVariable(key = "PASS",value = "")
public void testGetDbConnection() {
Connection connection = h2JdbcEnvDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
Code language: Java (java)
Below example shows setting environment variables at class level.
@SetEnvironmentVariable(key = "DB_URL",value = "jdbc:h2:~/test")
@SetEnvironmentVariable(key = "USER",value = "sa")
@SetEnvironmentVariable(key = "PASS",value = "")
public class H2JdbcDemoEnvTest3
{
H2JdbcEnvDemo h2JdbcEnvDemo;
@BeforeEach
public void setUp() {
h2JdbcEnvDemo = new H2JdbcEnvDemo();
}
@Test
public void testGetDbConnection() {
Connection connection = h2JdbcEnvDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
}
Code language: Java (java)
Handling System Properties
We are going to use the following sample program to test the handling of System properties in unit testing. The sample program is same as shown in handling environment section but instead of reading variables from the environment, it reads from system.
public class H2JdbcSysPropDemo
{
public Connection getDbConnection() {
Connection conn = null;
try{
// STEP 1: Register JDBC driver
Class.forName("org.h2.Driver");
conn = DriverManager.getConnection(System.getProperty("DB_URL"),System.getProperty("USER"),System.getProperty("PASS"));
} catch(Exception e) {
System.out.println("Exception while connecting to DB : "+e);
throw new RuntimeException(e);
}
return conn;
}
}
Code language: Java (java)
System Stubs
Using System Stubs, you can set up the System properties in 2 different ways.
Method 1
You can set the system properties in a method using SystemProperties
class and use the lambda expression to access them
@Test
public void testGetDbConnection() throws Exception {
SystemProperties someProperties = new SystemProperties(
"DB_URL", "jdbc:h2:~/test",
"USER", "sa",
"PASS","");
someProperties.execute(() -> {
Connection connection = h2JdbcSysPropDemo.getDbConnection();
Assertions.assertNotNull(connection);
});
}
Code language: Java (java)
You can also set up system properties at global level and access them in each method like below
public class H2JdbcSysPropDemoTest3 {
SystemProperties someProperties = new SystemProperties("DB_URL", "jdbc:h2:~/test","USER", "sa","PASS","");
private H2JdbcSysPropDemo h2JdbcSysPropDemo;
@BeforeEach
public void setUp() {
h2JdbcSysPropDemo = new H2JdbcSysPropDemo();
}
@Test
public void testGetDbConnection() throws Exception {
someProperties.execute(() -> {
Connection connection = h2JdbcSysPropDemo.getDbConnection();
Assertions.assertNotNull(connection);
});
}
}
Code language: Java (java)
Method 2
Instead of using lambda, you can also define system properties using @SystemStub
annotations. To use the @SystemStub,
you have to use the JUnit 5 extension class (SystemStubsExtension.class
)
@ExtendWith(SystemStubsExtension.class)
public class H2JdbcSysPropDemoTest1
{
@SystemStub
private static SystemProperties testVariables = new SystemProperties("DB_URL", "jdbc:h2:~/test","USER", "sa", "PASS","");
H2JdbcSysPropDemo h2JdbcSysPropDemo;
@BeforeEach
public void setUp() {
h2JdbcSysPropDemo = new H2JdbcSysPropDemo();
}
@Test
public void testGetDbConnection() {
Connection connection = h2JdbcSysPropDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
}
Code language: Java (java)
Instead of hard-coding system properties in test classes, you can also read from the properties file.
@SystemStub
private static SystemProperties testVariables = new SystemProperties(PropertySource.fromResource("testsys.properties"));
Code language: Java (java)
@Test
public void testReadFromResourceGetDbConnection() throws Exception {
new SystemProperties().set(PropertySource.fromResource("testsys.properties")).execute(() -> {
Connection connection = h2JdbcSysPropDemo.getDbConnection();
Assertions.assertNotNull(connection);
});
}
Code language: Java (java)
Junit Pioneer
The @ClearSystemProperty
and @SetSystemProperty
annotations can be used to clear, respectively, 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.
The Below example shows setting system properties at method level
@Test
@SetSystemProperty(key = "DB_URL", value = "jdbc:h2:~/test")
@SetSystemProperty(key = "USER", value = "sa")
@SetSystemProperty(key = "PASS", value = "")
public void testGetDbConnection() {
Connection connection = h2JdbcSysPropDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
Code language: Java (java)
The Below example shows setting system properties at class level
@SetSystemProperty(key = "DB_URL", value = "jdbc:h2:~/test")
@SetSystemProperty(key = "USER", value = "sa")
@SetSystemProperty(key = "PASS", value = "")
public class H2JdbcSysPropDemoTest {
private H2JdbcSysPropDemo h2JdbcSysPropDemo;
@BeforeEach
public void setUp() {
h2JdbcSysPropDemo = new H2JdbcSysPropDemo();
}
@Test
public void testGetDbConnection() {
Connection connection = h2JdbcSysPropDemo.getDbConnection();
Assertions.assertNotNull(connection);
}
}
Code language: Java (java)
You can download the source code for this blog post from GitHub