{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/migrate-xunit-to-xunit-v3",
  "version": "1.0.1",
  "name": "migrate-xunit-to-xunit-v3",
  "description": "Migrate .NET test projects from xUnit.net v2 to xunit.v3 and fix v3 breaks. Use for package/CPM conversion, OutputType=Exe, preserving the VSTest or MTP runner (including projects currently using YTest.MTP.XUnit2), incompatible TFMs, async void tests, string-to-Type attributes, custom Fact/Theory/BeforeAfterTest attributes, Xunit.SkippableFact, xunit.abstractions/extensibility consolidation, and Xunit.Combinatorial/StaFact compatibility. Do not use for framework conversion or a runner-only migration. For xUnit v3 MTP filter syntax, also use migrate-vstest-to-mtp.",
  "system_prompt_fragment": "# xunit.v3 Migration\n\nMigrate .NET test projects from xUnit.net v2 to xUnit.net v3. The outcome is a solution where all test projects reference `xunit.v3.*` packages, compiles cleanly, and all tests pass with the same results as before migration.\n\n## When to Use\n\n- Upgrading test projects from `xunit` (v2) packages to `xunit.v3`\n- Resolving compilation errors after updating xunit package references to v3\n\n## When Not to Use\n\n- Migrating between test frameworks (e.g., MSTest or NUnit to xUnit.net) — different effort entirely\n- Migrating from VSTest to Microsoft.Testing.Platform — use `migrate-vstest-to-mtp`\n- The projects already reference `xunit.v3` — migration is done\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Test project or solution | Yes | The .NET project or solution containing xUnit.net v2 test projects |\n\n## Workflow\n\n> **Commit strategy:** Commit after each major step so the migration is reviewable and bisectable. Separate project file changes from code changes.\n\n> **Prioritization:** Steps 1-5 are required for every migration. Steps 6-12 are conditional — only apply the ones relevant to the project's code patterns. Skip steps that don't apply.\n\n### Step 1: Identify xUnit.net projects and verify compatibility\n\nSearch for test projects referencing xUnit.net v2 packages:\n\n- `xunit`\n- `xunit.abstractions`\n- `xunit.assert`\n- `xunit.core`\n- `xunit.extensibility.core`\n- `xunit.extensibility.execution`\n- `xunit.runner.visualstudio`\n\nMake sure to check the package references in project files, MSBuild props and targets files, like `Directory.Build.props`, `Directory.Build.targets`, and `Directory.Packages.props`.\n\nVerify target framework compatibility: xUnit.net v3 requires **.NET 8+** or **.NET Framework 4.7.2+**. For test library projects, .NET Standard 2.0 is also supported. If any test projects have non-compatible target frameworks, STOP here — tell the user to upgrade the target framework first. Also verify the project uses SDK-style format.\n\n### Step 2: Update package references\n\n1. Update any `PackageReference` or `PackageVersion` items for the new package names, based on the following mapping:\n\n    - `xunit` → `xunit.v3`\n    - `xunit.abstractions` → Remove entirely\n    - `xunit.assert` → `xunit.v3.assert`\n    - `xunit.core` → `xunit.v3.core`\n    - `xunit.extensibility.core` and `xunit.extensibility.execution` → `xunit.v3.extensibility.core` (if both are referenced in a project consolidate to only a single entry as the two packages are merged)\n\n2. Update all `xunit.v3.*` packages to the latest correct version available on NuGet. Also update `xunit.runner.visualstudio` to the latest version.\n\n### Step 3: Set `OutputType` to `Exe`\n\nIn each test project (excluding test library projects), set `OutputType` to `Exe` in the project file:\n\n```xml\n<PropertyGroup>\n  <OutputType>Exe</OutputType>\n</PropertyGroup>\n```\n\nDepending on the solution in hand, there might be a centralized place where this can be added. For example:\n\n- If all test projects share (or can share) a common `Directory.Build.props`, add the `<OutputType>Exe</OutputType>` property there. Note that the OutputType should not be added to `Directory.Build.targets`.\n- If all test projects share a name pattern (e.g., `*.Tests.csproj`), add a conditional property group in `Directory.Build.props` that applies only to those projects, like `<OutputType Condition=\"$(MSBuildProjectName.EndsWith('.Tests'))\">Exe</OutputType>`. Adjust the condition as needed to target only test projects.\n- Otherwise, add the `<OutputType>Exe</OutputType>` property to each test project file individually.\n\n### Step 4: Configure test platform\n\nPreserve the same test platform that was used with xUnit.net v2. xUnit.net v2 always uses VSTest except if the project used `YTest.MTP.XUnit2`.\n\n- If the project had a reference to `YTest.MTP.XUnit2`:\n  - Remove the reference to `YTest.MTP.XUnit2` completely.\n  - Add `<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>` to `Directory.Build.props` under an unconditional `PropertyGroup`.\n- If the project did NOT reference `YTest.MTP.XUnit2` (the common case):\n  - Add `<IsTestingPlatformApplication>false</IsTestingPlatformApplication>` to `Directory.Build.props` under an unconditional `PropertyGroup`. If `Directory.Build.props` doesn't exist, create it. This keeps the project on VSTest.\n\n### Step 5: Remove `Xunit.Abstractions` usings\n\nFind any `using Xunit.Abstractions;` directives in C# files and remove them completely.\n\n### Step 6: Address `async void` breaking change (if applicable)\n\nIn xUnit.net v3, `async void` test methods are no longer supported and will fail to compile. Search for any test methods declared with `async void` and change them to `async Task`. Test methods can be identified via the `[Fact]` or `[Theory]` attributes or other test attributes.\n\n### Step 7: Address breaking change of attributes (if applicable)\n\nIn xUnit.net v3, some attributes were updated so that they accept a `System.Type` instead of two strings (fully qualified type name and assembly name). These attributes are:\n\n- `CollectionBehaviorAttribute`\n- `TestCaseOrdererAttribute`\n- `TestCollectionOrdererAttribute`\n- `TestFrameworkAttribute`\n\nFor example, `[assembly: CollectionBehavior(\"MyNamespace.MyCollectionFactory\", \"MyAssembly\")]` must be converted to `[assembly: CollectionBehavior(typeof(MyNamespace.MyCollectionFactory))]`.\n\n### Step 8: Inheriting from FactAttribute or TheoryAttribute (if applicable)\n\nIdentify if there are any custom attributes that inherit from `FactAttribute` or `TheoryAttribute`. These custom user-defined attributes must now provide source information. For example, if the attribute looked like this:\n\n```csharp\ninternal sealed class MyFactAttribute : FactAttribute\n{\n    public MyFactAttribute()\n    {\n    }\n}\n```\n\nit must be changed to this:\n\n```csharp\ninternal sealed class MyFactAttribute : FactAttribute\n{\n    public MyFactAttribute(\n        [CallerFilePath] string? sourceFilePath = null,\n        [CallerLineNumber] int sourceLineNumber = -1\n    ) : base(sourceFilePath, sourceLineNumber)\n    {\n    }\n}\n```\n\n### Step 9: Inheriting from BeforeAfterTestAttribute (if applicable)\n\nIdentify if there are any custom attributes that inherit from `BeforeAfterTestAttribute`. These custom user-defined attributes must update their method signatures. Previously, they would have `Before`/`After` overrides that look like this:\n\n```csharp\n    public override void Before(MethodInfo methodUnderTest)\n    {\n        // Possibly some custom logic here\n        base.Before(methodUnderTest);\n        // Possibly some custom logic here\n    }\n\n    public override void After(MethodInfo methodUnderTest)\n    {\n        // Possibly some custom logic here\n        base.After(methodUnderTest);\n        // Possibly some custom logic here\n    }\n```\n\nit must be changed to this:\n\n```csharp\n    public override void Before(MethodInfo methodUnderTest, IXunitTest test)\n    {\n        // Possibly some custom logic here\n        base.Before(methodUnderTest, test);\n        // Possibly some custom logic here\n    }\n\n    public override void After(MethodInfo methodUnderTest, IXunitTest test)\n    {\n        // Possibly some custom logic here\n        base.After(methodUnderTest, test);\n        // Possibly some custom logic here\n    }\n```\n\n### Step 10: Address new xUnit analyzer warnings (if applicable)\n\nxunit.v3 introduced new analyzer warnings. The most notable is xUnit1051 (use `TestContext.Current.CancellationToken` for methods accepting `CancellationToken`). Address these if present.\n\n### Step 11: Migrate `Xunit.SkippableFact` (if applicable)\n\nIf there are any package references to `Xunit.SkippableFact`, remove all these package references entirely.\n\nThen, follow these steps to eliminate usages of APIs coming from the removed package reference:\n\n- Update any `SkippableFact` attribute to the regular `Fact` attribute.\n- Update any `SkippableTheory` attribute to the regular `Theory` attribute.\n- Change `Skip.If` method calls to `Assert.SkipWhen`.\n- Change `Skip.IfNot` method calls to `Assert.SkipUnless`.\n\n### Step 12: Update companion packages (if applicable)\n\n- `Xunit.Combinatorial` 1.x → latest 2.x\n- `Xunit.StaFact` 1.x → latest 3.x\n\n### Step 13: Build and verify\n\nBuild the solution and fix any remaining compilation errors. Run `dotnet test` to verify all tests pass with the same results as before migration.",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/migrate-xunit-to-xunit-v3"
  ],
  "tags": [
    "dotnet-test",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-test-migration/skills/migrate-xunit-to-xunit-v3/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-test-migration/skills/migrate-xunit-to-xunit-v3/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py."
  }
}