Run each JUnit Test with a Separate ClassLoader (no, really)
Asked Answered
B

2

18

How can I have JUnit use a separate ClassLoader for each test class it executes?

I am writing a JUnit TestRunner for a library that sets a lot of static variables. I essentially want to reset all of these between each test class, without needing to know what all of them are. I do not want to be coupled to intimate knowledge of the framework, as whenever the library changes internally then my TestRunner will break.

Before I go any further, I want to make absolutely clear that I really do want to do this.

  • I do not have control over the library.
  • I do not have the option of not using static variables.
  • I do not want to use reflection or Powermock, as I don't want to know what's going on in the library.
  • I do not want to use Maven config to fork testing processes, as then the testing utility is tied to a build tool.

Every other answer I can find on StackOverflow just says "don't do that," which isn't helpful. First person to answer with "static variables are dumb" wins a doughnut.

Bicentennial answered 17/2, 2015 at 21:23 Comment(5)
I want a doughnut! "Static variables are dumb"Disastrous
You win sir! Put your name and address in a Singleton that extends String on GitHub and I'll post it to you.Bicentennial
Hold on let me fork log4j and throw it in there.Disastrous
Have you considered using Guice? It really helps you to avoid using static variables and helps you write much more cleaner code :)Leonor
Have you considered that there might be a dog loose in the woods?Bicentennial
B
13

In the end I wrote my own, loosely based on another Stack Overflow answer (which didn't work for me).

It's now on GitHub, and Maven Central. https://github.com/BinaryTweed/quarantining-test-runner

<dependency>
    <groupId>com.binarytweed</groupId>
    <artifactId>quarantining-test-runner</artifactId>
    <version>0.0.1</version>
</dependency>

To use it annotate your test classes accordingly:

@RunWith(QuarantiningRunner.class)
@Quarantine({"com.binarytweed"})
public class MyIsolatedTest {
...

The linked answer didn't work for me as the test class itself needs to be loaded in a separate ClassLoader, as then all the classes it references will use the same loader. Quarantining is inclusive (rather than exclusive) as you need the JUnit @Test annotations to be loaded by the parent ClassLoader, otherwise JUnit can't find any testable methods as it uses Class<Test> as a key in a lookup map.

Bicentennial answered 22/2, 2015 at 11:19 Comment(0)
B
2

MyFaces has a TestPerClassLoaderRunner which is (despite its name) what you are looking for.

Besprinkle answered 17/2, 2015 at 22:21 Comment(3)
Thanks. I've tried that, and it fails to find java.lang.Object! I think maybe I need to pass through the 'current' classpath to it...Bicentennial
Did you start it from Maven? Looks like it is very specific to its class path structure.Besprinkle
Nah, Eclipse. Will give that a go, and if it works then see if I can make it generic enough to work from either.Bicentennial

© 2022 - 2024 — McMap. All rights reserved.