Skillsdebug-surefire
D

debug-surefire

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.

eclipse-rdf4j
392 stars
7.8k downloads
Updated 6d ago

Readme

debug-surefire follows the SKILL.md standard. Use the install command to add it to your agent stack.

---
name: debug-surefire
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.
---

# debug-surefire

Run Maven Surefire tests **suspended in JDWP** so you can attach a debugger and step through the code.  
In headless/agent environments, **attach with `jdb`** (the JDK’s command-line debugger).

## What this does

- Runs `mvn test` with Surefire in debug mode via `-Dmaven.surefire.debug`.
- Surefire launches the **forked test JVM** with a JDWP socket and `suspend=y`, meaning:
  - the forked JVM starts,
  - prints “Listening for transport dt_socket at address: <port>”,
  - then **waits** until a debugger attaches,
  - only then does it begin executing tests.

This is important: you do **not** attach to the `mvn` process itself; you attach to the **forked JVM** running the tests.

## Quick start

1) Start a suspended test run

- Debug a test class:
  - `.codex/skills/debug-surefire/scripts/debug-surefire.sh --test-class MyTest`
- Debug a single test method (quote the `#`):
  - `.codex/skills/debug-surefire/scripts/debug-surefire.sh --test 'MyTest#shouldDoThing'`
- Debug a test in a specific module:
  - `.codex/skills/debug-surefire/scripts/debug-surefire.sh --module core/sail/shacl --test-class ShaclSailTest`
  - `.codex/skills/debug-surefire/scripts/debug-surefire.sh --module rdf4j-sail-shacl --test 'ShaclSailTest#testSomething'`

When the forked JVM is ready, you should see something like:

- `Listening for transport dt_socket at address: 55005`

2) Attach with `jdb` (preferred)

In a second terminal, attach to the printed port:

- Local attach (port only implies localhost):
  - `jdb -attach 55005`
- Explicit host+port:
  - `jdb -attach localhost:55005`

If you need source listing inside jdb, provide a sourcepath up front:

- `jdb -sourcepath module/src/main/java:module/src/test/java -attach 55005`

3) Set breakpoints / catches, then resume

Once you’re at the `jdb` prompt, set a breakpoint (or exception catch) and continue:

- `stop in com.example.MyTest.shouldDoThing`
- `catch uncaught java.lang.AssertionError`
- `cont`

Tip: right after attaching to a just-started suspended JVM, `jdb` may show:
- `No frames on the current call stack`

That’s normal. The JVM hasn’t executed into any Java frames yet. Set breakpoints first, then `cont`.

---

## Notes

- The script runs a fast pre-test install (`-Pquick` into the repo-local `.m2_repo`) and then runs `mvn test` with Surefire in debug mode.
- Use `SUREFIRE_DEBUG_PORT=8000` to change the port (default: `55005`).
- Use `--no-offline` / `--online` if offline (`-o`) resolution fails.
- Everything after `--` is passed to Maven, e.g.:
  - `.codex/skills/debug-surefire/scripts/debug-surefire.sh --test-class MyTest -- -DtrimStackTrace=false -DfailIfNoTests=false -DforkCount=1 -DreuseForks=false`

---

## jdb interaction guide

This section is optimized for quickly getting signal from a failing unit test without drowning in framework/JDK internals.

### Command cheat sheet (the ones you’ll actually use)

**Start / resume**
- `cont` — continue execution from current stop/breakpoint
- `run` — start execution *when jdb launched the VM* (less relevant when you used `-attach`)

**Breakpoints**
- `stop in <class>.<method>` — break on method entry
- `stop at <class>:<line>` — break at a specific line
- `stop` — list all breakpoints
- `clear <class>.<method>` / `clear <class>:<line>` — remove a breakpoint
- `clear` — list breakpoints (same idea as `stop` listing)

**Stepping**
- `next` — step over calls (line-level)
- `step` — step into calls (line-level)
- `step up` — run until current method returns
- `stepi` — step one bytecode instruction (rarely needed)

**Threads / stacks**
- `threads` — list threads
- `thread <id>` — select default thread
- `where` — stack trace for current thread
- `where all` — stack traces for all threads
- `up` / `down` — move the current frame up/down the stack

**Inspect state**
- `locals` — print locals in current frame
- `print <expr>` — evaluate/print an expression (same as `eval`)
- `dump <expr>` — more complete object dump
- `set <lvalue> = <expr>` — mutate a variable/field (use sparingly)

**Source**
- `list` — show source around current line
- `list <line>` or `list <method>` — show a specific region
- `use <path1>:<path2>` (aka `sourcepath`) — set where jdb looks for sources

