Reputation: 147
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
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
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