You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
description: Debug Maven Surefire unit tests by running them in JDWP "wait for debugger" mode (`-Dmaven.surefire.debug`) and attaching IntelliJ/VS Code/jdb. Use when asked to debug/step through a failing JUnit test, attach a debugger to a Maven test run, or run `mvn test -Dtest=Class[#method]` suspended on a port (including multi-module `-pl` runs).
3
+
description: Debug Maven Surefire unit tests by running them in JDWP "wait for debugger" mode (`-Dmaven.surefire.debug`) and attaching to the forked test JVM using **jdb** (preferred for CLI/agent debugging), IntelliJ, or VS Code. Use when asked to debug/step through a failing JUnit test, attach a debugger to a Maven test run, or run `mvn test -Dtest=Class[#method]` suspended on a port (including multi-module `-pl` runs). The JVM will block at startup until a debugger attaches; the agent should attach with `jdb -attach <host>:<port>` and drive the session from the terminal.
4
4
---
5
5
6
6
# debug-surefire
7
7
8
-
Run Maven Surefire tests suspended in JDWP so you can attach a debugger and step through the code.
8
+
Run Maven Surefire tests **suspended in JDWP** so you can attach a debugger and step through the code.
9
+
In headless/agent environments, **attach with `jdb`** (the JDK’s command-line debugger).
10
+
11
+
## What this does
12
+
13
+
- Runs `mvn test` with Surefire in debug mode via `-Dmaven.surefire.debug`.
14
+
- Surefire launches the **forked test JVM** with a JDWP socket and `suspend=y`, meaning:
15
+
- the forked JVM starts,
16
+
- prints “Listening for transport dt_socket at address: <port>”,
17
+
- then **waits** until a debugger attaches,
18
+
- only then does it begin executing tests.
19
+
20
+
This is important: you do **not** attach to the `mvn` process itself; you attach to the **forked JVM** running the tests.
If the method is overloaded, specify argument types:
159
+
160
+
-`stop in com.example.FooService.doWork(int,java.lang.String)`
161
+
162
+
Note: `jdb` supports **deferred breakpoints**. If the class isn’t loaded yet, it will still accept the breakpoint and activate it when the class loads.
163
+
164
+
#### 3) Break on the *failure*, not just your guess
165
+
166
+
For unit tests, breaking on the thrown assertion/error is often faster than guessing a line.
Tip: `catch all java.lang.Throwable` is the nuclear option. It works, but it can get loud.
184
+
185
+
#### 4) Resume and drive
186
+
187
+
Once breakpoints/catches are set:
188
+
189
+
-`cont`
190
+
191
+
When you hit a breakpoint:
192
+
193
+
-`where` to see the call stack
194
+
-`list` to see nearby source
195
+
-`locals` to see local variables
196
+
-`print someVar` or `print this.someField` to inspect
197
+
-`next` to step over, `step` to step into
198
+
199
+
#### 5) Find the “real” test thread quickly
200
+
201
+
Surefire + JUnit can spin up multiple threads (and Maven itself has its own). When in doubt:
202
+
203
+
-`threads`
204
+
-`where all`
205
+
206
+
Then pick the thread that’s in your test/code-under-test:
207
+
208
+
-`thread <id>`
209
+
-`where`
210
+
211
+
#### 6) Keep the JVM count predictable
212
+
213
+
Surefire forking and parallelism can make debugging confusing. If you see multiple JVMs / inconsistent behavior, pass Maven flags to keep it single and fresh:
#### 7) Use “thread-only” breakpoints when concurrency matters
222
+
223
+
By default, `jdb` breakpoints suspend all threads, which can create deadlocks in concurrent tests. You can tell jdb to suspend only the thread that hits the breakpoint:
224
+
225
+
-`stop thread in com.example.concurrent.Worker.run`
226
+
227
+
(You can also target a specific thread id with `stop thread <thread_id> in ...` if needed.)
228
+
229
+
---
230
+
231
+
## jdb startup customization (optional but powerful)
232
+
233
+
jdb will execute startup commands from `jdb.ini` or `.jdbrc` in either `user.home` or `user.dir`.
234
+
235
+
This is handy for keeping your default exclusions/catches consistent, e.g.:
0 commit comments