A REST Assured suite dies with NoSuchMethodError on an org.hamcrest class — how do you fix it?
answer
- two jars, one org.hamcrest package
- rest-assured brings hamcrest at compile scope
- legacy hamcrest-core and hamcrest-all overlap
- print the test dependency tree first
- the BOM manages only io.rest-assured
basics
~20 sTwo Hamcrest jars are on the test classpath and the older one is winning. REST Assured needs the modern org.hamcrest:hamcrest artifact; a legacy hamcrest-core or hamcrest-all 1.x jar supplies the same classes. Exclude or pin one Hamcrest and it goes.
solid answer
~40 sThis is a classpath fault, not a test fault. `then().body("state", equalTo("SPINNING"))` takes an `org.hamcrest.Matcher`, so Hamcrest is a compile-scope dependency of the `rest-assured` artifact and sits on the same test classpath as everything your runner brings. The modern coordinate is `org.hamcrest:hamcrest`, but the legacy `hamcrest-core` and `hamcrest-all` 1.x jars publish the same `org.hamcrest` package. Whichever copy the class loader finds first wins, and if it is the old one, REST Assured calls a method that class does not declare — `NoSuchMethodError`. Diagnose it by printing the test dependency tree and looking for more than one hamcrest artifact id. Fix it by excluding the legacy jar at its source, or by pinning `org.hamcrest:hamcrest` explicitly. Importing `rest-assured-bom` will not help, and neither will swapping in the shaded `rest-assured-all` jar.
code
xml · 24 lines<!-- pin the modern Hamcrest so a legacy 1.x jar cannot win -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- and cut the legacy artifact off at its source -->
<dependency>
<groupId>com.example.turbines</groupId>
<artifactId>legacy-test-support</artifactId>
<version>1.4.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>go deeper
Recognise the category of the error. A NoSuchMethodError or NoClassDefFoundError naming a library class is a build and classpath problem, and no change to your test code will make it go away.
Explain why REST Assured needs Hamcrest on the shared classpath at all, and how two jars publishing the same package let the older class win silently.
Walk the diagnosis and pick the durable fix. Read the dependency tree, name who introduces the legacy jar, and prefer an exclusion or a managed version over relying on declaration order.
Set the standard. Decide how the organisation pins the libraries REST Assured shares with the rest of the test stack, and how a regression in that pinning gets caught before it reaches a suite.
## Why REST Assured and your runner share Hamcrest `then().body("state", equalTo("SPINNING"))` accepts an `org.hamcrest.Matcher`. Hamcrest is therefore a compile-scope dependency of the `rest-assured` artifact, not an optional one, and it lands on the same flat test classpath as everything else the module needs. Hamcrest also has a long history: the modern coordinate is `org.hamcrest:hamcrest` (the 2.x line), while the 1.x era published `org.hamcrest:hamcrest-core` and `org.hamcrest:hamcrest-all`. All three contribute classes in the `org.hamcrest` package, `org.hamcrest.Matchers` among them. | Coordinate | Era | Package it publishes | |---|---|---| | `org.hamcrest:hamcrest` | 2.x, current | `org.hamcrest` | | `org.hamcrest:hamcrest-core` | 1.x, legacy | `org.hamcrest` | | `org.hamcrest:hamcrest-all` | 1.x, legacy fat jar | `org.hamcrest` | ## How two jars become a NoSuchMethodError - A flat classpath has no notion of "the right jar". The first `org.hamcrest.Matchers` the class loader finds is the one every caller gets, and the loser is silently invisible. - If the copy that wins is a 1.x class, REST Assured's own code — compiled against the 2.x API — calls a method that class does not declare, and the JVM raises `java.lang.NoSuchMethodError`. A missing type raises `NoClassDefFoundError` instead; both mean the same thing here. - The failure surfaces at the first assertion, not at start-up, which is exactly why it reads like a test problem when it is a build problem. - Nothing about this is REST Assured specific. The library is simply the loudest consumer of Hamcrest in most API suites, so it is where the clash becomes visible. ## Diagnosing it 1. Print the test-scope dependency tree and search it for `hamcrest`. You are looking for more than one **artifact id**, not merely more than one version of a single artifact id. 2. Identify who drags the legacy one in. Older test tooling and JUnit-4-era libraries are the usual sources — REST Assured's own Getting Started page calls this out by telling you to declare `rest-assured` before the JUnit dependency so the correct Hamcrest is used. 3. Confirm the theory before changing the build. The frame that raises `NoSuchMethodError` names the exact class and method, and that tells you which side of the clash is stale. ## Fixing it, strongest first - **Exclude the legacy artifact where it enters.** `hamcrest-core` and `hamcrest-all` are the two names to look for. Cutting them off at the dependency that brings them is durable: it survives file reordering, new contributors and future upgrades. - **Manage the version explicitly.** Pinning `org.hamcrest:hamcrest` in dependency management (or as a Gradle constraint) states the intent in one reviewable place, and makes a future regression a one-line diff. - **Reorder the declarations.** REST Assured's documentation suggests putting `rest-assured` ahead of the JUnit dependency so the build's conflict mediation picks its Hamcrest. It works, and it is the weakest of the three, because it depends on the order of elements in a file anyone may tidy. ## What will not fix it - `rest-assured-bom` will not. Its dependency-management block lists `io.rest-assured` artifacts only — `rest-assured`, `rest-assured-all`, `rest-assured-common`, `json-path`, `xml-path`, `json-schema-validator`, `spring-commons`, `spring-mock-mvc`, `spring-web-test-client` and the Kotlin and Scala modules. It says nothing about Hamcrest, Groovy or Apache HttpClient. - `rest-assured-all` will not either. It is a shaded jar of the `io.rest-assured` modules, and its third-party dependencies are promoted rather than bundled, so the same two Hamcrest jars can still meet on your classpath. - Upgrading REST Assured will not, unless the upgrade happens to move the Hamcrest baseline. The duplicate lives on your side of the graph, not the library's. ## The wider lesson for a turbine suite REST Assured deliberately owns very little of your test classpath. It brings no runner, no assertion library of its own beyond the Hamcrest seam, and no object mapper. That minimalism is exactly what makes the same `given()...then()` chain portable between JUnit 5 and TestNG — but the price is that every shared library becomes a negotiation between REST Assured and whatever else the module brought along. Three of them come up again and again: - **Hamcrest**, shared with older assertion tooling, the case described above. - **Groovy**, because GPath expressions are compiled and evaluated as Groovy at runtime. - **Apache HttpClient**, shared with any other client library in the same module. Keep a reviewed, explicit answer for those three. When a turbine suite starts failing in a way that has nothing to do with the turbine service — a `NoSuchMethodError`, a `NoClassDefFoundError`, a method that exists in the Javadoc but not at runtime — the dependency tree is the first place to look, and the assertions are the last.
- Would importing rest-assured-bom have prevented this?No. The BOM manages only `io.rest-assured` coordinates: the DSL, the path modules, the schema validator, the Spring modules and the Kotlin and Scala extensions. It keeps those versions consistent with each other and says nothing about Groovy, Apache HttpClient or Hamcrest — which are exactly the libraries you end up sharing with the rest of the test classpath.
- Why does REST Assured's documentation mention dependency ordering at all?It advises declaring `rest-assured` before the JUnit dependency so the build's conflict mediation picks REST Assured's Hamcrest. It genuinely works, but it depends on declaration order in a file anyone can reorder, so treat it as a hint rather than the fix. An explicit exclusion or a managed version survives refactoring; an ordering convention does not.
saying these in an interview costs you the question
- Blames the REST Assured version and upgrades it blindly
- Thinks importing rest-assured-bom pins Hamcrest as well
- Adds a second hamcrest dependency instead of removing one
- Believes declaration order alone is a durable fix for the clash
- Treats NoSuchMethodError as an assertion failure rather than a classpath fault
- Assumes rest-assured-all bundles its own private copy of Hamcrest