Skip to content
AI & Agents
Skill

/csharp-mstest

Get best practices for MSTest 3.x/4.x unit testing, including modern assertion APIs and data-driven tests

From plugin
awesome-copilot
39k200 skills200 agents
Install
$ npx -y skills add github/awesome-copilot --skill csharp-mstest --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/csharp-mstest

Context preview

The summary Claude sees to decide when to auto-load this skill.

Get best practices for MSTest 3.x/4.x unit testing, including modern assertion APIs and data-driven tests

SKILL.md

csharp-mstest.SKILL.md
name: csharp-mstest
description: 'Get best practices for MSTest 3.x/4.x unit testing, including modern assertion APIs and data-driven tests'

MSTest Best Practices (MSTest 3.x/4.x)

Your goal is to help me write effective unit tests with modern MSTest, using current APIs and best practices.

Project Setup

  • Use a separate test project with naming convention `[ProjectName].Tests`
  • Reference MSTest 3.x+ NuGet packages (includes analyzers)
  • Consider using MSTest.Sdk for simplified project setup
  • Run tests with `dotnet test`

Test Class Structure

  • Use `[TestClass]` attribute for test classes
  • **Seal test classes by default** for performance and design clarity
  • Use `[TestMethod]` for test methods (prefer over `[DataTestMethod]`)
  • Follow Arrange-Act-Assert (AAA) pattern
  • Name tests using pattern `MethodName_Scenario_ExpectedBehavior`
[TestClass]
public sealed class CalculatorTests
{
    [TestMethod]
    public void Add_TwoPositiveNumbers_ReturnsSum()
    {
        // Arrange
        var calculator = new Calculator();

        // Act
        var result = calculator.Add(2, 3);

        // Assert
        Assert.AreEqual(5, result);
    }
}

Test Lifecycle

  • **Prefer constructors over `[TestInitialize]`** - enables `readonly` fields and follows standard C# patterns
  • Use `[TestCleanup]` for cleanup that must run even if test fails
  • Combine constructor with async `[TestInitialize]` when async setup is needed
[TestClass]
public sealed class ServiceTests
{
    private readonly MyService _service;  // readonly enabled by constructor

    public ServiceTests()
    {
        _service = new MyService();
    }

    [TestInitialize]
    public async Task InitAsync()
    {
        // Use for async initialization only
        await _service.WarmupAsync();
    }

    [TestCleanup]
    public void Cleanup() => _service.Reset();
}

Execution Order

1. **Assembly Initialization** - `[AssemblyInitialize]` (once per test assembly) 2. **Class Initialization** - `[ClassInitialize]` (once per test class) 3. **Test Initialization** (for every test method): 1. Constructor 2. Set `TestContext` property 3. `[TestInitialize]` 4. **Test Execution** - test method runs 5. **Test Cleanup** (for every test method): 1. `[TestCleanup]` 2. `DisposeAsync` (if implemented) 3. `Dispose` (if implemented) 6. **Class Cleanup** - `[ClassCleanup]` (once per test class) 7. **Assembly Cleanup** - `[AssemblyCleanup]` (once per test assembly)

Modern Assertion APIs

MSTest provides three assertion classes: `Assert`, `StringAssert`, and `CollectionAssert`.

Assert Class - Core Assertions

// Equality
Assert.AreEqual(expected, actual);
Assert.AreNotEqual(notExpected, actual);
Assert.AreSame(expectedObject, actualObject);      // Reference equality
Assert.AreNotSame(notExpectedObject, actualObject);

// Null checks
Assert.IsNull(value);
Assert.IsNotNull(value);

// Boolean
Assert.IsTrue(condition);
Assert.IsFalse(condition);

// Fail/Inconclusive
Assert.Fail("Test failed due to...");
Assert.Inconclusive("Test cannot be completed because...");

Exception Testing (Prefer over `[ExpectedException]`)

// Assert.Throws - matches TException or derived types
var ex = Assert.Throws<ArgumentException>(() => Method(null));
Assert.AreEqual("Value cannot be null.", ex.Message);

// Assert.ThrowsExactly - matches exact type only
var ex = Assert.ThrowsExactly<InvalidOperationException>(() => Method());

// Async versions
var ex = await Assert.ThrowsAsync<HttpRequestException>(async () => await client.GetAsync(url));
var ex = await Assert.ThrowsExactlyAsync<InvalidOperationException>(async () => await Method());

Collection Assertions (Assert class)

Assert.Contains(expectedItem, collection);
Assert.DoesNotContain(unexpectedItem, collection);
Assert.ContainsSingle(collection);  // exactly one element
Assert.HasCount(5, collection);
Assert.IsEmpty(collection);
Assert.IsNotEmpty(collection);

String Assertions (Assert class)

Assert.Contains("expected", actualString);
Assert.StartsWith("prefix", actualString);
Assert.EndsWith("suffix", actualString);
Assert.DoesNotStartWith("prefix", actualString);
Assert.DoesNotEndWith("suffix", actualString);
Assert.MatchesRegex(@"\d{3}-\d{4}", phoneNumber);
Assert.DoesNotMatchRegex(@"\d+", textOnly);

Comparison Assertions

Assert.IsGreaterThan(lowerBound, actual);
Assert.IsGreaterThanOrEqualTo(lowerBound, actual);
Assert.IsLessThan(upperBound, actual);
Assert.IsLessThanOrEqualTo(upperBound, actual);
Assert.IsInRange(actual, low, high);
Assert.IsPositive(number);
Assert.IsNegative(number);

Type Assertions

// MSTest 3.x - uses out parameter
Assert.IsInstanceOfType<MyClass>(obj, out var typed);
typed.DoSomething();

// MSTest 4.x - returns typed result directly
var typed = Assert.IsInstanceOfType<MyClass>(obj);
typed.DoSomething();

Assert.IsNotInstanceOfType<WrongType>(obj);

Assert.That (MSTest 4.0+)

Assert.That(result.Count > 0);  // Auto-captures expression in failure message

StringAssert Class

> **Note:** Prefer `Assert` class equivalents when available (e.g., `Assert.Contains("expected", actual)` over `StringAssert.Contains(actual, "expected")`).

StringAssert.Contains(actualString, "expected");
StringAssert.StartsWith(actualString, "prefix");
StringAssert.EndsWith(actualString, "suffix");
StringAssert.Matches(actualString, new Regex(@"\d{3}-\d{4}"));
StringAssert.DoesNotMatch(actualString, new Regex(@"\d+"));

CollectionAssert Class

> **Note:** Prefer `Assert` class equivalents when available (e.g., `Assert.Contains`).

// Containment
CollectionAssert.Contains(collection, expectedItem);
CollectionAssert.DoesNotContain(collection, unexpectedItem);

// Equality (same elements, same order)
CollectionAssert.AreEqual(expectedCollection, actualCollection);
CollectionAssert.AreNotE
Read more
Ships withawesome-copilot

A community-created collection of custom agents, instructions, skills, hooks, workflows, and plugins to supercharge your GitHub Copilot experience.

Get the whole plugin

Other skills on awesome-copilot.