Skip to content

[PROTOTYPE] Add Microsoft.NET.Sdk.Testing skeleton (#49294)#54677

Draft
Evangelink wants to merge 4 commits into
dotnet:mainfrom
Evangelink:dev/amauryleve/sdk-testing-prototype
Draft

[PROTOTYPE] Add Microsoft.NET.Sdk.Testing skeleton (#49294)#54677
Evangelink wants to merge 4 commits into
dotnet:mainfrom
Evangelink:dev/amauryleve/sdk-testing-prototype

Conversation

@Evangelink

@Evangelink Evangelink commented Jun 10, 2026

Copy link
Copy Markdown
Member

Warning

Draft / prototype, not for merge. Opening this so the shape of #49294 can be discussed against concrete code rather than just a design doc. The MTP path for xUnit/NUnit/TUnit is the only one anywhere close to validated; MSTest support is naive (a real impl should delegate to MSTest.Sdk); no template integration; no test asset yet; strings are inline (no .resx/.xlf yet).

What this is

Prototype of Microsoft.NET.Sdk.Testing, the proposal in #49294, as an in-tree MSBuild SDK shipped in-band with the .NET SDK. Layout mirrors src/WebSdk/Worker/ so MSBuild''s SDK resolver picks it up the same way the existing per-area SDKs (Microsoft.NET.Sdk.Worker, Microsoft.NET.Sdk.Razor, etc.) are.

Design decisions reflected in this prototype

  1. Framework + platform are two properties, platform defaults from framework. Valid combos enforced at evaluation:

    Framework MTP VSTest Default when omitted
    MSTest MTP
    NUnit MTP
    XUnit (v3) MTP
    TUnit MTP
    Expecto MTP
  2. Frameworks come from NuGet — nothing framework-specific is shipped in-band. Avoids the maintenance/CVE-response burden of in-banding code the SDK team doesn''t own. Every <Framework>Version (and companion package version) has a pinned default in Targets/_DefaultPackageVersions.props so projects don''t need to specify a version. Users can override with a fixed version (recommended for reproducibility) or with * / *-* to opt into NuGet floating. The defaults shipped here are placeholders for the prototype; in the real SDK they would be sourced from eng/Versions.props at build time so the matrix advances on each .NET SDK release.

  3. MTP extensions surface mirrors MSTest.Sdk (TestingExtensionsProfile + per-extension Enable<Name> and <Name>Version), applied uniformly across frameworks because the extensions hook MTP, not the framework.

  4. <Framework>PackageSource local-path override (Arcade-style) is not in this prototype; deferred to v2 per the design discussion.

  5. IsTestProject semantic conflict is handled additively: IsTestProject stays VSTest-owned (set by Microsoft.NET.Test.Sdk package), IsTestUtilityProject is the new evaluation-time signal. Zero regression for _CalculateIsVSTestTestProject, IsRidAgnostic, and Arcade.

Layout

src/TestingSdk/
├── README.md
├── Sdk/
│   ├── Sdk.props                                    # MSBuild SDK entry
│   └── Sdk.targets
├── Targets/
│   ├── Microsoft.NET.Sdk.Testing.props              # Dispatch + validation
│   ├── Microsoft.NET.Sdk.Testing.targets
│   ├── Frameworks/
│   │   ├── MSTest.{props,targets}
│   │   ├── NUnit.{props,targets}
│   │   ├── XUnit.{props,targets}
│   │   ├── TUnit.{props,targets}
│   │   └── Expecto.{props,targets}
│   └── Platforms/
│       ├── MicrosoftTestingPlatform.{props,targets}
│       └── VSTest.{props,targets}
└── Tasks/
    └── Microsoft.NET.Sdk.Testing.Tasks.csproj       # Packaging only

Plus wiring in:

  • src/Layout/redist/targets/GenerateLayout.targetsPublishNetSdks builds the new csproj and lays out Sdks/Microsoft.NET.Sdk.Testing/.
  • sdk.slnx, source-build.slnf.

Sample consuming project

<Project Sdk="Microsoft.NET.Sdk.Testing">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <TestFramework>XUnit</TestFramework>
    <TestingExtensionsProfile>AllMicrosoft</TestingExtensionsProfile>
  </PropertyGroup>
</Project>

The xUnit version is omitted above — the SDK supplies a pinned default. Add <XUnitVersion>3.0.0</XUnitVersion> to lock to a specific version, or <XUnitVersion>*</XUnitVersion> to float to the latest stable on each restore.

What I''d like reviewers to push back on

  1. MSTest carve-out: delegate to MSTest.Sdk (recommended) vs re-implement inline (current prototype)?
  2. Token spelling: MicrosoftTestingPlatform (canonical) + MTP (alias) — fine, or pick one?
  3. XUnit = always v3 with no v2 path — confirmed direction?
  4. Default versions vs * floating vs explicit pins — the prototype now ships pinned defaults and exposes all three modes (no property = SDK default, <Version>X.Y</Version> = explicit pin, <Version>*</Version> = float). Reasonable tradeoff?
  5. Validating with BeforeTargets="CollectPackageReferences;BeforeBuild" for required-property checks — early enough? Better target?

Known gaps (also in src/TestingSdk/README.md)

  • MSTest path is inline; should delegate to MSTest.Sdk.
  • No dotnet new template changes (lives in dotnet/templating).
  • No Features/Aspire.targets, Features/Playwright.targets, Runner/NativeAOT.targets analogues yet.
  • No <Framework>PackageSource (v2).
  • No test asset under test/TestAssets/TestProjects yet.
  • Error messages inline; should move to .resx/.xlf for localization.
  • Has not yet been validated end-to-end through a build.cmd + dogfood + dotnet new + dotnet test cycle on my machine — CI on this PR will be the first real exercise.

Closes: nothing — keep #49294 open for design discussion.

Prototype for the testing-SDK proposal in dotnet#49294. Not production
ready and not wired into templates — opening as a draft PR so the shape can
be discussed against concrete code rather than just a design doc.

What it does
- New in-tree MSBuild SDK at src/TestingSdk/ that lays out the same way as
  src/WebSdk/Worker/ (Sdk/Sdk.props + Sdk/Sdk.targets entry points,
  Targets/Microsoft.NET.Sdk.Testing.{props,targets} dispatch, per-framework
  and per-platform files under Frameworks/ and Platforms/, packaging-only
  Tasks/Microsoft.NET.Sdk.Testing.Tasks.csproj with no compiled code).
- Wired into src/Layout/redist/targets/GenerateLayout.targets so the built
  SDK is deployed to Sdks/Microsoft.NET.Sdk.Testing/ next to the other
  in-band SDKs.
- Added to sdk.slnx and source-build.slnf.

Surface (prototype scope)
- TestFramework (required): MSTest | NUnit | XUnit | TUnit | Expecto.
  XUnit always means xUnit v3.
- <Framework>Version (required in this prototype, e.g. XUnitVersion):
  framework binaries come from NuGet at the user-pinned version; nothing
  framework-specific is shipped in-band. Mandatory in v1 to prevent silent
  test-framework upgrades on .NET SDK bumps.
- TestPlatform (optional): MicrosoftTestingPlatform (alias 'MTP') | VSTest.
  Defaults from framework. Invalid combos (e.g. TUnit + VSTest) error at
  evaluation.
- IsTestUtilityProject (optional, default false): when true, no Exe output,
  no Microsoft.NET.Test.Sdk reference, framework metapackage swapped for
  assert/extensibility packages.
- MTP extensions: TestingExtensionsProfile (Default | AllMicrosoft | None)
  + per-extension EnableMicrosoftTestingExtensions<Name> and version
  override properties, mirroring MSTest.Sdk's surface in
  src/Package/MSTest.Sdk/Sdk/Runner/Common.targets.

Known gaps (see src/TestingSdk/README.md)
- MSTest path is inline; a real implementation should delegate to MSTest.Sdk.
- No template integration; dotnet new mstest/nunit/xunit still emit the
  classic shape.
- No Aspire/Playwright/NativeAOT Features ported yet.
- No <Framework>PackageSource local-path override (v2).
- No test asset under test/TestAssets/TestProjects exercising the SDK yet.
- Strings are inline; should move to .resx + .xlf in a real version.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink and others added 3 commits June 10, 2026 14:05
Expecto does ship a VSTest adapter (YoloDev.Expecto.TestSdk); only TUnit is
genuinely MTP-only. Allow TestPlatform=VSTest for Expecto, require
YoloDevExpectoTestSdkVersion when on that path, and update the matrix
comments + README accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds Targets/_DefaultPackageVersions.props with pinned defaults for every
test-framework, companion-package, and MTP/VSTest platform package the SDK
emits. This is imported from the main Microsoft.NET.Sdk.Testing.props before
the per-framework dispatch so framework props always see a populated
<Framework>Version property.

Removes the mandatory <Framework>Version is required Error checks from
each framework's .targets (and from VSTest.targets for MicrosoftNETTestSdkVersion).
Users can now either accept the SDK default, override to a fixed version, or
override to '*' / '*-*' to opt into NuGet floating (with the usual
reproducibility caveats — recommend a NuGet lock file in that mode).

The pinned defaults are placeholder values for the prototype; in the real
SDK they would be sourced from eng/Versions.props at build time so the
matrix advances on each .NET SDK release.

README updated with no-version / pinned / floating examples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Switch from the VersionOverride approach to the documented MSTest.Sdk
pattern (src/Package/MSTest.Sdk/README.md):

  When CPM is off  -> <PackageReference Include="X"><Version>$(XVersion)</Version></PackageReference>
  When CPM is on   -> <PackageReference Include="X"/>                              (no version metadata)
                      <PackageVersion   Include="X" Version="$(XVersion)"/>        (CPM resolves)

Advantages over VersionOverride:
  - Works regardless of CentralPackageVersionOverrideEnabled (no NU1013).
  - No NU1009 "implicit reference" warnings.
  - User CPM PackageVersion overrides still propagate cleanly because our
    own PackageVersion is gated on the same condition.

Drops the now-unnecessary _NETSdkTestingUseCpmOverride private helper and
the CentralPackageVersionOverrideEnabled=false guidance error; the new
pattern just works in that mode.

README updated with a CPM section reflecting the new pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant