Search code examples
javajunitpowermockpowermockito

Using PowerMock to mock System.class in multiple tests not working as expected


Class under test:

public class SystemCaller {
  public String callSystem() {
    return System.getenv("test-this");
  }
}

Test 1:

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

@RunWith(PowerMockRunner.class)
@PrepareForTest({SystemCaller.class, System.class})
public class SystemTestOne {

    @Before
    public void start() throws Exception {
        PowerMockito.mockStatic(System.class);
        PowerMockito.when(System.getenv("test-this")).thenReturn("hello-one");
    }

    @After
    public void stop() {}

    @Test
    public void verifySystemTestOne() throws Exception {
        assertThat(new SystemCaller().callSystem(), is("hello-one"));
    }
}

Test 2:

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

@RunWith(PowerMockRunner.class)
@PrepareForTest({SystemCaller.class, System.class})
public class SystemTestTwo {

    @Before
    public void start() throws Exception {
        PowerMockito.mockStatic(System.class);
        PowerMockito.when(System.getenv("test-this")).thenReturn("hello-two");
    }

    @After
    public void stop() {}

    @Test
    public void verifySystemTestTwo() throws Exception {
        assertThat(new SystemCaller().callSystem(), is("hello-two"));
    }
}

If run individually, each test passes on its own. However, when I run all tests, the tests fail. And the error varies. Sometimes, the assertion expects hello-two, but gets hello-one. At other times, callSystem() just returns null!

Is there a setup issue here? Or, is this because I'm mocking a static method (on System class in this case) that's causing the issue?

My dependencies:

      "org.apache.commons" % "commons-lang3" % "3.5" ::
      "com.squareup.okhttp3" % "okhttp" % "3.6.0" ::
      "com.google.code.gson" % "gson" % "2.8.0" ::
      "com.novocode" % "junit-interface" % "0.8" % "test->default" ::
      "junit" % "junit" % "4.12" % "test" ::
      "org.hamcrest" % "hamcrest-core" % "1.3" % "test" ::
      "org.hamcrest" % "hamcrest-library" % "1.3.RC2" % "test" ::
      "com.squareup.okhttp3" % "mockwebserver" % "3.8.0" % "test" ::
      "org.powermock" % "powermock-core" % "1.6.6" % "test" ::
      "org.powermock" % "powermock-api-mockito" % "1.6.6" % "test" ::
      "org.powermock" % "powermock-module-junit4" % "1.6.6" % "test" ::

UPDATE 1:

OK, the suggestion from @kevin-welker, to use EnvironmentVariables actually worked. However, I'm seeing a weird issue where, when I run the JUnit tests from Intellij IDEA (using JUnit Run Configuration), everything works, but when I run them via sbt (thru ./sbt test) they fail with the same error :(.

I'm leveraging https://github.com/sbt/junit-interface to run these tests, but can't seem to get them to work.

UPDATE 2:

Alright! Thanks to @kevin-welker and @stefan-birkner, I got the tests to work as expected! Since there's only a handful of tests I need to run, and they're really fast, I'm OK losing out on the parallelism!


Solution

  • There's a micro library to help with mocking system classes called System Rules. Use is as simple as

    @Rule
    public final ProvideSystemProperty myPropertyHasMyValue
        = new ProvideSystemProperty("MyProperty", "MyValue");