**Exceptions**
- `catch uncaught <exception>` — break when an uncaught exception occurs
- `catch caught <exception>` — break when a caught exception occurs
- `catch all <exception>` — break for both caught+uncaught
- `ignore ...` — undo a catch

**Reduce noise**
- `exclude <pattern>,<pattern>,...` — don’t report step/method events for matching classes
- `exclude none` — clear exclusions

**Automation**
- `monitor <command>` — run a command every time the program stops (e.g., `monitor where`)
- `read <file>` — execute commands from a file
- `!!` — repeat last command
- `<n> <command>` — repeat command n times (e.g., `10 next`)

### Efficient workflow for failing JUnit tests

#### 1) Add exclusions immediately (so stepping doesn’t become a swamp)

Right after connecting, set exclusions to avoid stepping into JDK/framework code:

- `exclude java.*,javax.*,jdk.*,sun.*,com.sun.*,org.junit.*,org.junit.jupiter.*,org.assertj.*,org.hamcrest.*,org.mockito.*,org.apache.maven.*`

You can always clear this later:

- `exclude none`

#### 2) Break where it matters

Common breakpoint patterns:

- Break at the failing test method:
  - `stop in com.example.MyTest.shouldDoThing`

- Break inside the code under test:
  - `stop in com.example.service.FooService.doWork`

- Break at a specific suspicious line:
  - `stop at com.example.service.FooService:123`

If the method is overloaded, specify argument types:

- `stop in com.example.FooService.doWork(int,java.lang.String)`

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.

#### 3) Break on the *failure*, not just your guess

For unit tests, breaking on the thrown assertion/error is often faster than guessing a line.

Useful catches:

- Classic Java assertions / many test failures:
  - `catch uncaught java.lang.AssertionError`

- Common in JUnit 5 assertions:
  - `catch uncaught org.opentest4j.AssertionFailedError`

- NPE hunting:
  - `catch uncaught java.lang.NullPointerException`

You can remove a catch with `ignore`:

- `ignore uncaught java.lang.AssertionError`

Tip: `catch all java.lang.Throwable` is the nuclear option. It works, but it can get loud.

#### 4) Resume and drive

Once breakpoints/catches are set:

- `cont`

When you hit a breakpoint:

- `where` to see the call stack
- `list` to see nearby source
- `locals` to see local variables
- `print someVar` or `print this.someField` to inspect
- `next` to step over, `step` to step into

#### 5) Find the “real” test thread quickly

Surefire + JUnit can spin up multiple threads (and Maven itself has its own). When in doubt:

- `threads`
- `where all`

Then pick the thread that’s in your test/code-under-test:

- `thread <id>`
- `where`

#### 6) Keep the JVM count predictable

Surefire forking and parallelism can make debugging confusing. If you see multiple JVMs / inconsistent behavior, pass Maven flags to keep it single and fresh:

- `-DforkCount=1 -DreuseForks=false`

Example:

- `.codex/skills/debug-surefire/scripts/debug-surefire.sh --test 'MyTest#shouldDoThing' -- -DforkCount=1 -DreuseForks=false`

#### 7) Use “thread-only” breakpoints when concurrency matters

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:

- `stop thread in com.example.concurrent.Worker.run`

(You can also target a specific thread id with `stop thread <thread_id> in ...` if needed.)

---

## jdb startup customization (optional but powerful)

jdb will execute startup commands from `jdb.ini` or `.jdbrc` in either `user.home` or `user.dir`.

This is handy for keeping your default exclusions/catches consistent, e.g.:

- `exclude java.*,javax.*,jdk.*,sun.*,com.sun.*,org.junit.*,org.assertj.*`
- `catch uncaught java.lang.AssertionError`

You can also keep a project-local command file and load it on demand:

- `read .codex/skills/debug-surefire/jdb.cmds`

---

## Troubleshooting

- **jdb can’t connect**
  - Confirm the port printed by the suspended JVM matches what you used in `jdb -attach`.
  - Confirm you’re attaching to the forked JVM port (the one that prints “Listening for transport…”), not Maven’s PID.
  - If running remotely (CI box / container / VM), ensure the port is reachable or forwarded.

- **Breakpoints don’t hit**
  - Use fully qualified class names.
  - If overloaded, include argument types.
  - Use `classes` to see what’s loaded.
  - Try `stop at <class>:<line>` to avoid signature mismatch.

- **`list` can’t find source**
  - Set sourcepath:
    - `use module/src/main/java:module/src/test/java`
  - Or launch jdb with `-sourcepath ...` from the start.

Install

Requires askill CLI v1.0+

Metadata

LicenseUnknown
Version-
Updated6d ago
Publishereclipse-rdf4j

Tags

ci-cdgithub-actionspromptingtesting