mcpField
D
dotnet-test-frameworks

dotnet-test-frameworks

github.com/dotnetTesting & quality5.8k460 installsUpdated 1d ago
Repository

About dotnet-test-frameworks

.NET test framework reference for developers and code-review tools identifying tests and using framework-specific conventions. It covers MSTest, xUnit, NUnit, and TUnit, including assertions, lifecycle hooks, and integration-test markers.

Key features

  1. 01Identify test classes and methods
  2. 02Compare assertion APIs across four frameworks
  3. 03Recognize skip and ignore annotations
  4. 04Identify integration tests and external dependencies
  5. 05Reference setup and teardown conventions

Use cases

  1. 01Classify tests in a .NET codebase
  2. 02Suggest framework-native exception assertions
  3. 03Review tests for external dependencies
  4. 04Recognize test lifecycle and skip behavior

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/dotnet/skills --skill dotnet-test-frameworks

After installing, type/dotnet-test-frameworksin Claude Code to confirm it loaded.

Details

From the project README

.NET Test Framework Reference

Language-specific detection patterns for .NET test frameworks (MSTest, xUnit, NUnit, TUnit).

Test File Identification

FrameworkTest class markersTest method markers
MSTest[TestClass][TestMethod], [DataTestMethod]
xUnit(none — convention-based)[Fact], [Theory]
NUnit[TestFixture][Test], [TestCase], [TestCaseSource]
TUnit(none — convention-based)[Test]

Assertion APIs by Framework

CategoryMSTestxUnitNUnitTUnit
EqualityAssert.AreEqualAssert.EqualAssert.That(x, Is.EqualTo(y))await Assert.That(x).IsEqualTo(y)
BooleanAssert.IsTrue / Assert.IsFalseAssert.True / Assert.FalseAssert.That(x, Is.True)await Assert.That(x).IsTrue() / await Assert.That(x).IsFalse()
NullAssert.IsNull / Assert.IsNotNullAssert.Null / Assert.NotNullAssert.That(x, Is.Null)await Assert.That(x).IsNull() / await Assert.That(x).IsNotNull()
ExceptionAssert.Throws<T>() / Assert.ThrowsExactly<T>()Assert.Throws<T>()Assert.That(() => ..., Throws.TypeOf<T>())await Assert.That(() => ...).Throws<T>() / await Assert.That(() => ...).ThrowsExactly<T>()
CollectionCollectionAssert.ContainsAssert.ContainsAssert.That(col, Has.Member(x))await Assert.That(col).Contains(x)
StringStringAssert.ContainsAssert.Contains(str, sub)Assert.That(str, Does.Contain(sub))await Assert.That(str).Contains(sub)
TypeAssert.IsInstanceOfTypeAssert.IsAssignableFromAssert.That(x, Is.InstanceOf<T>())await Assert.That(x).IsAssignableTo<T>() (use await Assert.That(x).IsTypeOf<T>() for exact-type check)
InconclusiveAssert.Inconclusive()skip via [Fact(Skip)]Assert.Inconclusive()Skip.Test("reason") (no true inconclusive state)
FailAssert.Fail()Assert.Fail() (.NET 10+)Assert.Fail()Assert.Fail()

TUnit-specific: assertions are async and must be awaited — a forgotten await causes the assertion to never run, and the test passes silently. A built-in analyzer warns when await is missing. Multiple assertions can be combined with .And / .Or chaining or grouped via Assert.Multiple().

Third-party assertion libraries: Should* (Shouldly), .Should() (FluentAssertions / AwesomeAssertions), Verify() (Verify). TUnit also ships an optional TUnit.Assertions.Should package providing FluentAssertions-style value.Should().BeEqualTo(...) on top of the same infrastructure.

Sleep/Delay Patterns

PatternExample
Thread sleepThread.Sleep(2000)
Task delayawait Task.Delay(1000)
SpinWaitSpinWait.SpinUntil(() => condition, timeout)

