github/awesome-copilot1 file

Csharp Mstest

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

Specification
Skill ID
github/awesome-copilot/csharp-mstest
Publisher
github
Repository
awesome-copilot
Installs
0
Files
1
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

github/awesome-copilot/csharp-mstestInstalls these files
  • SKILL.md

What this skill tells the agent

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):
  4. Constructor
  5. Set TestContext property
  6. [TestInitialize]
  7. Test Execution - test method runs
  8. Test Cleanup (for every test method):
  9. [TestCleanup]
  10. DisposeAsync (if implemented)
  11. Dispose (if implemented)
  12. Class Cleanup - [ClassCleanup] (once per test class)
  13. 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.AreNotEqual(unexpectedCollection, actualCollection);

// Equivalence (same elements, any order)
CollectionAssert.AreEquivalent(expectedCollection, actualCollection);
CollectionAssert.AreNotEquivalent(unexpectedCollection, actualCollection);

// Subset checks
CollectionAssert.IsSubsetOf(subset, superset);
CollectionAssert.IsNotSubsetOf(notSubset, collection);

// Element validation
CollectionAssert.AllItemsAreInstancesOfType(collection, typeof(MyClass));
CollectionAssert.AllItemsAreNotNull(collection);
CollectionAssert.AllItemsAreUnique(collection);

Data-Driven Tests

DataRow

[TestMethod]
[DataRow(1, 2, 3)]
[DataRow(0, 0, 0, DisplayName = "Zeros")]
[DataRow(-1, 1, 0, IgnoreMessage = "Known issue #123")]  // MSTest 3.8+
public void Add_ReturnsSum(int a, int b, int expected)
{
    Assert.AreEqual(expected, Calculator.Add(a, b));
}

DynamicData

The data source can return any of the following types:

  • IEnumerable<(T1, T2, ...)> (ValueTuple) - preferred, provides type safety (MSTest 3.7+)
  • IEnumerable<Tuple<T1, T2, ...>> - provides type safety
  • IEnumerable<TestDataRow> - provides type safety plus control over test metadata (display name, categories)
  • IEnumerable<object[]> - least preferred, no type safety
Note: When creating new test data methods, prefer ValueTuple or TestDataRow over IEnumerable<object[]>. The object[] approach provides no compile-time type checking and can lead to runtime errors from type mismatches.
[TestMethod]
[DynamicData(nameof(TestData))]
public void DynamicTest(int a, int b, int expected)
{
    Assert.AreEqual(expected, Calculator.Add(a, b));
}

// ValueTuple - preferred (MSTest 3.7+)
public static IEnumerable<(int a, int b, int expected)> TestData =>
[
    (1, 2, 3),
    (0, 0, 0),
];

// TestDataRow - when you need custom display names or metadata
public static IEnumerable<TestDataRow<(int a, int b, int expected)>> TestDataWithMetadata =>
[
    new((1, 2, 3)) { DisplayName = "Positive numbers" },
    new((0, 0, 0)) { DisplayName = "Zeros" },

Use this skill in a project

Open any RiverX project, search the Skills panel for github/awesome-copilot/csharp-mstest, and install it in one click.

Use this skill