Add rest-client-test module: @RestClientTest on Boot 4.1, Jackson 3 by default, and the RestClient-vs-RestTemplate root-URI matching trap (post #49)
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# rest-client-test
|
||||
|
||||
Companion module for [ankurm.com](https://ankurm.com) - testing Spring REST clients on **Spring
|
||||
Boot 4.1.1** with `@RestClientTest` and `MockRestServiceServer`: a `RestClient`-based client, a
|
||||
`RestTemplate`-based client, a declarative `@HttpExchange` interface client, an error-path test,
|
||||
and `@MockitoBean` standing in for a plain collaborator bean now that `@MockBean` is gone.
|
||||
|
||||
## Versions actually used here
|
||||
|
||||
| Component | Version | Verified against |
|
||||
|---|---|---|
|
||||
| Spring Boot | 4.1.1 | `spring-boot-starter-parent` POM, Maven Central |
|
||||
| Spring Framework | 7.0.9 | resolved transitively, confirmed via `mvn dependency:tree` |
|
||||
| JDK | 25 (Temurin 25.0.4.1+1) | `java -version` in this sandbox |
|
||||
| JUnit | 6.0.3 | resolved transitively |
|
||||
| Jackson | **3.1.5**, groupId `tools.jackson.core` | resolved transitively, confirmed via `mvn dependency:tree` and by unzipping the jar |
|
||||
|
||||
## The module-naming trap, reconfirmed
|
||||
|
||||
`@RestClientTest` and `MockRestServiceServer`'s auto-configuration now live in
|
||||
**`spring-boot-restclient-test`** (`org.springframework.boot.restclient.test.autoconfigure.RestClientTest`),
|
||||
not in `spring-boot-resttestclient` - that second module backs the newer `RestTestClient` type used
|
||||
in the [`rest-test-client`](../rest-test-client) module instead. Confirmed by unzipping both jars
|
||||
and listing their classes; the names are one letter apart and Maven Central's search box will
|
||||
happily suggest the wrong one.
|
||||
|
||||
`RestTemplateBuilder` moved too, from `org.springframework.boot.web.client` (Boot 3.x) to
|
||||
**`org.springframework.boot.restclient.RestTemplateBuilder`** - same module, `spring-boot-restclient`,
|
||||
as `RestClient.Builder`'s auto-configuration. A Boot 3 import of `RestTemplateBuilder` fails to
|
||||
compile on Boot 4.1 with no hint beyond "package does not exist."
|
||||
|
||||
## The bigger surprise: Jackson 3 by default
|
||||
|
||||
`mvn dependency:tree` resolves `tools.jackson.core:jackson-databind:3.1.5`, not
|
||||
`com.fasterxml.jackson.core:jackson-databind`. Spring Boot 4.1's `spring-boot-starter-jackson`
|
||||
pulls in **Jackson 3**, whose `ObjectMapper` lives at `tools.jackson.databind.ObjectMapper` - a
|
||||
different package entirely, not just a version bump. `com.fasterxml.jackson.core:jackson-annotations`
|
||||
is still on the classpath (so `@JsonProperty` etc. keep their old import), but `jackson-databind`
|
||||
and `jackson-core` are the new `tools.jackson.*` artifacts. Every test class in this module imports
|
||||
`tools.jackson.databind.ObjectMapper`, not the classic one - this bit every test file on the first
|
||||
compile attempt here. See the [Jackson 3 post](https://ankurm.com) on this blog for the full
|
||||
migration story.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```bash
|
||||
./scripts/run-all.sh
|
||||
```
|
||||
|
||||
Runs the full suite and regenerates every file in `docs/output/`.
|
||||
|
||||
## Test classes
|
||||
|
||||
| Class | What it proves |
|
||||
|---|---|
|
||||
| [`RestClientEmployeeTest`](src/test/java/com/ankurm/restclienttest/RestClientEmployeeTest.java) | `@RestClientTest(EmployeeClient.class)` + `MockRestServiceServer`, with `@MockitoBean` replacing the collaborator `EmployeeAuditLogger` |
|
||||
| [`RestTemplateEmployeeTest`](src/test/java/com/ankurm/restclienttest/RestTemplateEmployeeTest.java) | The same job on `RestTemplate`, and the one case where a *relative* `requestTo()` still works against an absolute root URI |
|
||||
| [`HttpInterfaceClientTest`](src/test/java/com/ankurm/restclienttest/HttpInterfaceClientTest.java) | `@RestClientTest(components = AppConfig.class)` wiring a declarative `@HttpExchange` interface client through the mock |
|
||||
| [`ErrorScenarioTest`](src/test/java/com/ankurm/restclienttest/ErrorScenarioTest.java) | A 404 from `MockRestServiceServer` surfacing as `HttpClientErrorException.NotFound` |
|
||||
|
||||
## Captured output
|
||||
|
||||
| File | From |
|
||||
|---|---|
|
||||
| [`00-full-test-run.txt`](docs/output/00-full-test-run.txt) | `mvn test`, all 4 classes, green |
|
||||
| [`01-restclient-basic-fetch.txt`](docs/output/01-restclient-basic-fetch.txt) | `RestClientEmployeeTest` |
|
||||
| [`02-resttemplate-same-mocking-code.txt`](docs/output/02-resttemplate-same-mocking-code.txt) | `RestTemplateEmployeeTest` |
|
||||
| [`03-404-throws-notfound.txt`](docs/output/03-404-throws-notfound.txt) | `ErrorScenarioTest` |
|
||||
| [`04-http-interface-client.txt`](docs/output/04-http-interface-client.txt) | `HttpInterfaceClientTest` |
|
||||
| [`05-restclient-relative-requestto-fails.txt`](docs/output/05-restclient-relative-requestto-fails.txt) | A scratch test (not committed) reproducing the real `AssertionError` when `requestTo()` is written relative against a `RestClient` built with an absolute `baseUrl` |
|
||||
|
||||
## Why #01 and #05 use an absolute `requestTo()` but #02 does not
|
||||
|
||||
`EmployeeClient` builds its `RestClient` with `RestClient.Builder.baseUrl("http://hr-api.example.com")`.
|
||||
`EmployeeTemplateClient` builds its `RestTemplate` with `RestTemplateBuilder.rootUri(...)` instead.
|
||||
Spring Boot's `MockRestServiceServerAutoConfiguration` detects a `RestTemplate` built with a root
|
||||
URI and binds it through `RootUriRequestExpectationManager`
|
||||
(`org.springframework.boot.restclient.test.RootUriRequestExpectationManager`), which strips the
|
||||
root URI before matching - so `requestTo("/employees/2")` still works. There is no equivalent for
|
||||
`RestClient.Builder.baseUrl(...)`: a `RestClient` test has to match the full absolute URI, as #05's
|
||||
captured failure shows. This is covered in depth in the published post.
|
||||
Reference in New Issue
Block a user