When a test process dies, a red CI job is not enough. I need to know what completed and what was running when the host disappeared. Microsoft.Testing.Platform crash-resilient TRX gives me the first answer by writing completed results during the run instead of waiting for a clean shutdown. Its crash-sequence artifact helps with the second by naming completed and in-flight tests.
That turns an opaque infrastructure failure into evidence I can archive and inspect. The runnable sample for this article creates a controlled host crash, then proves that the partial TRX is still valid and that the sequence log points to the interrupted test.
Why Microsoft.Testing.Platform crash-resilient TRX matters
Starting with Microsoft.Testing.Platform (MTP) 2.3, the TRX reporter streams results while tests execute. If the host terminates abruptly, results already written remain available in a valid partial report. Microsoft documents the behavior in its MTP test-reporting guide.
That distinction matters. A report written only after a clean run can disappear with the process. A streamed report cannot recover a result that never finished, but it can preserve the work that did.
I pair TRX with two stable MTP crash-diagnostics options:
--crashdump --crashdump-type Mini --crash-sequence on
The dump can contain process state for deeper debugging. The sequence log is much smaller and records test progress, so it can identify the test that was in flight. Microsoft’s crash and hang dump documentation explains the switches and platform constraints.
These artifacts answer different questions: TRX tells me what completed, the sequence narrows down where execution stopped, and the dump may help explain why.
Configure the stable evidence path
The sample targets .NET 10 and uses MSTest.Sdk 4.3.3. Its resolved MTP reporting and crash-dump packages are version 2.3.3. I enable the crash-dump extension in the project; the default MSTest profile supplies TRX reporting:
<Project Sdk="MSTest.Sdk/4.3.3">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<EnableMicrosoftTestingExtensionsCodeCoverage>false</EnableMicrosoftTestingExtensionsCodeCoverage>
<EnableMicrosoftTestingExtensionsCrashDump>true</EnableMicrosoftTestingExtensionsCrashDump>
</PropertyGroup>
</Project>
The MSTest SDK documentation describes the runner profile and extension properties. I stay with TRX here: the JUnit, CTRF, HTML, and GitHub reporters are documented as experimental, so I would not present them as the stable path.
The three-test fixture is deliberately ordered for a deterministic demonstration. Test A passes, test B terminates the host only when an environment variable is present, and test C should never start in the crash run:
[TestMethod]
public void B_CrashHostOnlyWhenRequested()
{
var requested = Environment.GetEnvironmentVariable("DEMO_CRASH");
if (StringComparer.Ordinal.Equals(requested, "1"))
{
Environment.FailFast("Intentional crash for the MTP demonstration.");
}
Assert.IsNull(requested);
}
The guard is essential. I do not set DEMO_CRASH globally, and I would never include an unguarded FailFast test in a normal suite. The verification script scopes the variable to the child process and restores the old value afterward.
Verify the partial TRX and crash sequence
The complete project and deterministic verifier are in sample 025’s draft pull request. After restore and build, I run:
powershell -NoProfile -ExecutionPolicy Bypass -File .\verify.ps1
The script first runs all three tests normally and parses normal.trx. It then launches the guarded crash with:
--report-trx --report-trx-filename crash.trx
--crashdump --crashdump-type Mini --crash-sequence on
A nonzero exit is expected. The important assertions happen afterward: crash.trx must be parseable, it must contain the passed result for test A, it must not contain test C, and the sequence text must name test B.
My validation produced three passing results in the normal run. The controlled run exited with code 7, retained test A in the streamed TRX, omitted test C, and identified test B in the crash sequence. That is useful evidence even without a dump.
In fact, the current Windows validation host did not produce the requested dump because its native createdump tooling rejected the PID argument. The verifier reports that as information instead of pretending every host can create a dump. I would archive crash.console.log with the TRX and sequence so that tooling failures remain visible too.
In CI, make evidence upload run even when the test command fails. Otherwise, the expected nonzero crash exit can prevent the diagnostic step that makes the run useful.
Limitations and when not to use it
This pattern is for abrupt test-host termination: native crashes, fail-fast paths, stack overflows, and similar failures. It is unnecessary for an ordinary failed assertion because a normal TRX already records that failure.
A partial TRX contains only completed results. A sequence log identifies the likely in-flight test; it does not prove root cause. A deterministic method order makes this small reproduction easy to verify, but production tests should not depend on ordering as application behavior.
Crash dumps are platform- and permission-sensitive, can be large, and may contain secrets or customer data from process memory. Store them with restricted access and short retention. The MTP dump mechanism requires .NET 6 or later and does not support .NET Framework; the separate --crash-report option is limited to Linux and macOS.
For a different kind of test-suite stall, I use virtual time instead of waiting on the wall clock; my earlier article, The Test That Slept and the Test That Never Woke Up, covers that path.
What evidence does your CI preserve when the test host vanishes before the run finishes?
Happy testing!
Top comments (0)