Skip to content
End To End Tester

MSTest

Microsoft's own .NET test framework — where it sits against xUnit and NUnit, what MSTest 3 improved, and when it is the right organisational choice.

1 min read · updated 19 September 2026

MSTest is Microsoft's own test framework, bundled with Visual Studio and supported first-party. It spent years behind xUnit and NUnit on capability; MSTest 3 largely closed that gap.

#The basics

csharp
[TestClass]
public class PricingTests
{
    private Basket _basket = null!;

    // Like NUnit, one instance per test is NOT guaranteed the way xUnit
    // guarantees it — put per-test setup in TestInitialize.
    [TestInitialize]
    public void Setup() => _basket = new Basket();

    [TestCleanup]
    public void Cleanup() { }

    [ClassInitialize]
    public static void OnceForTheClass(TestContext context) { }

    [TestMethod]
    public void An_empty_basket_costs_nothing()
    {
        Assert.AreEqual(0, _basket.TotalCents);
    }

    [TestMethod]
    [DataRow(12_000, 2_400)]
    [DataRow(10_000, 2_000)]
    [DataRow(9_999, 0)]
    public void Discount_applies_at_or_above_the_threshold(int subtotal, int expected)
    {
        var result = Pricing.ApplyDiscount(new Order(subtotal), Policy);

        Assert.AreEqual(expected, result.DiscountCents);
    }

    // DynamicData when the cases are not constants
    public static IEnumerable<object[]> TierCases() =>
    [
        [CustomerTier.Standard, 395],
        [CustomerTier.Gold, 0]
    ];

    [TestMethod]
    [DynamicData(nameof(TierCases), DynamicDataSourceType.Method)]
    public void Shipping_depends_on_tier(CustomerTier tier, int expected) =>
        Assert.AreEqual(expected, Shipping.For(tier).Cents);
}

Note [TestMethod] plus [DataRow] — in older versions a parameterised test needed [DataTestMethod], which still works and is no longer required.

#Parallelism

Off by default. Turn it on at the assembly level:

csharp
// AssemblyInfo.cs
[assembly: Parallelize(Workers = 4, Scope = ExecutionScope.ClassLevel)]

// Opt a class out
[TestClass]
[DoNotParallelize]
public class MigrationTests { }

ExecutionScope.MethodLevel parallelises individual methods and is only safe when the class holds no mutable state — the same caveat as NUnit, for the same reason. See parallel test execution.

#Assertions

csharp
Assert.AreEqual(2_400, result.DiscountCents);
Assert.IsTrue(order.Lines.Any(l => l.Sku == "book-1"));
CollectionAssert.AreEquivalent(expected, actual);
StringAssert.Contains(message, "declined");
Assert.ThrowsException<InsufficientStockException>(() => service.Reserve(order));
await Assert.ThrowsExceptionAsync<TimeoutException>(() => client.FetchAsync());

The built-in set is functional and unlovely. As with xUnit, most teams add Shouldly or FluentAssertions for readable failure messages.

#What MSTest 3 brought

  • A source-generated runner (MSTest.Sdk), so tests can run as a plain executable with no VSTest host — much faster startup, and it works well in containers.
  • Assert.That-style improvements and better async support throughout.
  • Trimming and AOT compatibility, which matters if you test AOT-compiled code.
xml
<!-- The modern minimal project file -->
<Project Sdk="MSTest.Sdk/3.6.0">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
  </PropertyGroup>
</Project>

#Where it fits

MSTest is a reasonable default for a team standardised on the Microsoft toolchain, particularly with Azure DevOps where the reporting integration is seamless. There is no longer a capability argument against it.

For a new project with a free choice, xUnit remains the most common pick in the .NET open-source world, largely for its instance model and the ecosystem that has grown around it. The difference is small enough that team familiarity should decide it.

Common questions

Is MSTest worse than xUnit or NUnit?
No, though it was for a long time. MSTest 3 closed most of the gap — parallelism, better assertions, proper async support and a source-generated runner. The remaining differences are matters of taste more than capability.
Why would I pick MSTest?
Organisational alignment, mostly — first-party support, tight Visual Studio and Azure DevOps integration, and an existing estate. Those are legitimate reasons and worth more than a marginal API preference.
Does MSTest run tests in parallel?
Yes, since MSTest 2, configured with an assembly-level attribute. The default is still sequential, which surprises people migrating from xUnit.

Runnable samples for this page

last test results ↗
  • C#dotnet/Tests.MSTest/tools/mstest

Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.

Was this page useful?