Skip to content

feat: add TestContext.Isolation interface for test resource isolation#4801

Merged
thomhurst merged 3 commits intomainfrom
feat/test-isolation-interface
Feb 15, 2026
Merged

feat: add TestContext.Isolation interface for test resource isolation#4801
thomhurst merged 3 commits intomainfrom
feat/test-isolation-interface

Conversation

@thomhurst
Copy link
Owner

Summary

  • Adds a new ITestIsolation interface on TestContext with UniqueId, GetIsolatedName(), and GetIsolatedPrefix() helpers
  • Extracts isolation functionality from WebApplicationTest so any test can create unique resource names (database tables, queue topics, cache keys) without requiring a specific base class
  • WebApplicationTest now shares the same atomic counter, ensuring unique IDs across all test types

Changes

Action File
Create TUnit.Core/Interfaces/ITestIsolation.cs
Create TUnit.Core/TestContext.Isolation.cs
Modify TUnit.Core/TestContext.cs
Modify TUnit.AspNetCore/WebApplicationTest.cs
Create TUnit.UnitTests/TestIsolationTests.cs
Modify docs/docs/test-lifecycle/test-context.md
Modify docs/docs/examples/aspnet.md

Usage

// Available in any test - no base class required
var tableName = TestContext.Current!.Isolation.GetIsolatedName("todos");  // "Test_42_todos"
var prefix = TestContext.Current!.Isolation.GetIsolatedPrefix(".");       // "test.42."

Test plan

  • dotnet build succeeds (TUnit.Core + TUnit.AspNetCore)
  • 10/10 new unit tests pass (TestIsolationTests)
  • CI pipeline passes
  • Verify WebApplicationTest existing tests still pass

🤖 Generated with Claude Code

Extract isolation helpers (UniqueId, GetIsolatedName, GetIsolatedPrefix) from
WebApplicationTest into a new ITestIsolation interface on TestContext, making
them available to all tests without requiring a specific base class.

WebApplicationTest now shares the same atomic counter as TestContext to ensure
unique IDs across all test types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/// that need to be unique across parallel test execution.
/// Accessed via <see cref="TestContext.Isolation"/>.
/// </summary>
public interface ITestIsolation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Snapshot Test Updates

This PR introduces public API changes that require updating snapshot tests per CLAUDE.md Rule 2:

Snapshot Testing - Changes to source generator output or public APIs require running snapshot tests. Commit .verified.txt files. NEVER commit .received.txt.

Public API Changes Made

  1. New public interface: ITestIsolation (this file)
  2. Modified TestContext: Now implements ITestIsolation and exposes public ITestIsolation Isolation => this;

Required Action

Run the public API snapshot tests and commit the updated .verified.txt files:

dotnet test TUnit.PublicAPI
# Review the changes in *.verified.txt files
git add TUnit.PublicAPI/**/*.verified.txt

See mandatory-rules.md for details on when snapshot tests are required.

Add ITestIsolation interface and TestContext.Isolation property to all
TFM snapshot files (net8.0, net9.0, net10.0, net4.7).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link
Contributor

claude bot commented Feb 14, 2026

Code Review

I found an architectural issue with the dual-counter implementation that causes inconsistent behavior for users.

Issue: Inconsistent UniqueId Values

Problem: For tests inheriting from WebApplicationTest, the UniqueId property returns different values depending on whether accessed via this.UniqueId or TestContext.Current!.Isolation.UniqueId.

Root Cause: Both constructors independently increment the shared counter at different lifecycle stages:

Impact: This causes GetIsolatedName() and GetIsolatedPrefix() to produce different resource names for the same test depending on the access path:

// In a test inheriting from WebApplicationTest:
this.GetIsolatedName("users");                              // Returns "Test_5_users"
TestContext.Current!.Isolation.GetIsolatedName("users");   // Returns "Test_1_users"

This defeats the purpose of test isolation — a user mixing both APIs in the same test will get non-matching resource names.


Suggested Fix: Reuse TestContext's ID

Instead of generating a separate ID, have WebApplicationTest delegate to the existing TestContext.Isolation.UniqueId:

internal WebApplicationTest()
{
    UniqueId = TestContext.Current!.Isolation.UniqueId;
}

Why this is better:

  • Consistency: Both access paths return the same ID for the same test
  • Single source of truth: Only TestContext generates IDs
  • Simpler architecture: No dual increment logic to reason about
  • Better abstraction: WebApplicationTest becomes a thin wrapper around TestContext.Isolation

This ensures that whether a user calls this.GetIsolatedName("foo") or TestContext.Current!.Isolation.GetIsolatedName("foo"), they get the same resource name.


Additional Consideration: Make Helper Methods Wrappers

For maximum clarity and maintainability, consider having WebApplicationTest's helper methods delegate to TestContext.Isolation as well:

protected string GetIsolatedName(string baseName) 
    => TestContext.Current!.Isolation.GetIsolatedName(baseName);

protected string GetIsolatedPrefix(string separator = "_") 
    => TestContext.Current!.Isolation.GetIsolatedPrefix(separator);

This makes WebApplicationTest a pure convenience wrapper rather than a parallel implementation, reducing duplication and preventing future drift.


CLAUDE.md Compliance: ✅ No violations found. Checked snapshot tests, AOT compatibility, performance, and dual-mode requirements.

Address code review feedback: WebApplicationTest now delegates UniqueId,
GetIsolatedName, and GetIsolatedPrefix to TestContext.Current!.Isolation
instead of independently incrementing the shared counter. This ensures
consistent values regardless of the access path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@thomhurst thomhurst merged commit 75c3f66 into main Feb 15, 2026
12 of 14 checks passed
@thomhurst thomhurst deleted the feat/test-isolation-interface branch February 15, 2026 00:16
This was referenced Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant