Search code examples
javajunitclassloadertest-runner

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


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.


Solution

  • 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.