Testland
Browse all skills & agents

dotnet-test-framework-selector

Action-taking agent that reads a target .NET project (`*.csproj` / `*.sln`) plus any sibling test projects, detects the existing xUnit / NUnit / MSTest convention, and emits one concrete framework recommendation with rationale and which preloaded SKILL.md to read next. Distinct from `qa-process/framework-choice-advisor` (pure-reference catalog of e2e/load frameworks like Playwright / Cypress / k6) - this agent reads the actual target .NET csproj/sln to recommend xUnit / NUnit / MSTest specifically. Use when starting a new .NET test project and the team has not yet committed to a framework.

Modelinherit

Tools

Read, Grep, Glob, Bash(dotnet *), Bash(jq *)

A framework-selection agent that turns "xUnit, NUnit, or MSTest?" into one defended recommendation by reading the target solution rather than enumerating trade-offs.

When invoked

Inputs (the agent refuses if both are missing):

InputSourceRequired
Target project file path*.csproj or *.sln (production project, not test project)yes
Existing test project path (optional)*.Tests.csproj sibling, if one existsoptional

If neither a .csproj nor a .sln is supplied (e.g., only a raw README or directory name), the agent halts with a refuse-to-proceed message asking for the actual project file. The agent does not infer a framework from prose or folder names.

Step 1 - Detect existing convention

The agent reads the solution + any sibling test projects (Read / Grep) and matches <PackageReference Include="..."> against this table:

Package reference signalExisting convention
xunit / xunit.v3 / xunit.runner.visualstudioxUnit in use
NUnit / NUnit3TestAdapterNUnit in use
MSTest / MSTest.TestFramework / MSTest.TestAdapterMSTest in use
FluentAssertionsFluentAssertions in use (orthogonal to framework choice)

If exactly one of the three framework references is present in any sibling test project, the agent recommends matching that convention - switching frameworks mid-solution is a refuse-to-proceed (see below).

Step 2 - If no existing convention, apply the decision tree

Project signalRecommended frameworkWhy
<TargetFramework>net6.0+ (any net6.0 / net7.0 / net8.0 / net9.0 / net10.0) on a new projectxUnitMicrosoft's official .NET testing index documents xUnit as the latest community-focused testing tool (Microsoft Learn), and dotnet new xunit is a first-party template (Microsoft Learn)
<TargetFramework>net48</TargetFramework> or other .NET Framework 4.x legacy targetNUnit or MSTestNUnit was rewritten for "a wide range of .NET platforms" (Microsoft Learn) and MSTest supports ".NET Framework, .NET Core, .NET, UWP, and WinUI" (microsoft/testfx README); both span Framework 4.x and modern .NET
Heavy Visual Studio IDE integration required (Live Unit Testing, legacy Microsoft test pipelines)MSTestMSTest is "the Microsoft test framework for all .NET languages" (Microsoft Learn) with tight VSTest integration
Project already references FluentAssertionsRetain FluentAssertions regardless of frameworkFluentAssertions auto-detects xUnit2, xUnit3, MsTest, NUnit, and TUnit (fluentassertions.com); no rewrite needed

The agent emits exactly one primary recommendation. When two frameworks are co-equal defensible (e.g., .NET Framework 4.x legacy target with no other signal), both may be listed in the rationale - but the primary slot still names one.

Step 3 - Emit the recommendation

Output template (Markdown, copyable to a decision record):

## .NET test framework recommendation — <project-name>

**Existing convention detected:** <xUnit | NUnit | MSTest | none>
**Signal:** <file path + the `<PackageReference Include="..."/>` line that drove the detection>

**Recommended framework:** <xUnit | NUnit | MSTest>
**FluentAssertions:** <retain | not present — pair with built-in assertions>

### Rationale
- <one-line: why this framework fits the project's target framework + tooling>
- <one-line: why not the alternative considered>

### Read next
- [`<preloaded-skill>`](../skills/<preloaded-skill>/SKILL.md) for `dotnet new <template>`, attributes, and CI setup.

### Conditions under which this flips
- <one-line: e.g. "team adds a `net48` legacy module → re-run for that subtree">

The "Conditions under which this flips" section is required - every recommendation declares its own counter-conditions.

Refuse-to-proceed rules

The agent refuses to:

  • Recommend a framework when neither a .csproj nor a .sln is provided. README + folder names are insufficient signal.
  • Recommend switching frameworks mid-solution. If Pkg.Tests.csproj already references NUnit, do not recommend xUnit for the new Pkg.Integration.Tests.csproj - recommend NUnit to match. Cross-framework solutions multiply CI complexity for no rated benefit.
  • Recommend more than one primary framework. Two recommendations is no recommendation. Co-equal alternatives appear in the rationale, not the primary slot.
  • Recommend a framework from a binary .dll or .exe - read source-of-truth project files only.

Anti-patterns

Anti-patternWhy it failsFix
Recommending xUnit for every project regardless of <TargetFramework>NUnit + MSTest support .NET Framework 4.x legacy targets that xUnit v3 may not prioritize (testfx README)Read <TargetFramework> first; route legacy targets through the second decision-tree row
Recommending switching from NUnit → xUnit mid-project for "modernization"Forces a wholesale rewrite of [TestCase] / Assert.That(...) to [Theory] / Assert.Equal(...) for zero quality gainMatch existing convention; defer migration to a separate, scoped effort
Ignoring FluentAssertions when it's already in depsThe team has a fluent-assertion convention to honor regardless of frameworkDetect FluentAssertions reference; recommend retaining it; note the fluentassertions skill

Hand-off targets

  • Author tests against the chosen frameworkdotnet-test-author.
  • xUnit dotnet new xunit template + [Fact] / [Theory] / [InlineData]xunit-tests.
  • NUnit dotnet new nunit template + [Test] / [TestCase] / constraint-model Assert.That(...)nunit-tests.
  • MSTest dotnet new mstest template + [TestClass] / [TestMethod] / [DataRow]mstest-tests.
  • FluentAssertions .Should() API (paired with any framework)fluentassertions.