Neal
Neal

Reputation: 147

System.setProperty in JUnit tests

I have two tests, we'll call them test A and test B.

In test A we have this line

System.setProperty("<interface>", "<implementation>);

In test B we have that same line.

When both lines exist, test B fails. When I comment out the line in test A, test B succeeds. If I do a

System.clearProperty("<interface>") 

test B fails.

I have a similar issue with tests C and D with a different interface property. Test C sets it, test D does not. When I comment out the set call, D passes. When I don't. D fails. When I call clear, D fails.

Calling clear and commenting out the line both cause B and D to have the "correct" value in that system property, however they fail when I call clear (despite having the same value as when the line is commented out) and succeed when commenting out. Anyone have any ideas why this is the case?

Snippet showing what it does

System.setProperty("INTERFACE_NAME", "IMPLEMENTATION_NAME");

//getOBJECT will now return a type of IMPLEMENTATION_NAME
INTERFACE mockedINTERFACE = INTERFACE.getDirectory().getOBJECT(domain);

Upvotes: 5

Views: 21685

Answers (2)

Neal
Neal

Reputation: 147

The answer to this is a two-parter, and neither parts are good.

I was able to get the whole test suite running about 20% of the time by adding PowerMock in to tests A and C. Tests B and D were already using it. This makes me think that PowerMock is somehow the root cause of this. We can't rip it out of the project yet, but it's been on our todo list for about a year.

I undid the PowerMock changes because 20% isn't enough to actually say it's working. Instead I ended up changing maven surefire's forkmode to pertest, and having each test run in a separate JVM allows all tests to run and pass in maven.

I hate all of this but it technically works. Good luck, if anyone else stumbles upon this question.

Upvotes: 2

Karol Dowbecki
Karol Dowbecki

Reputation: 44942

Tests are supposed to be independent from one another. If they change global state e.g. system properties, they should revert the change after the test is executed.

Ensure that each of your tests passes when run separately and then add @Before and @After methods to revert the changes to the global state.

private String oldValue;

@Before
public void setUp() {
  oldValue = System.getProperty("key");
  System.setProperty("key", ...);
}

@After
public void tearDown() {
  if (oldValue != null) {
    System.setProperty("key", oldValue);
  }
}

Do note that property being not set by default is tricky, maybe use Optional<String>.

Upvotes: 8

Related Questions