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)
https://gist.github.com/sureshgadupu/43fa9a70dbb4cdc9e3287b52056f3047

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)
https://gist.github.com/sureshgadupu/8aca3352ae36443eb9f367101a20a073

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)
https://gist.github.com/sureshgadupu/8ff62a481e067260574991258cf22821

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)
https://gist.github.com/sureshgadupu/a937e738caea54e393d7f7186bc2a8c8
https://gist.github.com/sureshgadupu/8cb94bd012fbc5e14bb14fdbb99c11a5
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)
https://gist.github.com/sureshgadupu/67ecb28de83b708abc17709b0e7d1409

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)
https://gist.github.com/sureshgadupu/511165f53df82d33d5644d4bd747ed1d

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)
https://gist.github.com/sureshgadupu/5f263b01650051226669bd4b84d17552

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)
https://gist.github.com/sureshgadupu/6f786b3b2d6fff95fcac983c328aad22

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)
https://gist.github.com/sureshgadupu/887c65c0064aeeaea7d2751ad199ce2d

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)
https://gist.github.com/sureshgadupu/690da39271f61c5a24b6be71b7e90b21

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)
https://gist.github.com/sureshgadupu/d9eaa0af0fbef016e4c8972d7652767b

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)
https://gist.github.com/sureshgadupu/6f9fdb661370d1a30ee2d426562ce5e0
https://gist.github.com/sureshgadupu/c150c19dd112cfb30418d31c5dfc1403
@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)
https://gist.github.com/sureshgadupu/c2ea17a5151eb6e83cee479faf32494f

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)
https://gist.github.com/sureshgadupu/29f4a852ea664dfbbc770a99250432ae

You can download the source code for this blog post from GitHub

Similar Posts