Search code examples
mockitojunit4spring-testpowermockito

How to mock System.getenv() in JUnit [Powermock + Parameterized]


How to mock "System.getenv("...")" in JUnit.

Currently I am doing:

@RunWith(Parameterized.class)
@PowerMockRunnerDelegate(PowerMockRunner.class)
@PrepareForTest(System.class)
public class TestClass extends BaseTest {

    public TestClass(String testCase) {
        this.testCase = testCase;
    }

    @Before
    @Override
    public final void initTable() throws Throwable {
        super.initTable();
        PowerMockito.mockStatic(System.class); 
        PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");       
    }
...
}

I am using both PowerMock and Parameterizedrunner.

I am getting below exception for line:

PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");

Exception:

org.mockito.exceptions.base.MockitoException: 
'afterPropertiesSet' is a *void method* and it *cannot* be stubbed with a *return value*!
Voids are usually stubbed with Throwables:
    doThrow(exception).when(mock).someVoidMethod();
***

Solution

  • Use the @RunWith(PowerMockRunner.class) annotation at the class-level of the test case. Use the @PrepareForTest({ClassThatCallsTheSystemClass.class}) annotation at the class-level of the test case.

    Example with using EasyMock

    public class SystemClassUser {
    
    public String performEncode() throws UnsupportedEncodingException {
        return URLEncoder.encode("string", "enc");
    }
      }
    

    And test

    @RunWith(PowerMockRunner.class)
        @PrepareForTest( { SystemClassUser.class })
       public class SystemClassUserTest {
    
    @Test
    public void assertThatMockingOfNonFinalSystemClassesWorks() throws Exception {
        mockStatic(URLEncoder.class);
    
        expect(URLEncoder.encode("string", "enc")).andReturn("something");
        replayAll();
    
        assertEquals("something", new SystemClassUser().performEncode());
    
        verifyAll();
    }
     }
    

    From: https://github.com/powermock/powermock/wiki/MockSystem

    So, you should add a class that uses the System.getenv, not the System class to @PrepareForTest.

    This post explains why it should be done in such way.

    Also, I'd like to recommend to use the System Rules library for your case. It has a good way to stub environment variables. PowerMock modifies a class byte code, so it makes test slowly. And even if it not modify a class it at least read class from disk.