Skip to content

Breakpoints not hit in .NET test code - debugger attaches to parent dotnet.exe, not testhost child process #83

@mikejohnstonPremierinc

Description

@mikejohnstonPremierinc

Description

When debugging .NET test projects via dotnet test --filter, the debugger attaches to the parent dotnet.exe process but does NOT propagate to the child testhost.dll process where test code actually executes. Breakpoints set in test files are never hit, even though the test runs and passes.

.NET Test Platform Architecture

[Debugger attaches here] dotnet.exe (parent - orchestrates test discovery and execution)
                              |  spawns
                              v
                         testhost.dll (child process - loads and RUNS actual test code)
                              |  loads
                              v
                         YourTests.dll (test assembly with [Test] methods)

The dotnet test command is a test runner orchestrator. It does NOT execute test code directly. It spawns testhost.dll as a separate child process, which loads the test assembly and invokes test methods. If the debugger only attaches to the parent dotnet.exe, breakpoints in test code (which runs in testhost.dll) are never hit.

Steps to Reproduce

  1. Create a .vscode/launch.json with:
{
    "name": "Debug Test",
    "type": "coreclr",
    "request": "launch",
    "program": "dotnet",
    "args": ["test", "MyApp.UITest/MyApp.UITest.csproj", "--filter", "Name=MyTest", "--no-build"],
    "cwd": "${workspaceFolder}",
    "console": "integratedTerminal"
}
  1. Set a breakpoint inside MyTest() method
  2. Call start_debugging with configurationName: "Debug Test"
  3. Observe: test executes and passes, but breakpoint is never hit

Expected Behavior

Breakpoints in test code should be hit when debugging via dotnet test.

How VS Code C# Dev Kit Solves This

VS Code's "Debug Test" CodeLens (provided by C# Dev Kit) successfully hits breakpoints in test code. It likely uses one of these approaches:

  1. Sets VSTEST_HOST_DEBUG=1 environment variable, which causes testhost to pause on startup and print its PID, then attaches a second debugger to the child process
  2. Uses vstest's --Diag or --TestAdapterPath mechanisms to inject debug settings
  3. Communicates directly with the .NET test platform via its internal APIs to set up child process debugging

Proposed Fix Options

Option A: Use VSTEST_HOST_DEBUG=1 (simplest)

  • Add "env": { "VSTEST_HOST_DEBUG": "1" } to the generated coreclr test config
  • When testhost starts, it prints: "Host debugging is enabled. Please attach debugger to testhost process to continue."
  • DebugMCP would need to detect this message in the debug console output, extract the testhost PID, and automatically attach a second debug session to it
  • Complexity: Requires monitoring debug console output and launching a secondary attach session

Option B: Use --TestAdapterPath with debug configuration

  • Pass --TestAdapterPath pointing to a debug-enabled test adapter
  • Complexity: Requires bundling or locating the correct adapter

Option C: Mirror C# Dev Kit's approach

  • Investigate how the "Debug Test" CodeLens achieves child-process debugging
  • Replicate that approach in createTestDebugConfig

Relationship to Issue #80

Issue #80 is a prerequisite - it must be fixed first so that createTestDebugConfig is actually called for coreclr. Once #80 is fixed and the correct dotnet test --filter config IS generated, THIS issue becomes the next blocker: the config will launch dotnet test successfully, but breakpoints won't hit because of the child process architecture.

The two issues together form the complete fix for .NET test debugging:

  1. start_debugging with testName parameter produces broken config for C#/.NET (coreclr) — createTestDebugConfig is unreachable dead code #80: Generate the correct config (currently dead code)
  2. This issue: Ensure breakpoints hit in the child testhost process

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions