java.lang.NoSuchMethodError: org.junit.vintage.engine.descriptor.RunnerTestDescriptor.getAllDescendants()Ljava/util/Set;

So, this one had me beating my head against a brick wall for a few hours; maybe I can spare somebody else some pain by documenting what I discovered here. (Jump to the bottom for the TL;DR if you're in a hurry). I was working on adding unit test support to my project, but when I tried to run it, I got the following exception: Exception in thread "main" java.lang.NoSuchMethodError: org.junit.vintage.engine.descriptor.RunnerTestDescriptor.getAllDescendants()Ljava/util/Set;. Well, being a professional Java programmer, I'm no stranger to incomprehensible error messages, so I did what any of us would do — I googled the error message. My first hit was the JUnit GIT repository. The description seemed vaguely similar; the complainant was using IntelliJ just as I was. The suggested fix was to add an explicit reference to junit platform launcher M3. That made perfect sense — if I was getting a NoSuchMethodError, I must be using the wrong version of the Jar. However, when I double checked, I was using that version of the platform launcher. Hm. Scrolling through the thread, I couldn't find a smoking gun.

If you scroll to the very bottom of the linked thread, you'll see that when I did finally figure out what the problem was, I did go ahead and update the thread - as you should when you solve a tricky problem!

All of the other hits I got for the same issue were similar - somebody was trying to run JUnit tests in IntelliJ, and in every case, updating their Maven (or Gradle) dependencies resolved the issue. I started trying different versions to see if I could hit the "magic combination", but nothing seemed to work.

Now, I've been using JUnit pretty much since it was first released. Back then, using JUnit was a simple matter of adding a JAR file to the CLASSPATH (remembering not to deploy it at release time). Ant had some support for this concept of test classpaths and deployment classpaths, although it was a bit of a hassle since the test classpath "inherited" from the deployment classpath and support for that concept was a bit clunky, but workable. Maven made this a lot more explicit by allowing you to declare dependencies as being scoped to a particular goal. Still, adding JUnit was a matter of adding a dependency on a particular Jar and importing the org.junit.* classes.

With JUnit 5, this has gotten a bit more complex. For one thing, although we still need a build tool like Ant or Maven or Gradle in order to produce repeatable builds, even die-hard command line fanatics like me have to admit that a modern IDE enhances productivity. If I'm using an IDE, of course, I expect to be able to right-click and run my unit test and get feedback right there. Partially to support these multiple execution modes, JUnit has split into Jupiter and Vintage. Whereas a simple dependency declaration of:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
Allows both command-line (and therefore CI tool) execution of unit tests as well as right-click IDE usage, the equivalent JUnit 5 declaration:
<dependency>
    <groupId>org.junit</groupId>
    <artifactId>junit5-engine</artifactId>
    <version>5.0.0-ALPHA</version>
    <scope>test</scope>
</dependency>
Allows you to run your tests via mvn test, IntelliJ (at least build 2016.2 anyway) doesn't recognize this as a runnable test. Instead, you add a litany of other dependencies:
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.0.0-M3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.0.0-M3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.0.0-M3</version>
    <scope>test</scope>
</dependency>
This lets IntelliJ recognize this as a unit test and integrate with the IDE; the Jupiter API exposes a lot of internals that allows the IDE to "plug in" to the unit test lifecycle. However, this also means that all of your import statements change: rather than importing org.junit.Test and org.junit.Assert to write your tests, you instead import org.junit.jupiter.api.Test and org.junit.jupiter.api.Assertions. So, I'd been working with this configuration for quite a while when all of a sudden, after adding a new unit test, I was confronted with java.lang.NoSuchMethodError: org.junit.vintage.engine.descriptor.RunnerTestDescriptor.getAllDescendants()Ljava/util/Set;.

Reading through the docs, I came across this, which suggested that I may need to upgrade IntelliJ (I was, as indicated, using 2016.2.5). But still... it does say that it should work as long as I'm willing to use this specific version, which I was, but it still wasn't working.

After adding and removing dependencies and trying over and over again, I noticed something in my new unit test: it wasn't importing any of the new org.junit.jupiter.api classes. If I hadn't been poking around in vi, I might never have noticed this, because IntelliJ "helpfully" hides all of your import statements from you — in fact, I'm almost certain that I got the classes I got because I "alt-enter" imported my dependencies as a conscientious IDE user ought to. Instead, it was importing the org.junit.Test dependencies. I had never removed the dependency on the old JUnit engine in my pom (in fact, I thought - erroneously - that I still needed it for command-line support). Since the old Jar files were still there, I accidentally imported the wrong classes. I didn't get a compile error, and everything worked fine from the command line, but although IntelliJ did recognize the class as a unit test, it wasn't able to actually run it.

So after all of that, the solution ended up being changing a handful of import statements. IDE's: friend or foe?

Add a comment:

Completely off-topic or spam comments will be removed at the discretion of the moderator.

You may preserve formatting (e.g. a code sample) by indenting with four spaces preceding the formatted line(s)

Name: Name is required
Email (will not be displayed publicly):
Comment:
Comment is required
My Book

I'm the author of the book "Implementing SSL/TLS Using Cryptography and PKI". Like the title says, this is a from-the-ground-up examination of the SSL protocol that provides security, integrity and privacy to most application-level internet protocols, most notably HTTP. I include the source code to a complete working SSL implementation, including the most popular cryptographic algorithms (DES, 3DES, RC4, AES, RSA, DSA, Diffie-Hellman, HMAC, MD5, SHA-1, SHA-256, and ECC), and show how they all fit together to provide transport-layer security.

My Picture

Joshua Davies

Past Posts