Add testing-no-containers module: WireMock, MockWebServer, @MockitoBean/@MockitoSpyBean (post #43)
This commit is contained in:
@@ -40,8 +40,9 @@ files.
|
|||||||
| [`i18n/`](i18n) | [Internationalization (i18n) in Spring Boot 4: MessageSource, LocaleResolver and Localized ProblemDetail](https://ankurm.com/spring-boot-4-internationalization-messagesource-localeresolver-problemdetail/) | English, Marathi and Hindi message bundles, `Accept-Language` vs a cookie resolver, apostrophes and Devanagari digits in `MessageFormat`, the JVM default locale as a hidden fallback, localized validation messages and `ProblemDetail` titles |
|
| [`i18n/`](i18n) | [Internationalization (i18n) in Spring Boot 4: MessageSource, LocaleResolver and Localized ProblemDetail](https://ankurm.com/spring-boot-4-internationalization-messagesource-localeresolver-problemdetail/) | English, Marathi and Hindi message bundles, `Accept-Language` vs a cookie resolver, apostrophes and Devanagari digits in `MessageFormat`, the JVM default locale as a hidden fallback, localized validation messages and `ProblemDetail` titles |
|
||||||
| [`aot-cache/`](aot-cache) | [Faster Spring Boot Startup with the JDK AOT Cache (JEP 514/515): Benchmarks vs CDS and Native](https://ankurm.com/spring-boot-jdk-aot-cache-jep-514-515-startup-benchmarks/) | the JDK 25 AOT cache trained two ways, against a plain JVM, AppCDS, Spring's own AOT output and a GraalVM native image (ten interleaved rounds each), first-request latency, 24 cases where the cache silently stops applying (jar, timestamp, JVM build, ZGC, compact headers), `-XX:AOTMode=on`, Docker layer arithmetic |
|
| [`aot-cache/`](aot-cache) | [Faster Spring Boot Startup with the JDK AOT Cache (JEP 514/515): Benchmarks vs CDS and Native](https://ankurm.com/spring-boot-jdk-aot-cache-jep-514-515-startup-benchmarks/) | the JDK 25 AOT cache trained two ways, against a plain JVM, AppCDS, Spring's own AOT output and a GraalVM native image (ten interleaved rounds each), first-request latency, 24 cases where the cache silently stops applying (jar, timestamp, JVM build, ZGC, compact headers), `-XX:AOTMode=on`, Docker layer arithmetic |
|
||||||
| [`kotlin/`](kotlin) | [Kotlin with Spring Boot 4.1: Null Safety, Coroutines, and What Java Developers Get Wrong](https://ankurm.com/) | Kotlin 2.3.21 (Boot 4.1.1's own managed version, not the latest GA), the real `@Configuration class may not be final` and `@Bean method must not be private or final` failures without the allopen/noarg `spring`/`jpa` compiler plugins, `suspend fun`/`Flow<T>` controller methods proven to still resume off a Tomcat thread under plain `spring-boot-starter-webmvc`, a JSpecify `@NullMarked` Java method that still NPEs at runtime because the contract isn't runtime-checked, and `TestRestTemplate` confirmed absent from Boot 4.1's `spring-boot-test` jar |
|
| [`kotlin/`](kotlin) | [Kotlin with Spring Boot 4.1: Null Safety, Coroutines, and What Java Developers Get Wrong](https://ankurm.com/) | Kotlin 2.3.21 (Boot 4.1.1's own managed version, not the latest GA), the real `@Configuration class may not be final` and `@Bean method must not be private or final` failures without the allopen/noarg `spring`/`jpa` compiler plugins, `suspend fun`/`Flow<T>` controller methods proven to still resume off a Tomcat thread under plain `spring-boot-starter-webmvc`, a JSpecify `@NullMarked` Java method that still NPEs at runtime because the contract isn't runtime-checked, and `TestRestTemplate` confirmed absent from Boot 4.1's `spring-boot-test` jar |
|
||||||
|
| [`testing-no-containers/`](testing-no-containers) | [Testing Spring Boot 4 Without Containers: WireMock, MockWebServer and @MockitoBean](https://ankurm.com/) | `@MockBean`'s total removal proven with a real `javac` failure, `@MockitoBean`/`@MockitoSpyBean` context-cache behavior proven with real `org.springframework.test.context.cache` DEBUG log lines, WireMock fault injection against a real `RestClient`, a real `NoClassDefFoundError` from mixing `wiremock` with `wiremock-jetty12` (fixed with `wiremock-standalone`), and MockWebServer's `SocketEffect.ShutdownConnection` for genuinely severing a connection mid-response |
|
||||||
|
|
||||||
`core-di`, `core-beans`, `custom-starter`, `core-events`, `multi-datasource`, `i18n`, `aot-cache`, `kotlin` and `hexagonal` are the exception: they have **no `docs/` folder**. Their deeper material lives in
|
`core-di`, `core-beans`, `custom-starter`, `core-events`, `multi-datasource`, `i18n`, `aot-cache`, `kotlin`, `testing-no-containers` and `hexagonal` are the exception: they have **no `docs/` folder**. Their deeper material lives in
|
||||||
collapsible sections inside the articles themselves, and their captured output sits in a top-level `output/`
|
collapsible sections inside the articles themselves, and their captured output sits in a top-level `output/`
|
||||||
directory instead of `docs/output/` (`hexagonal` is also the one multi-module reactor in this
|
directory instead of `docs/output/` (`hexagonal` is also the one multi-module reactor in this
|
||||||
repo, so its output sits one level deeper still, under each module: `core/output/`,
|
repo, so its output sits one level deeper still, under each module: `core/output/`,
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# testing-no-containers
|
||||||
|
|
||||||
|
Companion project for the article **[Testing Spring Boot 4 Without Containers: WireMock, MockWebServer and @MockitoBean](https://ankurm.com/)** on **[ankurm.com](https://ankurm.com)**.
|
||||||
|
|
||||||
|
Every console block quoted in the article came out of `docs/output/`. These are real `mvn test`
|
||||||
|
and `javac` transcripts, not retyped output — including two real, captured failures (a
|
||||||
|
`NoClassDefFoundError` from a bad WireMock dependency combination, and a `@MockBean` compiler
|
||||||
|
error) that are as much the point of the post as the passing tests are.
|
||||||
|
|
||||||
|
There is deliberately **no `docs/NN-topic.md` chapter folder**: the deeper material lives in
|
||||||
|
collapsible "going deeper" sections inside the article itself, next to the paragraph each one
|
||||||
|
extends. `docs/output/` (captured transcripts) is the one thing that still lives under `docs/`.
|
||||||
|
|
||||||
|
## Versions
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| Spring Boot | 4.1.1 |
|
||||||
|
| Spring Framework | 7.0.9 |
|
||||||
|
| WireMock | 3.13.2 (`wiremock-standalone`; the 4.0.0 line is beta-only as of this writing — see the article) |
|
||||||
|
| OkHttp MockWebServer | 5.5.0 (`mockwebserver3-junit5`) |
|
||||||
|
| Mockito | 5.23.0 (Spring Boot 4.1.1's own managed version, via `mockito-bom`) |
|
||||||
|
| JDK | 25 (GraalVM CE 25.4.4.1.1) |
|
||||||
|
| Maven | 3.9 |
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export JAVA_HOME=/path/to/jdk-25
|
||||||
|
mvn test # runs everything below; no Docker, no Testcontainers, no running server required
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's demonstrated
|
||||||
|
|
||||||
|
| Area | Where |
|
||||||
|
|---|---|
|
||||||
|
| `@MockBean` is completely removed in Spring Boot 4 (not deprecated — the package is gone) | `docs/output/01-mockbean-removed-compiler-error.txt` |
|
||||||
|
| `@MockitoBean`/`@MockitoSpyBean` (Spring Framework 7's replacement) mocking a collaborator with no running server | `GreetingMockitoBeanATest.java`, `GreetingMockitoBeanBTest.java` |
|
||||||
|
| The identical `@MockitoBean` override set is the `ApplicationContext` cache key: two classes with the same override reuse one context | `GreetingMockitoBeanATest.java` / `GreetingMockitoBeanBTest.java`, proven via real `org.springframework.test.context.cache` DEBUG log lines |
|
||||||
|
| `@MockitoSpyBean` is a different override set than `@MockitoBean`, even against the same type — a genuine cache miss | `GreetingMockitoSpyBeanCTest.java` |
|
||||||
|
| WireMock fault injection (`Fault.CONNECTION_RESET_BY_PEER`, `withFixedDelay`) against a real `RestClient` | `QuoteClientWireMockTest.java` |
|
||||||
|
| The wrong way and the right way to pull a WireMock server into the classpath in the 3.x line | `docs/output/02-wiremock-bare-fatalstartup.txt`, `docs/output/03-wiremock-plus-jetty12-noclassdef.txt`, `pom.xml` |
|
||||||
|
| MockWebServer's lower-level socket primitives (`SocketEffect.ShutdownConnection`) for genuinely severing a connection mid-response, vs. just sending truncated JSON | `QuoteClientMockWebServerTest.java` |
|
||||||
|
| Boot 4.1's `ClientHttpRequestFactoryBuilder`/`HttpClientSettings` API (not the Boot-3-era `ClientHttpRequestFactorySettings`/`ClientHttpRequestFactories` pair, which no longer exists) | `QuoteClient.java` |
|
||||||
|
|
||||||
|
## Captured output
|
||||||
|
|
||||||
|
| File | What it shows |
|
||||||
|
|---|---|
|
||||||
|
| `01-mockbean-removed-compiler-error.txt` | a standalone `javac` run against code importing `org.springframework.boot.test.mock.mockito.MockBean`: "package ... does not exist" |
|
||||||
|
| `02-wiremock-bare-fatalstartup.txt` | the bare `wiremock` artifact alone: `FatalStartupException`, no `HttpServerFactory` on the classpath |
|
||||||
|
| `03-wiremock-plus-jetty12-noclassdef.txt` | `wiremock` + `wiremock-jetty12` together: a mismatched-Jetty-version `NoClassDefFoundError` at server startup |
|
||||||
|
| `04-full-test-run-passing.txt` | the real, full `mvn clean test` run: all 8 tests passing, including the real `org.springframework.test.context.cache` DEBUG lines that prove the hit/miss behavior |
|
||||||
|
|
||||||
|
Nothing here needs Docker, a running server, or a network connection outside the JVM — that is the
|
||||||
|
whole premise of the post.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
Picked up JAVA_TOOL_OPTIONS: -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts -Djavax.net.ssl.trustStoreType=JKS -Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=38339 -Dhttp.nonProxyHosts=localhost|127.0.0.1|::1|127.*|0.*|::|169.254.*|api.anthropic.com|api-staging.anthropic.com|api-pr-preview.anthropic.com|mcp-proxy.anthropic.com|mcp-proxy-staging.anthropic.com|registry.npmjs.org|jsr.io|npm.jsr.io|pypi.org|files.pythonhosted.org|index.crates.io|proxy.golang.org|host.docker.internal|10.*|172.16.*|172.17.*|172.18.*|172.19.*|172.20.*|172.21.*|172.22.*|172.23.*|172.24.*|172.25.*|172.26.*|172.27.*|172.28.*|172.29.*|172.30.*|172.31.*|192.168.*|100.64.0.0/10|*.svc.cluster.local|*.svc.cluster.local -Djdk.http.auth.tunneling.disabledSchemes= -Djdk.http.auth.proxying.disabledSchemes=
|
||||||
|
/tmp/mockbean-removed-demo/com/ankurm/testingnc/LegacyMockBeanTest.java:5: error: package org.springframework.boot.test.mock.mockito does not exist
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
^
|
||||||
|
/tmp/mockbean-removed-demo/com/ankurm/testingnc/LegacyMockBeanTest.java:10: error: cannot find symbol
|
||||||
|
@MockBean
|
||||||
|
^
|
||||||
|
symbol: class MockBean
|
||||||
|
location: class LegacyMockBeanTest
|
||||||
|
2 errors
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.924 s <<< FAILURE! -- in com.ankurm.testingnc.QuoteClientWireMockTest
|
||||||
|
[ERROR] com.ankurm.testingnc.QuoteClientWireMockTest -- Time elapsed: 0.924 s <<< ERROR!
|
||||||
|
com.github.tomakehurst.wiremock.common.FatalStartupException: Jetty 11 is not present and no suitable HttpServerFactory extension was found. Please ensure that the classpath includes a WireMock extension that provides an HttpServerFactory implementation. See http://wiremock.org/docs/extending-wiremock/ for more information.
|
||||||
|
at com.github.tomakehurst.wiremock.http.HttpServerFactoryLoader.couldNotFindSuitableServerException(HttpServerFactoryLoader.java:85)
|
||||||
|
at com.github.tomakehurst.wiremock.http.HttpServerFactoryLoader.pickMostAppropriateFrom(HttpServerFactoryLoader.java:73)
|
||||||
|
at com.github.tomakehurst.wiremock.http.HttpServerFactoryLoader.load(HttpServerFactoryLoader.java:57)
|
||||||
|
at com.github.tomakehurst.wiremock.WireMockServer.getHttpServerFactory(WireMockServer.java:89)
|
||||||
|
at com.github.tomakehurst.wiremock.WireMockServer.<init>(WireMockServer.java:72)
|
||||||
|
at com.github.tomakehurst.wiremock.junit5.WireMockExtension.startServerIfRequired(WireMockExtension.java:233)
|
||||||
|
at com.github.tomakehurst.wiremock.junit5.WireMockExtension.beforeAll(WireMockExtension.java:300)
|
||||||
|
at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
|
||||||
|
|
||||||
|
[ERROR] Errors:
|
||||||
|
[ERROR] QuoteClientWireMockTest ? FatalStartup Jetty 11 is not present and no suitable HttpServerFactory extension was found. Please ensure that the classpath includes a WireMock extension that provides an HttpServerFactory implementation. See http://wiremock.org/docs/extending-wiremock/ for more information.
|
||||||
|
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
|
||||||
|
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.6:test (default-test) on project testing-no-containers:
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] See /home/claude/spring-boot-demo/testing-no-containers/target/surefire-reports for the individual test results.
|
||||||
|
[ERROR] See dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
|
||||||
|
[ERROR] -> [Help 1]
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
|
||||||
|
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] For more information about the errors and possible solutions, please read the following articles:
|
||||||
|
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 1.463 s <<< FAILURE! -- in com.ankurm.testingnc.QuoteClientWireMockTest
|
||||||
|
[ERROR] com.ankurm.testingnc.QuoteClientWireMockTest -- Time elapsed: 1.463 s <<< ERROR!
|
||||||
|
java.lang.NoClassDefFoundError: org/eclipse/jetty/io/WriteFlusher$Listener
|
||||||
|
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
|
||||||
|
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:962)
|
||||||
|
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:144)
|
||||||
|
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:776)
|
||||||
|
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:691)
|
||||||
|
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:620)
|
||||||
|
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:578)
|
||||||
|
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
|
||||||
|
at com.github.tomakehurst.wiremock.jetty12.Jetty12HttpServer.createHttpConnector(Jetty12HttpServer.java:88)
|
||||||
|
at com.github.tomakehurst.wiremock.jetty.JettyHttpServer.<init>(JettyHttpServer.java:79)
|
||||||
|
at com.github.tomakehurst.wiremock.jetty12.Jetty12HttpServer.<init>(Jetty12HttpServer.java:74)
|
||||||
|
at com.github.tomakehurst.wiremock.jetty12.Jetty12HttpServerFactory.buildHttpServer(Jetty12HttpServerFactory.java:33)
|
||||||
|
at com.github.tomakehurst.wiremock.WireMockServer.<init>(WireMockServer.java:75)
|
||||||
|
at com.github.tomakehurst.wiremock.junit5.WireMockExtension.startServerIfRequired(WireMockExtension.java:233)
|
||||||
|
at com.github.tomakehurst.wiremock.junit5.WireMockExtension.beforeAll(WireMockExtension.java:300)
|
||||||
|
at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
|
||||||
|
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.io.WriteFlusher$Listener
|
||||||
|
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
|
||||||
|
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
|
||||||
|
... 16 more
|
||||||
|
|
||||||
|
[ERROR] Errors:
|
||||||
|
[ERROR] QuoteClientWireMockTest ? NoClassDefFound org/eclipse/jetty/io/WriteFlusher$Listener
|
||||||
|
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
|
||||||
|
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.6:test (default-test) on project testing-no-containers:
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] See /home/claude/spring-boot-demo/testing-no-containers/target/surefire-reports for the individual test results.
|
||||||
|
[ERROR] See dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
|
||||||
|
[ERROR] -> [Help 1]
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
|
||||||
|
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] For more information about the errors and possible solutions, please read the following articles:
|
||||||
|
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
[INFO] Scanning for projects...
|
||||||
|
[INFO]
|
||||||
|
[INFO] ------------------< com.ankurm:testing-no-containers >------------------
|
||||||
|
[INFO] Building testing-no-containers 1.0.0
|
||||||
|
[INFO] from pom.xml
|
||||||
|
[INFO] --------------------------------[ jar ]---------------------------------
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- clean:3.5.0:clean (default-clean) @ testing-no-containers ---
|
||||||
|
[INFO] Deleting /home/claude/spring-boot-demo/testing-no-containers/target
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- resources:3.5.0:resources (default-resources) @ testing-no-containers ---
|
||||||
|
[INFO] Copying 1 resource from src/main/resources to target/classes
|
||||||
|
[INFO] Copying 0 resource from src/main/resources to target/classes
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- compiler:3.15.0:compile (default-compile) @ testing-no-containers ---
|
||||||
|
[INFO] Recompiling the module because of changed source code.
|
||||||
|
[INFO] Compiling 5 source files with javac [debug parameters release 25] to target/classes
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- resources:3.5.0:testResources (default-testResources) @ testing-no-containers ---
|
||||||
|
[INFO] Copying 1 resource from src/test/resources to target/test-classes
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- compiler:3.15.0:testCompile (default-testCompile) @ testing-no-containers ---
|
||||||
|
[INFO] Recompiling the module because of changed dependency.
|
||||||
|
[INFO] Compiling 5 source files with javac [debug parameters release 25] to target/test-classes
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- surefire:3.5.6:test (default-test) @ testing-no-containers ---
|
||||||
|
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
|
||||||
|
[INFO]
|
||||||
|
[INFO] -------------------------------------------------------
|
||||||
|
[INFO] T E S T S
|
||||||
|
[INFO] -------------------------------------------------------
|
||||||
|
[INFO] Running com.ankurm.testingnc.GreetingMockitoSpyBeanCTest
|
||||||
|
11:46:56.481 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [com.ankurm.testingnc.GreetingMockitoSpyBeanCTest]: GreetingMockitoSpyBeanCTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
|
||||||
|
11:46:56.753 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration com.ankurm.testingnc.Application for test class com.ankurm.testingnc.GreetingMockitoSpyBeanCTest
|
||||||
|
11:46:56.879 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [com.ankurm.testingnc.GreetingMockitoSpyBeanCTest]: GreetingMockitoSpyBeanCTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
|
||||||
|
11:46:56.886 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration com.ankurm.testingnc.Application for test class com.ankurm.testingnc.GreetingMockitoSpyBeanCTest
|
||||||
|
|
||||||
|
. ____ _ __ _ _
|
||||||
|
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||||
|
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||||
|
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||||
|
' |____| .__|_| |_|_| |_\__, | / / / /
|
||||||
|
=========|_|==============|___/=/_/_/_/
|
||||||
|
|
||||||
|
:: Spring Boot :: (v4.1.1)
|
||||||
|
|
||||||
|
2026-10-08T11:46:57.415+05:30 INFO 13524 --- [ main] c.a.t.GreetingMockitoSpyBeanCTest : Starting GreetingMockitoSpyBeanCTest using Java 25.0.4.1.1 with PID 13524 (started by root in /home/claude/spring-boot-demo/testing-no-containers)
|
||||||
|
2026-10-08T11:46:57.418+05:30 INFO 13524 --- [ main] c.a.t.GreetingMockitoSpyBeanCTest : No active profile set, falling back to 1 default profile: "default"
|
||||||
|
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
|
||||||
|
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
|
||||||
|
2026-10-08T11:47:01.581+05:30 INFO 13524 --- [ main] c.a.t.GreetingMockitoSpyBeanCTest : Started GreetingMockitoSpyBeanCTest in 4.598 seconds (process running for 6.85)
|
||||||
|
2026-10-08T11:47:01.589+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 0, parentContextCount = 0, hitCount = 0, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.607+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 1, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.611+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 2, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.613+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 3, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.637+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 4, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.638+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 5, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.647+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 6, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.766+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 7, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.768+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 8, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.773+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 9, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.791+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 10, missCount = 1, failureCount = 0]
|
||||||
|
2026-10-08T11:47:01.792+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 1, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 11, missCount = 1, failureCount = 0]
|
||||||
|
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.679 s -- in com.ankurm.testingnc.GreetingMockitoSpyBeanCTest
|
||||||
|
[INFO] Running com.ankurm.testingnc.GreetingMockitoBeanBTest
|
||||||
|
2026-10-08T11:47:01.820+05:30 INFO 13524 --- [ main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.ankurm.testingnc.GreetingMockitoBeanBTest]: GreetingMockitoBeanBTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
|
||||||
|
2026-10-08T11:47:01.828+05:30 INFO 13524 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.ankurm.testingnc.Application for test class com.ankurm.testingnc.GreetingMockitoBeanBTest
|
||||||
|
2026-10-08T11:47:01.830+05:30 INFO 13524 --- [ main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.ankurm.testingnc.GreetingMockitoBeanBTest]: GreetingMockitoBeanBTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
|
||||||
|
2026-10-08T11:47:01.834+05:30 INFO 13524 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.ankurm.testingnc.Application for test class com.ankurm.testingnc.GreetingMockitoBeanBTest
|
||||||
|
|
||||||
|
. ____ _ __ _ _
|
||||||
|
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||||
|
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||||
|
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||||
|
' |____| .__|_| |_|_| |_\__, | / / / /
|
||||||
|
=========|_|==============|___/=/_/_/_/
|
||||||
|
|
||||||
|
:: Spring Boot :: (v4.1.1)
|
||||||
|
|
||||||
|
2026-10-08T11:47:01.986+05:30 INFO 13524 --- [ main] c.a.testingnc.GreetingMockitoBeanBTest : Starting GreetingMockitoBeanBTest using Java 25.0.4.1.1 with PID 13524 (started by root in /home/claude/spring-boot-demo/testing-no-containers)
|
||||||
|
2026-10-08T11:47:01.987+05:30 INFO 13524 --- [ main] c.a.testingnc.GreetingMockitoBeanBTest : No active profile set, falling back to 1 default profile: "default"
|
||||||
|
2026-10-08T11:47:02.978+05:30 INFO 13524 --- [ main] c.a.testingnc.GreetingMockitoBeanBTest : Started GreetingMockitoBeanBTest in 1.118 seconds (process running for 8.246)
|
||||||
|
2026-10-08T11:47:02.979+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 0, parentContextCount = 0, hitCount = 11, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:02.981+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 12, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:02.981+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 13, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:02.982+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 14, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:02.986+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 15, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:02.988+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 16, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:02.991+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 17, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:02.999+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 18, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:03.000+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 19, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:03.003+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 20, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:03.008+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 21, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:03.009+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 22, missCount = 2, failureCount = 0]
|
||||||
|
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.195 s -- in com.ankurm.testingnc.GreetingMockitoBeanBTest
|
||||||
|
[INFO] Running com.ankurm.testingnc.QuoteClientMockWebServerTest
|
||||||
|
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.729 s -- in com.ankurm.testingnc.QuoteClientMockWebServerTest
|
||||||
|
[INFO] Running com.ankurm.testingnc.QuoteClientWireMockTest
|
||||||
|
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.896 s -- in com.ankurm.testingnc.QuoteClientWireMockTest
|
||||||
|
[INFO] Running com.ankurm.testingnc.GreetingMockitoBeanATest
|
||||||
|
2026-10-08T11:47:05.788+05:30 INFO 13524 --- [ main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.ankurm.testingnc.GreetingMockitoBeanATest]: GreetingMockitoBeanATest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
|
||||||
|
2026-10-08T11:47:05.797+05:30 INFO 13524 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.ankurm.testingnc.Application for test class com.ankurm.testingnc.GreetingMockitoBeanATest
|
||||||
|
2026-10-08T11:47:05.808+05:30 INFO 13524 --- [ main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.ankurm.testingnc.GreetingMockitoBeanATest]: GreetingMockitoBeanATest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
|
||||||
|
2026-10-08T11:47:05.810+05:30 INFO 13524 --- [ main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.ankurm.testingnc.Application for test class com.ankurm.testingnc.GreetingMockitoBeanATest
|
||||||
|
2026-10-08T11:47:05.813+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 0, parentContextCount = 0, hitCount = 23, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.815+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 24, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.816+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 25, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.817+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 26, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.819+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 27, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.822+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 28, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.822+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 29, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.824+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 30, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.826+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 31, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.827+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 32, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.828+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 33, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.829+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 34, missCount = 2, failureCount = 0]
|
||||||
|
2026-10-08T11:47:05.831+05:30 DEBUG 13524 --- [ main] org.springframework.test.context.cache : Spring test ApplicationContext cache statistics: [DefaultContextCache@778c2e7c size = 2, maxSize = 32, contextUsageCount = 1, parentContextCount = 0, hitCount = 35, missCount = 2, failureCount = 0]
|
||||||
|
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.050 s -- in com.ankurm.testingnc.GreetingMockitoBeanATest
|
||||||
|
[INFO]
|
||||||
|
[INFO] Results:
|
||||||
|
[INFO]
|
||||||
|
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO]
|
||||||
|
[INFO] ------------------------------------------------------------------------
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
[INFO] ------------------------------------------------------------------------
|
||||||
|
[INFO] Total time: 16.113 s
|
||||||
|
[INFO] Finished at: 2026-10-08T11:47:06+05:30
|
||||||
|
[INFO] ------------------------------------------------------------------------
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.831 s <<< FAILURE! -- in com.ankurm.testingnc.QuoteClientMockWebServerTest
|
||||||
|
[ERROR] com.ankurm.testingnc.QuoteClientMockWebServerTest.truncatedBodyIsNotTheSameAsASeveredConnection -- Time elapsed: 1.564 s <<< FAILURE!
|
||||||
|
java.lang.AssertionError:
|
||||||
|
|
||||||
|
Expecting actual throwable to be an instance of:
|
||||||
|
org.springframework.web.client.ResourceAccessException
|
||||||
|
but was:
|
||||||
|
org.springframework.web.client.RestClientException: Error while extracting response for type [com.ankurm.testingnc.Quote] and content type [application/json]
|
||||||
|
at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:274)
|
||||||
|
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:931)
|
||||||
|
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.lambda$body$0(DefaultRestClient.java:833)
|
||||||
|
...(93 remaining lines not displayed - this can be changed with Assertions.setMaxStackTraceElementsDisplayed)
|
||||||
|
at com.ankurm.testingnc.QuoteClientMockWebServerTest.truncatedBodyIsNotTheSameAsASeveredConnection(QuoteClientMockWebServerTest.java:55)
|
||||||
|
|
||||||
|
[ERROR] Failures:
|
||||||
|
[ERROR] QuoteClientMockWebServerTest.truncatedBodyIsNotTheSameAsASeveredConnection:55
|
||||||
|
Expecting actual throwable to be an instance of:
|
||||||
|
org.springframework.web.client.ResourceAccessException
|
||||||
|
but was:
|
||||||
|
org.springframework.web.client.RestClientException: Error while extracting response for type [com.ankurm.testingnc.Quote] and content type [application/json]
|
||||||
|
at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:274)
|
||||||
|
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:931)
|
||||||
|
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.lambda$body$0(DefaultRestClient.java:833)
|
||||||
|
...(93 remaining lines not displayed - this can be changed with Assertions.setMaxStackTraceElementsDisplayed)
|
||||||
|
[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0
|
||||||
|
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.6:test (default-test) on project testing-no-containers: There are test failures.
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] See /home/claude/spring-boot-demo/testing-no-containers/target/surefire-reports for the individual test results.
|
||||||
|
[ERROR] See dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
|
||||||
|
[ERROR] -> [Help 1]
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
|
||||||
|
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
|
||||||
|
[ERROR]
|
||||||
|
[ERROR] For more information about the errors and possible solutions, please read the following articles:
|
||||||
|
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>4.1.1</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.ankurm</groupId>
|
||||||
|
<artifactId>testing-no-containers</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<name>testing-no-containers</name>
|
||||||
|
<description>Testing Spring Boot 4 without containers: @MockitoBean/@MockitoSpyBean, WireMock fault injection, MockWebServer for RestClient, and context-cache behavior</description>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>25</java.version>
|
||||||
|
<!-- org.wiremock:wiremock's 4.0.0 line is beta-only as of this writing (4.0.0-beta.1
|
||||||
|
through 4.0.0-beta.40 on Maven Central): 3.13.2 is the real latest GA; see the post. -->
|
||||||
|
<wiremock.version>3.13.2</wiremock.version>
|
||||||
|
<mockwebserver.version>5.5.0</mockwebserver.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webmvc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-restclient</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- WireMock: standalone HTTP server with fault injection, not managed by
|
||||||
|
spring-boot-dependencies; pin it explicitly. The bare "wiremock" artifact no longer
|
||||||
|
bundles a server in the 3.x line (FatalStartupException: "Jetty 11 is not present and
|
||||||
|
no suitable HttpServerFactory extension was found"), and adding wiremock-jetty12 on top
|
||||||
|
of it is worse, not better: wiremock's own transitive deps still drag in real Jetty 11
|
||||||
|
modules (jetty-servlet, jetty-servlets, jetty-webapp, http2-server at 11.0.26) alongside
|
||||||
|
wiremock-jetty12's Jetty 12 ones, Maven's dependency mediation then picks one jetty-io
|
||||||
|
version for everyone, and the mismatched pair throws NoClassDefFoundError at server
|
||||||
|
startup for a class as basic as org.eclipse.jetty.io.WriteFlusher$Listener. The fix is
|
||||||
|
wiremock-standalone: a single shaded jar with its own relocated copy of Jetty
|
||||||
|
(wiremock.org.eclipse.jetty.*), so nothing on your own classpath can clash with it;
|
||||||
|
see the post. -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wiremock</groupId>
|
||||||
|
<artifactId>wiremock-standalone</artifactId>
|
||||||
|
<version>${wiremock.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MockWebServer: OkHttp's own stub server, JUnit 5 extension flavor. Also not
|
||||||
|
managed by spring-boot-dependencies. -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>mockwebserver3-junit5</artifactId>
|
||||||
|
<version>${mockwebserver.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>testing-no-containers</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
public interface GreetingService {
|
||||||
|
String greet(String name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class GreetingServiceImpl implements GreetingService {
|
||||||
|
@Override
|
||||||
|
public String greet(String name) {
|
||||||
|
return "Hello, " + name + "!";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
public record Quote(long id, String text, String author) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||||
|
import org.springframework.boot.http.client.HttpClientSettings;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A deliberately simple HTTP client, built with the timeout wired so a test can actually
|
||||||
|
* trigger it -- see the fault-injection section of the post for why a 300ms read timeout
|
||||||
|
* matters. Boot 4.1 replaced the old {@code ClientHttpRequestFactorySettings}/
|
||||||
|
* {@code ClientHttpRequestFactories} pair (Boot 3.x) with {@link ClientHttpRequestFactoryBuilder}
|
||||||
|
* and the {@link HttpClientSettings} record -- a tutorial written against Boot 3 will not
|
||||||
|
* compile here; see the post.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class QuoteClient {
|
||||||
|
|
||||||
|
private final RestClient restClient;
|
||||||
|
|
||||||
|
public QuoteClient(@Value("${quotes.api.base-url}") String baseUrl) {
|
||||||
|
var settings = HttpClientSettings.defaults().withReadTimeout(Duration.ofMillis(300));
|
||||||
|
this.restClient = RestClient.builder()
|
||||||
|
.baseUrl(baseUrl)
|
||||||
|
.requestFactory(ClientHttpRequestFactoryBuilder.detect().build(settings))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quote getQuote(long id) {
|
||||||
|
return restClient.get()
|
||||||
|
.uri("/quotes/{id}", id)
|
||||||
|
.retrieve()
|
||||||
|
.body(Quote.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
spring.application.name=testing-no-containers
|
||||||
|
quotes.api.base-url=http://localhost:9999
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliberately has the exact same @MockitoBean override as {@link GreetingMockitoBeanBTest}:
|
||||||
|
* same type, same (default) name, same reset mode. That identical override set is the context
|
||||||
|
* cache key, and the point of this pair is to prove, with a real captured log line, that the
|
||||||
|
* second class reuses the first class's ApplicationContext instead of building a new one.
|
||||||
|
*/
|
||||||
|
@SpringBootTest
|
||||||
|
class GreetingMockitoBeanATest {
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
GreetingService greetingService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void mockedGreetingIsReturned() {
|
||||||
|
when(greetingService.greet("Ada")).thenReturn("yo Ada");
|
||||||
|
|
||||||
|
assertThat(greetingService.greet("Ada")).isEqualTo("yo Ada");
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class GreetingMockitoBeanBTest {
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
GreetingService greetingService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void mockedGreetingIsReturnedHere_too() {
|
||||||
|
when(greetingService.greet("Linus")).thenReturn("yo Linus");
|
||||||
|
|
||||||
|
assertThat(greetingService.greet("Linus")).isEqualTo("yo Linus");
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A @MockitoSpyBean override is a different override set than @MockitoBean (A and B above),
|
||||||
|
* even though both target GreetingService -- a spy wraps the real bean, a mock replaces it
|
||||||
|
* entirely. Different override set, different cache key: this class should cost a brand new
|
||||||
|
* ApplicationContext, a real, captured cache miss.
|
||||||
|
*/
|
||||||
|
@SpringBootTest
|
||||||
|
class GreetingMockitoSpyBeanCTest {
|
||||||
|
|
||||||
|
@MockitoSpyBean
|
||||||
|
GreetingServiceImpl greetingService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void realMethodRunsAndCanStillBeVerified() {
|
||||||
|
String result = greetingService.greet("Grace");
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo("Hello, Grace!");
|
||||||
|
verify(greetingService).greet("Grace");
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import mockwebserver3.MockResponse;
|
||||||
|
import mockwebserver3.MockWebServer;
|
||||||
|
import mockwebserver3.RecordedRequest;
|
||||||
|
import mockwebserver3.SocketEffect;
|
||||||
|
import mockwebserver3.junit5.StartStop;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.web.client.ResourceAccessException;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same QuoteClient, same scenarios, a different stub server. MockWebServer is a real OkHttp
|
||||||
|
* TCP server too -- not a mock of the HTTP client -- but it is lower-level than WireMock: no
|
||||||
|
* named "fault" API, so a reset connection or a slow response is built from the raw socket
|
||||||
|
* primitives OkHttp's MockResponse exposes.
|
||||||
|
*/
|
||||||
|
class QuoteClientMockWebServerTest {
|
||||||
|
|
||||||
|
@StartStop
|
||||||
|
MockWebServer server = new MockWebServer();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsTheStubbedQuote() throws InterruptedException {
|
||||||
|
server.enqueue(new MockResponse.Builder()
|
||||||
|
.addHeader("Content-Type", "application/json")
|
||||||
|
.body("{\"id\":42,\"text\":\"Talk is cheap. Show me the code.\",\"author\":\"Linus Torvalds\"}")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
var client = new QuoteClient(server.url("/").toString());
|
||||||
|
Quote quote = client.getQuote(42);
|
||||||
|
|
||||||
|
assertThat(quote.text()).isEqualTo("Talk is cheap. Show me the code.");
|
||||||
|
|
||||||
|
RecordedRequest recorded = server.takeRequest(1, TimeUnit.SECONDS);
|
||||||
|
assertThat(recorded).isNotNull();
|
||||||
|
assertThat(recorded.getTarget()).isEqualTo("/quotes/42");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void socketClosedMidResponseSurfacesAsResourceAccessException() {
|
||||||
|
// No named "connection reset" fault here, unlike WireMock -- it's built from the raw
|
||||||
|
// primitive: MockResponse.Builder.onResponseStart(SocketEffect) runs the effect right as
|
||||||
|
// the server begins writing the response. SocketEffect.ShutdownConnection tears down the
|
||||||
|
// TCP connection at that point, so the client never receives a complete (or even
|
||||||
|
// well-formed) response -- this is a genuine severed connection, not merely truncated
|
||||||
|
// JSON that still parses as a body. (An earlier draft of this test just sent a short,
|
||||||
|
// invalid JSON body and got a RestClientException -- a real response the client could
|
||||||
|
// read and fail to parse, which is a different failure mode than the connection actually
|
||||||
|
// dying. See the post.)
|
||||||
|
server.enqueue(new MockResponse.Builder()
|
||||||
|
.addHeader("Content-Type", "application/json")
|
||||||
|
.onResponseStart(SocketEffect.ShutdownConnection.INSTANCE)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
var client = new QuoteClient(server.url("/").toString());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> client.getQuote(99))
|
||||||
|
.isInstanceOf(ResourceAccessException.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.ankurm.testingnc;
|
||||||
|
|
||||||
|
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||||
|
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.springframework.web.client.ResourceAccessException;
|
||||||
|
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No Spring context here at all -- QuoteClient only needs a base URL string, so this is a
|
||||||
|
* plain JUnit 5 test against a real WireMock server. "Without containers" in this post means
|
||||||
|
* no Docker, not no Spring; this test also happens to need no Spring.
|
||||||
|
*/
|
||||||
|
class QuoteClientWireMockTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static WireMockExtension wireMock = WireMockExtension.newInstance().build();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsTheStubbedQuote() {
|
||||||
|
wireMock.stubFor(get(urlEqualTo("/quotes/42")).willReturn(aResponse()
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody("{\"id\":42,\"text\":\"Talk is cheap. Show me the code.\",\"author\":\"Linus Torvalds\"}")));
|
||||||
|
|
||||||
|
var client = new QuoteClient(wireMock.baseUrl());
|
||||||
|
Quote quote = client.getQuote(42);
|
||||||
|
|
||||||
|
assertThat(quote.text()).isEqualTo("Talk is cheap. Show me the code.");
|
||||||
|
assertThat(quote.author()).isEqualTo("Linus Torvalds");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void connectionResetByPeerSurfacesAsResourceAccessException() {
|
||||||
|
wireMock.stubFor(get(urlEqualTo("/quotes/99")).willReturn(aResponse()
|
||||||
|
.withFault(com.github.tomakehurst.wiremock.http.Fault.CONNECTION_RESET_BY_PEER)));
|
||||||
|
|
||||||
|
var client = new QuoteClient(wireMock.baseUrl());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> client.getQuote(99))
|
||||||
|
.isInstanceOf(ResourceAccessException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fixedDelayPastTheReadTimeoutSurfacesAsResourceAccessException() {
|
||||||
|
// QuoteClient is built with a 300ms read timeout; this stub takes 1000ms to respond.
|
||||||
|
// ClientHttpRequestFactoryBuilder.detect() picks whichever HTTP client implementation
|
||||||
|
// is actually on the classpath, and that choice changes the wording: the JDK HttpClient
|
||||||
|
// reports a cancelled request as "Request cancelled" (java.net.http.HttpTimeoutException),
|
||||||
|
// while Apache HttpClient 5 would say "Read timed out" (java.net.SocketTimeoutException).
|
||||||
|
// Both are ResourceAccessException at the Spring level -- that type, not the message
|
||||||
|
// text, is the thing worth asserting on; see the post.
|
||||||
|
wireMock.stubFor(get(urlEqualTo("/quotes/7")).willReturn(aResponse()
|
||||||
|
.withFixedDelay(1000)
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody("{\"id\":7,\"text\":\"late\",\"author\":\"nobody\"}")));
|
||||||
|
|
||||||
|
var client = new QuoteClient(wireMock.baseUrl());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> client.getQuote(7))
|
||||||
|
.isInstanceOf(ResourceAccessException.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
quotes.api.base-url=http://localhost:9999
|
||||||
|
logging.level.org.springframework.test.context.cache=DEBUG
|
||||||
Reference in New Issue
Block a user