Skip/Ignore Annotations

FrameworkAnnotationWith reason
MSTest[Ignore][Ignore("reason")]
xUnit[Fact(Skip = "reason")](reason is required)
NUnit[Ignore("reason")](reason is required)
TUnit[Skip("reason")](reason is required; also valid at class and assembly scope, e.g. [assembly: Skip("…")]. Dynamic in-test skipping via Skip.Test("reason").)
Conditional#if false / #if NEVER(no reason possible)

Exception Handling — Idiomatic Alternatives

When a test uses try/catch to verify exceptions, suggest the framework-native alternative:

MSTest:

// Instead of try/catch (matches exact type):
var ex = Assert.ThrowsExactly<InvalidOperationException>(
    () => processor.ProcessOrder(emptyOrder));
Assert.AreEqual("Order must contain at least one item", ex.Message);

// Or (also matches derived types):
var ex = Assert.Throws<InvalidOperationException>(
    () => processor.ProcessOrder(emptyOrder));
Assert.AreEqual("Order must contain at least one item", ex.Message);

xUnit:

var ex = Assert.Throws<InvalidOperationException>(
    () => processor.ProcessOrder(emptyOrder));
Assert.Equal("Order must contain at least one item", ex.Message);

NUnit:

var ex = Assert.Throws<InvalidOperationException>(
    () => processor.ProcessOrder(emptyOrder));
Assert.That(ex.Message, Is.EqualTo("Order must contain at least one item"));

TUnit:

await Assert.That(() => processor.ProcessOrder(emptyOrder))
    .Throws<InvalidOperationException>()
    .WithMessage("Order must contain at least one item");

// Or, for exact-type matching (no derived types):
await Assert.That(() => processor.ProcessOrder(emptyOrder))
    .ThrowsExactly<InvalidOperationException>();

Mystery Guest — Common .NET Patterns

Smell indicatorWhat to look for
File systemFile.ReadAllText, File.Exists, File.WriteAllBytes, Directory.GetFiles, Path.Combine with hard-coded paths
DatabaseSqlConnection, DbContext (without in-memory provider), SqlCommand
NetworkHttpClient without HttpMessageHandler override, WebRequest, TcpClient
EnvironmentEnvironment.GetEnvironmentVariable, Environment.CurrentDirectory
AcceptableMemoryStream, StringReader, InMemory database providers, custom DelegatingHandler

Integration Test Markers

Recognize these as integration tests (adjust smell severity accordingly):

  • Class name contains Integration, E2E, EndToEnd, or Acceptance
  • [TestCategory("Integration")] (MSTest)
  • [Trait("Category", "Integration")] (xUnit)
  • [Category("Integration")] (NUnit, TUnit)
  • Project name ending in .IntegrationTests or .E2ETests

Setup/Teardown Methods

FrameworkSetupTeardown
MSTest[TestInitialize] or constructor[TestCleanup] or IDisposable.Dispose / IAsyncDisposable.DisposeAsync
xUnitconstructorIDisposable.Dispose / IAsyncDisposable.DisposeAsync
NUnit[SetUp][TearDown]
TUnit[Before(Test)] or constructor[After(Test)] or IDisposable.Dispose / IAsyncDisposable.DisposeAsync
MSTest (class)[ClassInitialize][ClassCleanup]
NUnit (class)[OneTimeSetUp][OneTimeTearDown]
xUnit (class)IClassFixture<T>fixture's Dispose
TUnit (class)[Before(Class)][After(Class)]
TUnit (assembly)[Before(Assembly)][After(Assembly)]
TUnit (session)[Before(TestSession)][After(TestSession)]

TUnit-specific: [BeforeEvery(Test)] / [AfterEvery(Test)] (and the Class / Assembly variants) run for every test/class/assembly across the whole test run — useful for global cross-cutting hooks. Hooks may optionally accept a context object (TestContext, ClassHookContext, etc.) and/or a CancellationToken.

Comments

Markdown supported