[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-dotnet-migrate-mstest-v3-to-v4":3,"mdc-6j942m-key":34,"related-repo-dotnet-migrate-mstest-v3-to-v4":4174,"related-org-dotnet-migrate-mstest-v3-to-v4":4282},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":22,"repoUrl":23,"updatedAt":24,"license":25,"forks":26,"topics":27,"repo":29,"sourceUrl":32,"mdContent":33},"migrate-mstest-v3-to-v4","migrate MSTest projects to v4","Fix build errors and breaking changes after upgrading MSTest v3 to v4, or plan a complete v3-to-v4 migration. Use when user says \"upgrade to MSTest v4\", \"MSTest 4 migration\", \"MSTest v4 breaking changes\", \"tests don't compile after upgrading MSTest 3.x to 4.x\", or hits CS0507, CS0103, CS1061, CS1615 after updating MSTest packages to 4.x. USE FOR: Execute to ExecuteAsync, CallerInfo ctor on TestMethodAttribute, sealed custom attributes, ClassCleanupBehavior removal, TestContext.Properties Contains to ContainsKey, Assert.ThrowsException to ThrowsExactly, Assert.IsInstanceOfType out param removal, ExpectedExceptionAttribute removal, TestTimeout enum removal, [TestMethod(\"name\")] to DisplayName syntax, TreatDiscoveryWarningsAsErrors, TestContext.TestName in ClassInitialize, MSTest.Sdk MTP changes, dropped TFMs (net6.0\u002Fnet7.0 to net8.0+). DO NOT USE FOR: MSTest v1\u002Fv2 to v3, or errors left from a 2.x-to-3.x upgrade (use migrate-mstest-v1v2-to-v3 first); test framework conversions; general .NET upgrades.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"dotnet",".NET (Microsoft)","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fdotnet.png",[12,16,19],{"name":13,"slug":14,"type":15},".NET","net","tag",{"name":17,"slug":18,"type":15},"Migration","migration",{"name":20,"slug":21,"type":15},"Testing","testing",4576,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills","2026-08-01T05:42:17.269607","MIT",332,[28],"agent-skills",{"repoUrl":23,"stars":22,"forks":26,"topics":30,"description":31},[28],"Repository for skills to assist AI coding agents with .NET and C#","https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fdotnet-test-migration\u002Fskills\u002Fmigrate-mstest-v3-to-v4","---\nname: migrate-mstest-v3-to-v4\ndescription: >\n  Fix build errors and breaking changes after upgrading MSTest v3 to v4, or\n  plan a complete v3-to-v4 migration. Use when user says \"upgrade to\n  MSTest v4\", \"MSTest 4 migration\", \"MSTest v4 breaking changes\", \"tests don't\n  compile after upgrading MSTest 3.x to 4.x\", or hits CS0507, CS0103, CS1061, CS1615 after updating MSTest packages to 4.x.\n  USE FOR: Execute to ExecuteAsync, CallerInfo ctor on TestMethodAttribute,\n  sealed custom attributes, ClassCleanupBehavior removal, TestContext.Properties\n  Contains to ContainsKey, Assert.ThrowsException to ThrowsExactly,\n  Assert.IsInstanceOfType out param removal, ExpectedExceptionAttribute\n  removal, TestTimeout enum removal, [TestMethod(\"name\")] to DisplayName syntax,\n  TreatDiscoveryWarningsAsErrors, TestContext.TestName in ClassInitialize,\n  MSTest.Sdk MTP changes, dropped TFMs (net6.0\u002Fnet7.0 to net8.0+).\n  DO NOT USE FOR: MSTest v1\u002Fv2 to v3, or errors left from a 2.x-to-3.x upgrade\n  (use migrate-mstest-v1v2-to-v3 first); test framework conversions; general\n  .NET upgrades.\nlicense: MIT\n---\n\n# MSTest v3 -> v4 Migration\n\nMigrate a test project from MSTest v3 to MSTest v4. The outcome is a project using MSTest v4 that builds cleanly, passes tests, and accounts for every source-incompatible and behavioral change. MSTest v4 is **not binary compatible** with MSTest v3 -- any library compiled against v3 must be recompiled against v4.\n\n## When to Use\n\n- Upgrading `MSTest.TestFramework`, `MSTest.TestAdapter`, or `MSTest` metapackage from 3.x to 4.x\n- Upgrading `MSTest.Sdk` from 3.x to 4.x\n- Fixing build errors after updating to MSTest v4 packages\n- Resolving behavioral changes in test execution after upgrading to MSTest v4\n- Updating custom `TestMethodAttribute` or `ConditionBaseAttribute` implementations for v4\n\n## When Not to Use\n\n- The project already uses MSTest v4 and builds cleanly -- migration is done\n- Upgrading from MSTest v1 or v2 -- use `migrate-mstest-v1v2-to-v3` first, then return here\n- The project does not use MSTest\n- Migrating between test frameworks (e.g., MSTest to xUnit or NUnit)\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Project or solution path | No | The `.csproj`, `.sln`, or `.slnx` entry point containing MSTest test projects. **Discover it yourself** by globbing the working directory; ask only when nothing is found or the choice is genuinely ambiguous |\n| Build command | No | How to build (e.g., `dotnet build`, a repo build script). Auto-detect if not provided |\n| Test command | No | How to run tests (e.g., `dotnet test`). Auto-detect if not provided |\n\n## Response Guidelines\n\n- **Always identify the current version first**: Before recommending any migration steps, explicitly state the current MSTest version detected in the project (e.g., \"Your project uses MSTest v3 (3.8.0)\"). This confirms you've read the project files and grounds the migration advice.\n- **Focused fix requests** (user has specific compilation errors after upgrading): Address only the relevant breaking changes from Step 3. **Always provide concrete fixed code** using the user's actual types and method names — show a complete, copy-pasteable code snippet, not just a description of what to change. For custom `TestMethodAttribute` subclasses, show the full fixed class including CallerInfo propagation to the base constructor. Mention any related analyzer that could have caught this earlier (e.g., MSTEST0006 for ExpectedException). Do not walk through the entire migration workflow.\n- **\"What to expect\" questions** (user asks about breaking changes before upgrading): Present ALL major breaking changes from the Step 3 quick-lookup table -- not just the ones visible in the current code. For each, provide a one-line fix summary. Also mention key behavioral changes from Step 4 (especially TestCase.Id history impact and TreatDiscoveryWarningsAsErrors default). If project code is available, highlight which changes apply directly.\n- **Full migration requests** (user wants complete migration): Follow the complete workflow below.\n- **Behavioral\u002Fruntime symptom reports** (user describes test execution differences without build errors): Match described symptoms to the behavioral changes table in Step 4. Provide targeted, symptom-specific advice. Mention other behavioral changes the user should watch for. Do not walk through source breaking changes unless the user also has build errors.\n- **CI\u002Ftest-discovery issues** (tests not discovered, vstest.console stopped working, CI pipeline failures after upgrading): Focus on 4.5 (MSTest.Sdk defaults to MTP mode, which does not include Microsoft.NET.Test.Sdk -- needed for vstest.console) and 4.4 (TreatDiscoveryWarningsAsErrors). Explain the root cause clearly and give both fix options (add Microsoft.NET.Test.Sdk package or switch to `dotnet test`). Do not walk through the full migration workflow.\n- **Explanatory questions** (user asks \"is this a known change?\", \"what else should I watch out for?\"): Explain the relevant changes and advise. Mention related changes the user might encounter next. Do not prescribe a full migration procedure.\n\n## Workflow\n\n> **Commit strategy:** Commit at each logical boundary -- after updating packages (Step 2), after resolving source breaking changes (Step 3), after addressing behavioral changes (Step 4). This keeps each commit focused and reviewable.\n\n### Step 1: Assess the project\n\n1. Identify the current MSTest version by checking package references for `MSTest`, `MSTest.TestFramework`, `MSTest.TestAdapter`, or `MSTest.Sdk` in `.csproj`, `Directory.Build.props`, or `Directory.Packages.props`.\n2. Confirm the project is on MSTest v3 (3.x). If on v1 or v2, use `migrate-mstest-v1v2-to-v3` first.\n3. Check target framework(s) -- MSTest v4 drops support for .NET Core 3.1 through .NET 7. Supported target frameworks are: **net8.0**, **net9.0**, **net462** (.NET Framework 4.6.2+), **uap10.0.16299** (UWP), **net9.0-windows10.0.17763.0** (modern UWP), and **net8.0-windows10.0.18362.0** (WinUI).\n4. Check for custom `TestMethodAttribute` subclasses -- these require changes in v4.\n5. Check for usages of `ExpectedExceptionAttribute` -- removed in v4 (deprecated since v3 with analyzer MSTEST0006).\n6. Check for usages of `Assert.ThrowsException` (deprecated) -- removed in v4.\n7. Run a clean build to establish a baseline of existing errors\u002Fwarnings.\n\n### Step 2: Update packages to MSTest v4\n\n**If using the MSTest metapackage:**\n\n```xml\n\u003CPackageReference Include=\"MSTest\" Version=\"4.1.0\" \u002F>\n```\n\n**If using individual packages:**\n\n```xml\n\u003CPackageReference Include=\"MSTest.TestFramework\" Version=\"4.1.0\" \u002F>\n\u003CPackageReference Include=\"MSTest.TestAdapter\" Version=\"4.1.0\" \u002F>\n```\n\n**If using MSTest.Sdk:**\n\n```xml\n\u003CProject Sdk=\"MSTest.Sdk\u002F4.1.0\">\n```\n\nRun `dotnet restore`, then `dotnet build`. Collect all errors for Step 3.\n\n### Step 3: Resolve source breaking changes\n\nWork through compilation errors systematically. Use this quick-lookup table to identify all applicable changes, then apply each fix:\n\n| Error \u002F Pattern in code | Breaking change | Fix |\n|---|---|---|\n| Custom `TestMethodAttribute` overrides `Execute` | Execute removed | Change to `ExecuteAsync` returning `Task\u003CTestResult[]>` (3.1) |\n| `[TestMethod(\"name\")]` or custom attribute constructor | CallerInfo params added | Use `DisplayName = \"name\"` named param; propagate CallerInfo in subclasses (3.2) |\n| `ClassCleanupBehavior.EndOfClass` | Enum removed | Remove argument: just `[ClassCleanup]` (3.3) |\n| `TestContext.Properties.Contains(\"key\")` | `Properties` is `IDictionary\u003Cstring, object>` | Change to `ContainsKey(\"key\")` (3.4) |\n| `[Timeout(TestTimeout.Infinite)]` | `TestTimeout` enum removed | Replace with `[Timeout(int.MaxValue)]` (3.5) |\n| `TestContext.ManagedType` | Property removed | Use `FullyQualifiedTestClassName` (3.6) |\n| `Assert.AreEqual(a, b, \"msg {0}\", arg)` | Message+params overloads removed | Use string interpolation: `$\"msg {arg}\"` (3.7) |\n| `Assert.ThrowsException\u003CT>(...)` | Renamed | Replace with `Assert.ThrowsExactly\u003CT>(...)` or `Assert.Throws\u003CT>(...)` (3.7) |\n| `Assert.IsInstanceOfType\u003CT>(obj, out var t)` | Out parameter removed | Use `var t = Assert.IsInstanceOfType\u003CT>(obj)` (3.7) |\n| `[ExpectedException(typeof(T))]` | Attribute removed | Move assertion into test body: `Assert.ThrowsExactly\u003CT>(() => ...)` (3.8) |\n| Project targets net5.0, net6.0, or net7.0 | TFM dropped | Change to net8.0 or net9.0 (3.9) |\n\n> **Important**: Scan the entire project for ALL patterns above before starting fixes. Multiple breaking changes often coexist in the same project.\n\n#### 3.1 TestMethodAttribute.Execute -> ExecuteAsync\n\nIf you have custom `TestMethodAttribute` subclasses that override `Execute`, change to `ExecuteAsync`. This change was made because the v3 synchronous `Execute` API caused deadlocks when test code used `async`\u002F`await` internally -- the synchronous wrapper would block the thread while the async operation needed that same thread to complete.\n\n```csharp\n\u002F\u002F Before (v3)\npublic sealed class MyTestMethodAttribute : TestMethodAttribute\n{\n    public override TestResult[] Execute(ITestMethod testMethod)\n    {\n        \u002F\u002F custom logic\n        return result;\n    }\n}\n\n\u002F\u002F After (v4) -- Option A: wrap synchronous logic with Task.FromResult\npublic sealed class MyTestMethodAttribute : TestMethodAttribute\n{\n    public override Task\u003CTestResult[]> ExecuteAsync(ITestMethod testMethod)\n    {\n        \u002F\u002F custom logic (synchronous)\n        return Task.FromResult(result);\n    }\n}\n\n\u002F\u002F After (v4) -- Option B: make properly async\npublic sealed class MyTestMethodAttribute : TestMethodAttribute\n{\n    public override async Task\u003CTestResult[]> ExecuteAsync(ITestMethod testMethod)\n    {\n        \u002F\u002F custom async logic\n        return await base.ExecuteAsync(testMethod);\n    }\n}\n```\n\nUse `Task.FromResult` when your override logic is purely synchronous. Use `async`\u002F`await` when you call `base.ExecuteAsync` or other async methods.\n\n#### 3.2 TestMethodAttribute CallerInfo constructor\n\n`TestMethodAttribute` now uses `[CallerFilePath]` and `[CallerLineNumber]` parameters in its constructor.\n\n**If you inherit from TestMethodAttribute**, propagate caller info to the base class:\n\n```csharp\npublic class MyTestMethodAttribute : TestMethodAttribute\n{\n    public MyTestMethodAttribute(\n        [CallerFilePath] string callerFilePath = \"\",\n        [CallerLineNumber] int callerLineNumber = -1)\n        : base(callerFilePath, callerLineNumber)\n    {\n    }\n}\n```\n\n**If you use `[TestMethodAttribute(\"Custom display name\")]`**, switch to the named parameter syntax:\n\n```csharp\n\u002F\u002F Before (v3)\n[TestMethodAttribute(\"Custom display name\")]\n\n\u002F\u002F After (v4)\n[TestMethodAttribute(DisplayName = \"Custom display name\")]\n```\n\n#### 3.3 ClassCleanupBehavior enum removed\n\nThe `ClassCleanupBehavior` enum is removed. In v3, this enum controlled whether class cleanup ran at end of class (`EndOfClass`) or end of assembly (`EndOfAssembly`). In v4, class cleanup always runs at end of class. Remove the enum argument:\n\n```csharp\n\u002F\u002F Before (v3)\n[ClassCleanup(ClassCleanupBehavior.EndOfClass)]\npublic static void ClassCleanup(TestContext testContext) { }\n\n\u002F\u002F After (v4)\n[ClassCleanup]\npublic static void ClassCleanup(TestContext testContext) { }\n```\n\nIf you previously used `ClassCleanupBehavior.EndOfAssembly`, move that cleanup logic to an `[AssemblyCleanup]` method instead.\n\n#### 3.4 TestContext.Properties type change\n\n`TestContext.Properties` changed from `IDictionary` to `IDictionary\u003Cstring, object>`. Update any `Contains` calls to `ContainsKey`:\n\n```csharp\n\u002F\u002F Before (v3)\ntestContext.Properties.Contains(\"key\");\n\n\u002F\u002F After (v4)\ntestContext.Properties.ContainsKey(\"key\");\n```\n\n#### 3.5 TestTimeout enum removed\n\nThe `TestTimeout` enum (with only `TestTimeout.Infinite`) is removed. Replace with `int.MaxValue`:\n\n```csharp\n\u002F\u002F Before (v3)\n[Timeout(TestTimeout.Infinite)]\n\n\u002F\u002F After (v4)\n[Timeout(int.MaxValue)]\n```\n\n#### 3.6 TestContext.ManagedType removed\n\nThe `TestContext.ManagedType` property is removed. Use `TestContext.FullyQualifiedTestClassName` instead.\n\n#### 3.7 Assert API signature changes\n\n- **Message + params removed**: Assert methods that accepted both `message` and `object[]` parameters now accept only `message`. Use string interpolation instead of format strings:\n\n```csharp\n\u002F\u002F Before (v3)\nAssert.AreEqual(expected, actual, \"Expected {0} but got {1}\", expected, actual);\n\n\u002F\u002F After (v4)\nAssert.AreEqual(expected, actual, $\"Expected {expected} but got {actual}\");\n```\n\n- **Assert.ThrowsException renamed**: The `Assert.ThrowsException` APIs are renamed. Use `Assert.ThrowsExactly` (strict type match) or `Assert.Throws` (accepts derived exception types):\n\n```csharp\n\u002F\u002F Before (v3)\nAssert.ThrowsException\u003CInvalidOperationException>(() => DoSomething());\n\n\u002F\u002F After (v4) -- exact type match (same behavior as old ThrowsException)\nAssert.ThrowsExactly\u003CInvalidOperationException>(() => DoSomething());\n\n\u002F\u002F After (v4) -- also catches derived exception types\nAssert.Throws\u003CInvalidOperationException>(() => DoSomething());\n```\n\n- **Assert.IsInstanceOfType out parameter changed**: `Assert.IsInstanceOfType\u003CT>(x, out var t)` changes to `var t = Assert.IsInstanceOfType\u003CT>(x)`:\n\n```csharp\n\u002F\u002F Before (v3)\nAssert.IsInstanceOfType\u003CMyType>(obj, out var typed);\n\n\u002F\u002F After (v4)\nvar typed = Assert.IsInstanceOfType\u003CMyType>(obj);\n```\n\n- **Assert.AreEqual for IEquatable\\\u003CT\\> removed**: If you get generic type inference errors, explicitly specify the type argument as `object`.\n\n#### 3.8 ExpectedExceptionAttribute removed\n\nThe `[ExpectedException]` attribute is removed in v4. In MSTest 3.2, the `MSTEST0006` analyzer was introduced to flag `[ExpectedException]` usage and suggest migrating to `Assert.ThrowsExactly` while still on v3 (a non-breaking change). In v4, the attribute is gone entirely. Migrate to `Assert.ThrowsExactly`:\n\n```csharp\n\u002F\u002F Before (v3)\n[ExpectedException(typeof(InvalidOperationException))]\n[TestMethod]\npublic void TestMethod()\n{\n    MyCall();\n}\n\n\u002F\u002F After (v4)\n[TestMethod]\npublic void TestMethod()\n{\n    Assert.ThrowsExactly\u003CInvalidOperationException>(() => MyCall());\n}\n```\n\n**When the test has setup code before the throwing call**, wrap only the throwing call in the lambda -- keep Arrange\u002FAct separation clear:\n\n```csharp\n\u002F\u002F Before (v3)\n[ExpectedException(typeof(ArgumentNullException))]\n[TestMethod]\npublic void Validate_NullInput_Throws()\n{\n    var service = new ValidationService();\n    service.Validate(null);  \u002F\u002F throws here\n}\n\n\u002F\u002F After (v4)\n[TestMethod]\npublic void Validate_NullInput_Throws()\n{\n    var service = new ValidationService();\n    Assert.ThrowsExactly\u003CArgumentNullException>(() => service.Validate(null));\n}\n```\n\n**For async test methods**, use `Assert.ThrowsExactlyAsync`:\n\n```csharp\n\u002F\u002F Before (v3)\n[ExpectedException(typeof(HttpRequestException))]\n[TestMethod]\npublic async Task FetchData_BadUrl_Throws()\n{\n    await client.GetAsync(\"https:\u002F\u002Flocalhost:0\");\n}\n\n\u002F\u002F After (v4)\n[TestMethod]\npublic async Task FetchData_BadUrl_Throws()\n{\n    await Assert.ThrowsExactlyAsync\u003CHttpRequestException>(\n        () => client.GetAsync(\"https:\u002F\u002Flocalhost:0\"));\n}\n```\n\n**If `[ExpectedException]` used the `AllowDerivedTypes` property**, use `Assert.ThrowsAsync\u003CT>` (base type matching) instead of `Assert.ThrowsExactlyAsync\u003CT>` (exact type matching).\n\n#### 3.9 Dropped target frameworks\n\nMSTest v4 supports: **net8.0**, **net9.0**, **net462** (.NET Framework 4.6.2+), **uap10.0.16299** (UWP), **net9.0-windows10.0.17763.0** (modern UWP), and **net8.0-windows10.0.18362.0** (WinUI). All other frameworks are dropped -- including net5.0, net6.0, net7.0, and netcoreapp3.1.\n\nIf the test project targets an unsupported framework, update `TargetFramework`:\n\n```xml\n\u003C!-- Before -->\n\u003CTargetFramework>net6.0\u003C\u002FTargetFramework>\n\n\u003C!-- After -->\n\u003CTargetFramework>net8.0\u003C\u002FTargetFramework>\n```\n\n#### 3.10 Unfolding strategy moved to TestMethodAttribute\n\nThe `UnfoldingStrategy` property (introduced in MSTest 3.7) has moved from individual data source attributes (`DataRowAttribute`, `DynamicDataAttribute`) to `TestMethodAttribute`.\n\n#### 3.11 ConditionBaseAttribute.ShouldRun renamed\n\nThe `ConditionBaseAttribute.ShouldRun` property is renamed to `IsConditionMet`.\n\n#### 3.12 Internal\u002Fremoved types\n\nSeveral types previously public are now internal or removed:\n\n- `MSTestDiscoverer`, `MSTestExecutor`, `AssemblyResolver`, `LogMessageListener`\n- `TestExecutionManager`, `TestMethodInfo`, `TestResultExtensions`\n- `UnitTestOutcomeExtensions`, `GenericParameterHelper`\n- `ITestMethod` in PlatformServices assembly (the one in TestFramework is unchanged)\n\nIf your code references any of these, find alternative approaches or remove the dependency.\n\n### Step 4: Address behavioral changes\n\nThese changes won't cause build errors but may affect test runtime behavior.\n\n| Symptom | Cause | Fix |\n|---|---|---|\n| Tests show as new in Azure DevOps \u002F test history lost | `TestCase.Id` generation changed (4.3) | No code fix; history will re-baseline |\n| `TestContext.TestName` throws in `[ClassInitialize]` | v4 enforces lifecycle scope (4.2) | Move access to `[TestInitialize]` or test methods |\n| Tests not discovered \u002F discovery failures | `TreatDiscoveryWarningsAsErrors` now true (4.4) | Fix warnings, or set to false in .runsettings |\n| Tests hang that didn't before | AppDomain disabled by default (4.1) | Set `DisableAppDomain` to false in .runsettings `RunConfiguration` |\n| vstest.console can't find tests with MSTest.Sdk | MSTest.Sdk defaults to MTP; `Microsoft.NET.Test.Sdk` only added in VSTest mode (4.5) | Add explicit package reference or switch to `dotnet test` |\n| New warnings from analyzers | Analyzer severities upgraded (4.6) | Fix warnings or suppress in .editorconfig |\n\n#### 4.1 DisableAppDomain defaults to true\n\nAppDomains are disabled by default. On .NET Framework, when running inside testhost (the default for `dotnet test` and VS), MSTest re-enables AppDomains automatically. If you need to explicitly control AppDomain isolation, set it via `.runsettings`:\n\n```xml\n\u003CRunSettings>\n  \u003CRunConfiguration>\n    \u003CDisableAppDomain>false\u003C\u002FDisableAppDomain>\n  \u003C\u002FRunConfiguration>\n\u003C\u002FRunSettings>\n```\n\n#### 4.2 TestContext throws when used incorrectly\n\nMSTest v4 now throws when accessing test-specific properties in the wrong lifecycle stage:\n\n- `TestContext.FullyQualifiedTestClassName` -- cannot be accessed in `[AssemblyInitialize]`\n- `TestContext.TestName` -- cannot be accessed in `[AssemblyInitialize]` or `[ClassInitialize]`\n\n**Fix**: Move any code that accesses `TestContext.TestName` from `[ClassInitialize]` to `[TestInitialize]` or individual test methods, where per-test context is available. Do not replace `TestName` with `FullyQualifiedTestClassName` as a workaround -- they have different semantics.\n\n#### 4.3 TestCase.Id generation changed\n\nThe generation algorithm for `TestCase.Id` has changed to fix long-standing bugs. This may affect Azure DevOps test result tracking (e.g., test failure tracking over time). There is no code fix needed, but be aware of test result history discontinuity.\n\n#### 4.4 TreatDiscoveryWarningsAsErrors defaults to true\n\nv4 uses stricter defaults. Discovery warnings are now treated as errors, which means tests that previously ran despite discovery issues may now fail entirely. If you see unexpected test failures after upgrading (not build errors, but tests not being discovered), check for discovery warnings. To restore v3 behavior while you investigate:\n\n```xml\n\u003CRunSettings>\n  \u003CMSTest>\n    \u003CTreatDiscoveryWarningsAsErrors>false\u003C\u002FTreatDiscoveryWarningsAsErrors>\n  \u003C\u002FMSTest>\n\u003C\u002FRunSettings>\n```\n\n> **Recommended**: Fix the underlying discovery warnings rather than suppressing this setting.\n\n#### 4.5 MSTest.Sdk and vstest.console compatibility\n\nMSTest.Sdk defaults to Microsoft.Testing.Platform (MTP) mode. In MTP mode, MSTest.Sdk does **not** add a reference to `Microsoft.NET.Test.Sdk` -- it only adds it in VSTest mode. This is not a v4-specific change; it applies to MSTest.Sdk v3 as well. Without `Microsoft.NET.Test.Sdk`, `vstest.console` cannot discover or run tests and will silently find zero tests. This commonly surfaces during migration when a CI pipeline uses `vstest.console` but the project uses MSTest.Sdk in its default MTP mode.\n\n**Option A -- Switch to VSTest mode**: Set the `UseVSTest` property. MSTest.Sdk will then automatically add `Microsoft.NET.Test.Sdk`:\n\n```xml\n\u003CProject Sdk=\"MSTest.Sdk\u002F4.1.0\">\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net8.0\u003C\u002FTargetFramework>\n    \u003CUseVSTest>true\u003C\u002FUseVSTest>\n  \u003C\u002FPropertyGroup>\n\u003C\u002FProject>\n```\n\n**Option B -- Switch CI to `dotnet test`**: Replace `vstest.console` invocations in your CI pipeline with `dotnet test`. This works natively with MTP and is the recommended long-term approach for MSTest.Sdk projects.\n\nIf you need VSTest during a transition period, Option A works without changing CI pipelines.\n\n#### 4.6 Analyzer severity changes\n\nMultiple analyzers have been upgraded from Info to Warning by default:\n\n- MSTEST0001, MSTEST0007, MSTEST0017, MSTEST0023, MSTEST0024, MSTEST0025\n- MSTEST0030, MSTEST0031, MSTEST0032, MSTEST0035, MSTEST0037, MSTEST0045\n\nReview and fix any new warnings, or suppress them in `.editorconfig` if intentional.\n\n### Step 5: Verify\n\n1. Run `dotnet build` -- confirm zero errors and review any new warnings\n2. Run `dotnet test` -- confirm all tests pass\n3. Compare test results (pass\u002Ffail counts) to the pre-migration baseline\n4. If using Azure DevOps test tracking, be aware that `TestCase.Id` changes may affect history continuity\n5. Check that no tests were silently dropped due to stricter discovery\n\n## Validation\n\n- [ ] All MSTest packages updated to 4.x\n- [ ] Project builds with zero errors\n- [ ] All tests pass with `dotnet test`\n- [ ] Custom `TestMethodAttribute` subclasses updated for `ExecuteAsync` and CallerInfo\n- [ ] `ExpectedExceptionAttribute` replaced with `Assert.ThrowsExactly`\n- [ ] `Assert.ThrowsException` replaced with `Assert.ThrowsExactly` (or `Assert.Throws`)\n- [ ] `ClassCleanupBehavior` enum usages removed\n- [ ] `TestContext.Properties.Contains` updated to `ContainsKey`\n- [ ] All target frameworks are net8.0+, net9.0, net462+, uap10.0.16299, or WinUI\n- [ ] Behavioral changes reviewed and addressed\n- [ ] No tests were lost during migration (compare test counts)\n\n## Related Skills\n\n- `writing-mstest-tests` -- for modern MSTest v4 assertion APIs and test authoring best practices\n- `run-tests` -- for running tests after migration\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| Custom `TestMethodAttribute` still overrides `Execute` | Change to `ExecuteAsync` returning `Task\u003CTestResult[]>` |\n| `TestMethodAttribute(\"display name\")` no longer compiles | Use `TestMethodAttribute(DisplayName = \"display name\")` |\n| `ClassCleanupBehavior` enum not found | Remove the enum argument; `[ClassCleanup]` now always runs at end of class. For end-of-assembly cleanup, use `[AssemblyCleanup]` |\n| `TestContext.Properties.Contains` missing | Use `ContainsKey` -- `Properties` is now `IDictionary\u003Cstring, object>` |\n| `ExpectedException` attribute not found | Replace with `Assert.ThrowsExactly\u003CT>(() => ...)` inside the test body |\n| `Assert.ThrowsException` not found | Replace with `Assert.ThrowsExactly` (or `Assert.Throws` for derived types) |\n| `Assert.AreEqual` with format string args fails | Use string interpolation: `$\"message {value}\"` |\n| Tests hang that didn't before | AppDomain is disabled by default; on .NET Fx in testhost it is re-enabled automatically |\n| Azure DevOps test history breaks | Expected -- `TestCase.Id` generation changed; no code fix, results will re-baseline |\n| Discovery warnings now fail the run | `TreatDiscoveryWarningsAsErrors` is true by default; fix the discovery warnings |\n| Net6.0\u002Fnet7.0 targets don't compile | Update to net8.0 -- MSTest v4 supports net8.0, net9.0, net462, uap10.0.16299, modern UWP, and WinUI |\n",{"data":35,"body":36},{"name":4,"description":6,"license":25},{"type":37,"children":38},"root",[39,48,62,69,147,153,184,190,320,326,420,426,440,447,601,607,615,636,644,668,676,690,710,716,721,1104,1117,1124,1173,1431,1464,1470,1496,1506,1581,1597,1642,1648,1676,1735,1756,1762,1804,1848,1854,1880,1924,1930,1949,1955,1990,2034,2070,2138,2166,2210,2230,2236,2276,2386,2396,2520,2537,2654,2694,2700,2736,2748,2794,2800,2833,2839,2858,2864,2869,2952,2957,2963,2968,3161,3167,3186,3233,3239,3244,3283,3327,3333,3345,3351,3356,3401,3414,3420,3460,3484,3538,3567,3572,3578,3583,3596,3609,3615,3662,3668,3843,3849,3874,3880,4168],{"type":40,"tag":41,"props":42,"children":44},"element","h1",{"id":43},"mstest-v3-v4-migration",[45],{"type":46,"value":47},"text","MSTest v3 -> v4 Migration",{"type":40,"tag":49,"props":50,"children":51},"p",{},[52,54,60],{"type":46,"value":53},"Migrate a test project from MSTest v3 to MSTest v4. The outcome is a project using MSTest v4 that builds cleanly, passes tests, and accounts for every source-incompatible and behavioral change. MSTest v4 is ",{"type":40,"tag":55,"props":56,"children":57},"strong",{},[58],{"type":46,"value":59},"not binary compatible",{"type":46,"value":61}," with MSTest v3 -- any library compiled against v3 must be recompiled against v4.",{"type":40,"tag":63,"props":64,"children":66},"h2",{"id":65},"when-to-use",[67],{"type":46,"value":68},"When to Use",{"type":40,"tag":70,"props":71,"children":72},"ul",{},[73,104,116,121,126],{"type":40,"tag":74,"props":75,"children":76},"li",{},[77,79,86,88,94,96,102],{"type":46,"value":78},"Upgrading ",{"type":40,"tag":80,"props":81,"children":83},"code",{"className":82},[],[84],{"type":46,"value":85},"MSTest.TestFramework",{"type":46,"value":87},", ",{"type":40,"tag":80,"props":89,"children":91},{"className":90},[],[92],{"type":46,"value":93},"MSTest.TestAdapter",{"type":46,"value":95},", or ",{"type":40,"tag":80,"props":97,"children":99},{"className":98},[],[100],{"type":46,"value":101},"MSTest",{"type":46,"value":103}," metapackage from 3.x to 4.x",{"type":40,"tag":74,"props":105,"children":106},{},[107,108,114],{"type":46,"value":78},{"type":40,"tag":80,"props":109,"children":111},{"className":110},[],[112],{"type":46,"value":113},"MSTest.Sdk",{"type":46,"value":115}," from 3.x to 4.x",{"type":40,"tag":74,"props":117,"children":118},{},[119],{"type":46,"value":120},"Fixing build errors after updating to MSTest v4 packages",{"type":40,"tag":74,"props":122,"children":123},{},[124],{"type":46,"value":125},"Resolving behavioral changes in test execution after upgrading to MSTest v4",{"type":40,"tag":74,"props":127,"children":128},{},[129,131,137,139,145],{"type":46,"value":130},"Updating custom ",{"type":40,"tag":80,"props":132,"children":134},{"className":133},[],[135],{"type":46,"value":136},"TestMethodAttribute",{"type":46,"value":138}," or ",{"type":40,"tag":80,"props":140,"children":142},{"className":141},[],[143],{"type":46,"value":144},"ConditionBaseAttribute",{"type":46,"value":146}," implementations for v4",{"type":40,"tag":63,"props":148,"children":150},{"id":149},"when-not-to-use",[151],{"type":46,"value":152},"When Not to Use",{"type":40,"tag":70,"props":154,"children":155},{},[156,161,174,179],{"type":40,"tag":74,"props":157,"children":158},{},[159],{"type":46,"value":160},"The project already uses MSTest v4 and builds cleanly -- migration is done",{"type":40,"tag":74,"props":162,"children":163},{},[164,166,172],{"type":46,"value":165},"Upgrading from MSTest v1 or v2 -- use ",{"type":40,"tag":80,"props":167,"children":169},{"className":168},[],[170],{"type":46,"value":171},"migrate-mstest-v1v2-to-v3",{"type":46,"value":173}," first, then return here",{"type":40,"tag":74,"props":175,"children":176},{},[177],{"type":46,"value":178},"The project does not use MSTest",{"type":40,"tag":74,"props":180,"children":181},{},[182],{"type":46,"value":183},"Migrating between test frameworks (e.g., MSTest to xUnit or NUnit)",{"type":40,"tag":63,"props":185,"children":187},{"id":186},"inputs",[188],{"type":46,"value":189},"Inputs",{"type":40,"tag":191,"props":192,"children":193},"table",{},[194,218],{"type":40,"tag":195,"props":196,"children":197},"thead",{},[198],{"type":40,"tag":199,"props":200,"children":201},"tr",{},[202,208,213],{"type":40,"tag":203,"props":204,"children":205},"th",{},[206],{"type":46,"value":207},"Input",{"type":40,"tag":203,"props":209,"children":210},{},[211],{"type":46,"value":212},"Required",{"type":40,"tag":203,"props":214,"children":215},{},[216],{"type":46,"value":217},"Description",{"type":40,"tag":219,"props":220,"children":221},"tbody",{},[222,270,295],{"type":40,"tag":199,"props":223,"children":224},{},[225,231,236],{"type":40,"tag":226,"props":227,"children":228},"td",{},[229],{"type":46,"value":230},"Project or solution path",{"type":40,"tag":226,"props":232,"children":233},{},[234],{"type":46,"value":235},"No",{"type":40,"tag":226,"props":237,"children":238},{},[239,241,247,248,254,255,261,263,268],{"type":46,"value":240},"The ",{"type":40,"tag":80,"props":242,"children":244},{"className":243},[],[245],{"type":46,"value":246},".csproj",{"type":46,"value":87},{"type":40,"tag":80,"props":249,"children":251},{"className":250},[],[252],{"type":46,"value":253},".sln",{"type":46,"value":95},{"type":40,"tag":80,"props":256,"children":258},{"className":257},[],[259],{"type":46,"value":260},".slnx",{"type":46,"value":262}," entry point containing MSTest test projects. ",{"type":40,"tag":55,"props":264,"children":265},{},[266],{"type":46,"value":267},"Discover it yourself",{"type":46,"value":269}," by globbing the working directory; ask only when nothing is found or the choice is genuinely ambiguous",{"type":40,"tag":199,"props":271,"children":272},{},[273,278,282],{"type":40,"tag":226,"props":274,"children":275},{},[276],{"type":46,"value":277},"Build command",{"type":40,"tag":226,"props":279,"children":280},{},[281],{"type":46,"value":235},{"type":40,"tag":226,"props":283,"children":284},{},[285,287,293],{"type":46,"value":286},"How to build (e.g., ",{"type":40,"tag":80,"props":288,"children":290},{"className":289},[],[291],{"type":46,"value":292},"dotnet build",{"type":46,"value":294},", a repo build script). Auto-detect if not provided",{"type":40,"tag":199,"props":296,"children":297},{},[298,303,307],{"type":40,"tag":226,"props":299,"children":300},{},[301],{"type":46,"value":302},"Test command",{"type":40,"tag":226,"props":304,"children":305},{},[306],{"type":46,"value":235},{"type":40,"tag":226,"props":308,"children":309},{},[310,312,318],{"type":46,"value":311},"How to run tests (e.g., ",{"type":40,"tag":80,"props":313,"children":315},{"className":314},[],[316],{"type":46,"value":317},"dotnet test",{"type":46,"value":319},"). Auto-detect if not provided",{"type":40,"tag":63,"props":321,"children":323},{"id":322},"response-guidelines",[324],{"type":46,"value":325},"Response Guidelines",{"type":40,"tag":70,"props":327,"children":328},{},[329,339,363,373,383,393,410],{"type":40,"tag":74,"props":330,"children":331},{},[332,337],{"type":40,"tag":55,"props":333,"children":334},{},[335],{"type":46,"value":336},"Always identify the current version first",{"type":46,"value":338},": Before recommending any migration steps, explicitly state the current MSTest version detected in the project (e.g., \"Your project uses MSTest v3 (3.8.0)\"). This confirms you've read the project files and grounds the migration advice.",{"type":40,"tag":74,"props":340,"children":341},{},[342,347,349,354,356,361],{"type":40,"tag":55,"props":343,"children":344},{},[345],{"type":46,"value":346},"Focused fix requests",{"type":46,"value":348}," (user has specific compilation errors after upgrading): Address only the relevant breaking changes from Step 3. ",{"type":40,"tag":55,"props":350,"children":351},{},[352],{"type":46,"value":353},"Always provide concrete fixed code",{"type":46,"value":355}," using the user's actual types and method names — show a complete, copy-pasteable code snippet, not just a description of what to change. For custom ",{"type":40,"tag":80,"props":357,"children":359},{"className":358},[],[360],{"type":46,"value":136},{"type":46,"value":362}," subclasses, show the full fixed class including CallerInfo propagation to the base constructor. Mention any related analyzer that could have caught this earlier (e.g., MSTEST0006 for ExpectedException). Do not walk through the entire migration workflow.",{"type":40,"tag":74,"props":364,"children":365},{},[366,371],{"type":40,"tag":55,"props":367,"children":368},{},[369],{"type":46,"value":370},"\"What to expect\" questions",{"type":46,"value":372}," (user asks about breaking changes before upgrading): Present ALL major breaking changes from the Step 3 quick-lookup table -- not just the ones visible in the current code. For each, provide a one-line fix summary. Also mention key behavioral changes from Step 4 (especially TestCase.Id history impact and TreatDiscoveryWarningsAsErrors default). If project code is available, highlight which changes apply directly.",{"type":40,"tag":74,"props":374,"children":375},{},[376,381],{"type":40,"tag":55,"props":377,"children":378},{},[379],{"type":46,"value":380},"Full migration requests",{"type":46,"value":382}," (user wants complete migration): Follow the complete workflow below.",{"type":40,"tag":74,"props":384,"children":385},{},[386,391],{"type":40,"tag":55,"props":387,"children":388},{},[389],{"type":46,"value":390},"Behavioral\u002Fruntime symptom reports",{"type":46,"value":392}," (user describes test execution differences without build errors): Match described symptoms to the behavioral changes table in Step 4. Provide targeted, symptom-specific advice. Mention other behavioral changes the user should watch for. Do not walk through source breaking changes unless the user also has build errors.",{"type":40,"tag":74,"props":394,"children":395},{},[396,401,403,408],{"type":40,"tag":55,"props":397,"children":398},{},[399],{"type":46,"value":400},"CI\u002Ftest-discovery issues",{"type":46,"value":402}," (tests not discovered, vstest.console stopped working, CI pipeline failures after upgrading): Focus on 4.5 (MSTest.Sdk defaults to MTP mode, which does not include Microsoft.NET.Test.Sdk -- needed for vstest.console) and 4.4 (TreatDiscoveryWarningsAsErrors). Explain the root cause clearly and give both fix options (add Microsoft.NET.Test.Sdk package or switch to ",{"type":40,"tag":80,"props":404,"children":406},{"className":405},[],[407],{"type":46,"value":317},{"type":46,"value":409},"). Do not walk through the full migration workflow.",{"type":40,"tag":74,"props":411,"children":412},{},[413,418],{"type":40,"tag":55,"props":414,"children":415},{},[416],{"type":46,"value":417},"Explanatory questions",{"type":46,"value":419}," (user asks \"is this a known change?\", \"what else should I watch out for?\"): Explain the relevant changes and advise. Mention related changes the user might encounter next. Do not prescribe a full migration procedure.",{"type":40,"tag":63,"props":421,"children":423},{"id":422},"workflow",[424],{"type":46,"value":425},"Workflow",{"type":40,"tag":427,"props":428,"children":429},"blockquote",{},[430],{"type":40,"tag":49,"props":431,"children":432},{},[433,438],{"type":40,"tag":55,"props":434,"children":435},{},[436],{"type":46,"value":437},"Commit strategy:",{"type":46,"value":439}," Commit at each logical boundary -- after updating packages (Step 2), after resolving source breaking changes (Step 3), after addressing behavioral changes (Step 4). This keeps each commit focused and reviewable.",{"type":40,"tag":441,"props":442,"children":444},"h3",{"id":443},"step-1-assess-the-project",[445],{"type":46,"value":446},"Step 1: Assess the project",{"type":40,"tag":448,"props":449,"children":450},"ol",{},[451,502,514,559,571,584,596],{"type":40,"tag":74,"props":452,"children":453},{},[454,456,461,462,467,468,473,474,479,481,486,487,493,494,500],{"type":46,"value":455},"Identify the current MSTest version by checking package references for ",{"type":40,"tag":80,"props":457,"children":459},{"className":458},[],[460],{"type":46,"value":101},{"type":46,"value":87},{"type":40,"tag":80,"props":463,"children":465},{"className":464},[],[466],{"type":46,"value":85},{"type":46,"value":87},{"type":40,"tag":80,"props":469,"children":471},{"className":470},[],[472],{"type":46,"value":93},{"type":46,"value":95},{"type":40,"tag":80,"props":475,"children":477},{"className":476},[],[478],{"type":46,"value":113},{"type":46,"value":480}," in ",{"type":40,"tag":80,"props":482,"children":484},{"className":483},[],[485],{"type":46,"value":246},{"type":46,"value":87},{"type":40,"tag":80,"props":488,"children":490},{"className":489},[],[491],{"type":46,"value":492},"Directory.Build.props",{"type":46,"value":95},{"type":40,"tag":80,"props":495,"children":497},{"className":496},[],[498],{"type":46,"value":499},"Directory.Packages.props",{"type":46,"value":501},".",{"type":40,"tag":74,"props":503,"children":504},{},[505,507,512],{"type":46,"value":506},"Confirm the project is on MSTest v3 (3.x). If on v1 or v2, use ",{"type":40,"tag":80,"props":508,"children":510},{"className":509},[],[511],{"type":46,"value":171},{"type":46,"value":513}," first.",{"type":40,"tag":74,"props":515,"children":516},{},[517,519,524,525,530,531,536,538,543,545,550,552,557],{"type":46,"value":518},"Check target framework(s) -- MSTest v4 drops support for .NET Core 3.1 through .NET 7. Supported target frameworks are: ",{"type":40,"tag":55,"props":520,"children":521},{},[522],{"type":46,"value":523},"net8.0",{"type":46,"value":87},{"type":40,"tag":55,"props":526,"children":527},{},[528],{"type":46,"value":529},"net9.0",{"type":46,"value":87},{"type":40,"tag":55,"props":532,"children":533},{},[534],{"type":46,"value":535},"net462",{"type":46,"value":537}," (.NET Framework 4.6.2+), ",{"type":40,"tag":55,"props":539,"children":540},{},[541],{"type":46,"value":542},"uap10.0.16299",{"type":46,"value":544}," (UWP), ",{"type":40,"tag":55,"props":546,"children":547},{},[548],{"type":46,"value":549},"net9.0-windows10.0.17763.0",{"type":46,"value":551}," (modern UWP), and ",{"type":40,"tag":55,"props":553,"children":554},{},[555],{"type":46,"value":556},"net8.0-windows10.0.18362.0",{"type":46,"value":558}," (WinUI).",{"type":40,"tag":74,"props":560,"children":561},{},[562,564,569],{"type":46,"value":563},"Check for custom ",{"type":40,"tag":80,"props":565,"children":567},{"className":566},[],[568],{"type":46,"value":136},{"type":46,"value":570}," subclasses -- these require changes in v4.",{"type":40,"tag":74,"props":572,"children":573},{},[574,576,582],{"type":46,"value":575},"Check for usages of ",{"type":40,"tag":80,"props":577,"children":579},{"className":578},[],[580],{"type":46,"value":581},"ExpectedExceptionAttribute",{"type":46,"value":583}," -- removed in v4 (deprecated since v3 with analyzer MSTEST0006).",{"type":40,"tag":74,"props":585,"children":586},{},[587,588,594],{"type":46,"value":575},{"type":40,"tag":80,"props":589,"children":591},{"className":590},[],[592],{"type":46,"value":593},"Assert.ThrowsException",{"type":46,"value":595}," (deprecated) -- removed in v4.",{"type":40,"tag":74,"props":597,"children":598},{},[599],{"type":46,"value":600},"Run a clean build to establish a baseline of existing errors\u002Fwarnings.",{"type":40,"tag":441,"props":602,"children":604},{"id":603},"step-2-update-packages-to-mstest-v4",[605],{"type":46,"value":606},"Step 2: Update packages to MSTest v4",{"type":40,"tag":49,"props":608,"children":609},{},[610],{"type":40,"tag":55,"props":611,"children":612},{},[613],{"type":46,"value":614},"If using the MSTest metapackage:",{"type":40,"tag":616,"props":617,"children":622},"pre",{"className":618,"code":619,"language":620,"meta":621,"style":621},"language-xml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CPackageReference Include=\"MSTest\" Version=\"4.1.0\" \u002F>\n","xml","",[623],{"type":40,"tag":80,"props":624,"children":625},{"__ignoreMap":621},[626],{"type":40,"tag":627,"props":628,"children":631},"span",{"class":629,"line":630},"line",1,[632],{"type":40,"tag":627,"props":633,"children":634},{},[635],{"type":46,"value":619},{"type":40,"tag":49,"props":637,"children":638},{},[639],{"type":40,"tag":55,"props":640,"children":641},{},[642],{"type":46,"value":643},"If using individual packages:",{"type":40,"tag":616,"props":645,"children":647},{"className":618,"code":646,"language":620,"meta":621,"style":621},"\u003CPackageReference Include=\"MSTest.TestFramework\" Version=\"4.1.0\" \u002F>\n\u003CPackageReference Include=\"MSTest.TestAdapter\" Version=\"4.1.0\" \u002F>\n",[648],{"type":40,"tag":80,"props":649,"children":650},{"__ignoreMap":621},[651,659],{"type":40,"tag":627,"props":652,"children":653},{"class":629,"line":630},[654],{"type":40,"tag":627,"props":655,"children":656},{},[657],{"type":46,"value":658},"\u003CPackageReference Include=\"MSTest.TestFramework\" Version=\"4.1.0\" \u002F>\n",{"type":40,"tag":627,"props":660,"children":662},{"class":629,"line":661},2,[663],{"type":40,"tag":627,"props":664,"children":665},{},[666],{"type":46,"value":667},"\u003CPackageReference Include=\"MSTest.TestAdapter\" Version=\"4.1.0\" \u002F>\n",{"type":40,"tag":49,"props":669,"children":670},{},[671],{"type":40,"tag":55,"props":672,"children":673},{},[674],{"type":46,"value":675},"If using MSTest.Sdk:",{"type":40,"tag":616,"props":677,"children":679},{"className":618,"code":678,"language":620,"meta":621,"style":621},"\u003CProject Sdk=\"MSTest.Sdk\u002F4.1.0\">\n",[680],{"type":40,"tag":80,"props":681,"children":682},{"__ignoreMap":621},[683],{"type":40,"tag":627,"props":684,"children":685},{"class":629,"line":630},[686],{"type":40,"tag":627,"props":687,"children":688},{},[689],{"type":46,"value":678},{"type":40,"tag":49,"props":691,"children":692},{},[693,695,701,703,708],{"type":46,"value":694},"Run ",{"type":40,"tag":80,"props":696,"children":698},{"className":697},[],[699],{"type":46,"value":700},"dotnet restore",{"type":46,"value":702},", then ",{"type":40,"tag":80,"props":704,"children":706},{"className":705},[],[707],{"type":46,"value":292},{"type":46,"value":709},". Collect all errors for Step 3.",{"type":40,"tag":441,"props":711,"children":713},{"id":712},"step-3-resolve-source-breaking-changes",[714],{"type":46,"value":715},"Step 3: Resolve source breaking changes",{"type":40,"tag":49,"props":717,"children":718},{},[719],{"type":46,"value":720},"Work through compilation errors systematically. Use this quick-lookup table to identify all applicable changes, then apply each fix:",{"type":40,"tag":191,"props":722,"children":723},{},[724,745],{"type":40,"tag":195,"props":725,"children":726},{},[727],{"type":40,"tag":199,"props":728,"children":729},{},[730,735,740],{"type":40,"tag":203,"props":731,"children":732},{},[733],{"type":46,"value":734},"Error \u002F Pattern in code",{"type":40,"tag":203,"props":736,"children":737},{},[738],{"type":46,"value":739},"Breaking change",{"type":40,"tag":203,"props":741,"children":742},{},[743],{"type":46,"value":744},"Fix",{"type":40,"tag":219,"props":746,"children":747},{},[748,795,827,857,898,934,963,993,1028,1056,1086],{"type":40,"tag":199,"props":749,"children":750},{},[751,769,774],{"type":40,"tag":226,"props":752,"children":753},{},[754,756,761,763],{"type":46,"value":755},"Custom ",{"type":40,"tag":80,"props":757,"children":759},{"className":758},[],[760],{"type":46,"value":136},{"type":46,"value":762}," overrides ",{"type":40,"tag":80,"props":764,"children":766},{"className":765},[],[767],{"type":46,"value":768},"Execute",{"type":40,"tag":226,"props":770,"children":771},{},[772],{"type":46,"value":773},"Execute removed",{"type":40,"tag":226,"props":775,"children":776},{},[777,779,785,787,793],{"type":46,"value":778},"Change to ",{"type":40,"tag":80,"props":780,"children":782},{"className":781},[],[783],{"type":46,"value":784},"ExecuteAsync",{"type":46,"value":786}," returning ",{"type":40,"tag":80,"props":788,"children":790},{"className":789},[],[791],{"type":46,"value":792},"Task\u003CTestResult[]>",{"type":46,"value":794}," (3.1)",{"type":40,"tag":199,"props":796,"children":797},{},[798,809,814],{"type":40,"tag":226,"props":799,"children":800},{},[801,807],{"type":40,"tag":80,"props":802,"children":804},{"className":803},[],[805],{"type":46,"value":806},"[TestMethod(\"name\")]",{"type":46,"value":808}," or custom attribute constructor",{"type":40,"tag":226,"props":810,"children":811},{},[812],{"type":46,"value":813},"CallerInfo params added",{"type":40,"tag":226,"props":815,"children":816},{},[817,819,825],{"type":46,"value":818},"Use ",{"type":40,"tag":80,"props":820,"children":822},{"className":821},[],[823],{"type":46,"value":824},"DisplayName = \"name\"",{"type":46,"value":826}," named param; propagate CallerInfo in subclasses (3.2)",{"type":40,"tag":199,"props":828,"children":829},{},[830,839,844],{"type":40,"tag":226,"props":831,"children":832},{},[833],{"type":40,"tag":80,"props":834,"children":836},{"className":835},[],[837],{"type":46,"value":838},"ClassCleanupBehavior.EndOfClass",{"type":40,"tag":226,"props":840,"children":841},{},[842],{"type":46,"value":843},"Enum removed",{"type":40,"tag":226,"props":845,"children":846},{},[847,849,855],{"type":46,"value":848},"Remove argument: just ",{"type":40,"tag":80,"props":850,"children":852},{"className":851},[],[853],{"type":46,"value":854},"[ClassCleanup]",{"type":46,"value":856}," (3.3)",{"type":40,"tag":199,"props":858,"children":859},{},[860,869,886],{"type":40,"tag":226,"props":861,"children":862},{},[863],{"type":40,"tag":80,"props":864,"children":866},{"className":865},[],[867],{"type":46,"value":868},"TestContext.Properties.Contains(\"key\")",{"type":40,"tag":226,"props":870,"children":871},{},[872,878,880],{"type":40,"tag":80,"props":873,"children":875},{"className":874},[],[876],{"type":46,"value":877},"Properties",{"type":46,"value":879}," is ",{"type":40,"tag":80,"props":881,"children":883},{"className":882},[],[884],{"type":46,"value":885},"IDictionary\u003Cstring, object>",{"type":40,"tag":226,"props":887,"children":888},{},[889,890,896],{"type":46,"value":778},{"type":40,"tag":80,"props":891,"children":893},{"className":892},[],[894],{"type":46,"value":895},"ContainsKey(\"key\")",{"type":46,"value":897}," (3.4)",{"type":40,"tag":199,"props":899,"children":900},{},[901,910,921],{"type":40,"tag":226,"props":902,"children":903},{},[904],{"type":40,"tag":80,"props":905,"children":907},{"className":906},[],[908],{"type":46,"value":909},"[Timeout(TestTimeout.Infinite)]",{"type":40,"tag":226,"props":911,"children":912},{},[913,919],{"type":40,"tag":80,"props":914,"children":916},{"className":915},[],[917],{"type":46,"value":918},"TestTimeout",{"type":46,"value":920}," enum removed",{"type":40,"tag":226,"props":922,"children":923},{},[924,926,932],{"type":46,"value":925},"Replace with ",{"type":40,"tag":80,"props":927,"children":929},{"className":928},[],[930],{"type":46,"value":931},"[Timeout(int.MaxValue)]",{"type":46,"value":933}," (3.5)",{"type":40,"tag":199,"props":935,"children":936},{},[937,946,951],{"type":40,"tag":226,"props":938,"children":939},{},[940],{"type":40,"tag":80,"props":941,"children":943},{"className":942},[],[944],{"type":46,"value":945},"TestContext.ManagedType",{"type":40,"tag":226,"props":947,"children":948},{},[949],{"type":46,"value":950},"Property removed",{"type":40,"tag":226,"props":952,"children":953},{},[954,955,961],{"type":46,"value":818},{"type":40,"tag":80,"props":956,"children":958},{"className":957},[],[959],{"type":46,"value":960},"FullyQualifiedTestClassName",{"type":46,"value":962}," (3.6)",{"type":40,"tag":199,"props":964,"children":965},{},[966,975,980],{"type":40,"tag":226,"props":967,"children":968},{},[969],{"type":40,"tag":80,"props":970,"children":972},{"className":971},[],[973],{"type":46,"value":974},"Assert.AreEqual(a, b, \"msg {0}\", arg)",{"type":40,"tag":226,"props":976,"children":977},{},[978],{"type":46,"value":979},"Message+params overloads removed",{"type":40,"tag":226,"props":981,"children":982},{},[983,985,991],{"type":46,"value":984},"Use string interpolation: ",{"type":40,"tag":80,"props":986,"children":988},{"className":987},[],[989],{"type":46,"value":990},"$\"msg {arg}\"",{"type":46,"value":992}," (3.7)",{"type":40,"tag":199,"props":994,"children":995},{},[996,1005,1010],{"type":40,"tag":226,"props":997,"children":998},{},[999],{"type":40,"tag":80,"props":1000,"children":1002},{"className":1001},[],[1003],{"type":46,"value":1004},"Assert.ThrowsException\u003CT>(...)",{"type":40,"tag":226,"props":1006,"children":1007},{},[1008],{"type":46,"value":1009},"Renamed",{"type":40,"tag":226,"props":1011,"children":1012},{},[1013,1014,1020,1021,1027],{"type":46,"value":925},{"type":40,"tag":80,"props":1015,"children":1017},{"className":1016},[],[1018],{"type":46,"value":1019},"Assert.ThrowsExactly\u003CT>(...)",{"type":46,"value":138},{"type":40,"tag":80,"props":1022,"children":1024},{"className":1023},[],[1025],{"type":46,"value":1026},"Assert.Throws\u003CT>(...)",{"type":46,"value":992},{"type":40,"tag":199,"props":1029,"children":1030},{},[1031,1040,1045],{"type":40,"tag":226,"props":1032,"children":1033},{},[1034],{"type":40,"tag":80,"props":1035,"children":1037},{"className":1036},[],[1038],{"type":46,"value":1039},"Assert.IsInstanceOfType\u003CT>(obj, out var t)",{"type":40,"tag":226,"props":1041,"children":1042},{},[1043],{"type":46,"value":1044},"Out parameter removed",{"type":40,"tag":226,"props":1046,"children":1047},{},[1048,1049,1055],{"type":46,"value":818},{"type":40,"tag":80,"props":1050,"children":1052},{"className":1051},[],[1053],{"type":46,"value":1054},"var t = Assert.IsInstanceOfType\u003CT>(obj)",{"type":46,"value":992},{"type":40,"tag":199,"props":1057,"children":1058},{},[1059,1068,1073],{"type":40,"tag":226,"props":1060,"children":1061},{},[1062],{"type":40,"tag":80,"props":1063,"children":1065},{"className":1064},[],[1066],{"type":46,"value":1067},"[ExpectedException(typeof(T))]",{"type":40,"tag":226,"props":1069,"children":1070},{},[1071],{"type":46,"value":1072},"Attribute removed",{"type":40,"tag":226,"props":1074,"children":1075},{},[1076,1078,1084],{"type":46,"value":1077},"Move assertion into test body: ",{"type":40,"tag":80,"props":1079,"children":1081},{"className":1080},[],[1082],{"type":46,"value":1083},"Assert.ThrowsExactly\u003CT>(() => ...)",{"type":46,"value":1085}," (3.8)",{"type":40,"tag":199,"props":1087,"children":1088},{},[1089,1094,1099],{"type":40,"tag":226,"props":1090,"children":1091},{},[1092],{"type":46,"value":1093},"Project targets net5.0, net6.0, or net7.0",{"type":40,"tag":226,"props":1095,"children":1096},{},[1097],{"type":46,"value":1098},"TFM dropped",{"type":40,"tag":226,"props":1100,"children":1101},{},[1102],{"type":46,"value":1103},"Change to net8.0 or net9.0 (3.9)",{"type":40,"tag":427,"props":1105,"children":1106},{},[1107],{"type":40,"tag":49,"props":1108,"children":1109},{},[1110,1115],{"type":40,"tag":55,"props":1111,"children":1112},{},[1113],{"type":46,"value":1114},"Important",{"type":46,"value":1116},": Scan the entire project for ALL patterns above before starting fixes. Multiple breaking changes often coexist in the same project.",{"type":40,"tag":1118,"props":1119,"children":1121},"h4",{"id":1120},"_31-testmethodattributeexecute-executeasync",[1122],{"type":46,"value":1123},"3.1 TestMethodAttribute.Execute -> ExecuteAsync",{"type":40,"tag":49,"props":1125,"children":1126},{},[1127,1129,1134,1136,1141,1143,1148,1150,1155,1157,1163,1165,1171],{"type":46,"value":1128},"If you have custom ",{"type":40,"tag":80,"props":1130,"children":1132},{"className":1131},[],[1133],{"type":46,"value":136},{"type":46,"value":1135}," subclasses that override ",{"type":40,"tag":80,"props":1137,"children":1139},{"className":1138},[],[1140],{"type":46,"value":768},{"type":46,"value":1142},", change to ",{"type":40,"tag":80,"props":1144,"children":1146},{"className":1145},[],[1147],{"type":46,"value":784},{"type":46,"value":1149},". This change was made because the v3 synchronous ",{"type":40,"tag":80,"props":1151,"children":1153},{"className":1152},[],[1154],{"type":46,"value":768},{"type":46,"value":1156}," API caused deadlocks when test code used ",{"type":40,"tag":80,"props":1158,"children":1160},{"className":1159},[],[1161],{"type":46,"value":1162},"async",{"type":46,"value":1164},"\u002F",{"type":40,"tag":80,"props":1166,"children":1168},{"className":1167},[],[1169],{"type":46,"value":1170},"await",{"type":46,"value":1172}," internally -- the synchronous wrapper would block the thread while the async operation needed that same thread to complete.",{"type":40,"tag":616,"props":1174,"children":1178},{"className":1175,"code":1176,"language":1177,"meta":621,"style":621},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F Before (v3)\npublic sealed class MyTestMethodAttribute : TestMethodAttribute\n{\n    public override TestResult[] Execute(ITestMethod testMethod)\n    {\n        \u002F\u002F custom logic\n        return result;\n    }\n}\n\n\u002F\u002F After (v4) -- Option A: wrap synchronous logic with Task.FromResult\npublic sealed class MyTestMethodAttribute : TestMethodAttribute\n{\n    public override Task\u003CTestResult[]> ExecuteAsync(ITestMethod testMethod)\n    {\n        \u002F\u002F custom logic (synchronous)\n        return Task.FromResult(result);\n    }\n}\n\n\u002F\u002F After (v4) -- Option B: make properly async\npublic sealed class MyTestMethodAttribute : TestMethodAttribute\n{\n    public override async Task\u003CTestResult[]> ExecuteAsync(ITestMethod testMethod)\n    {\n        \u002F\u002F custom async logic\n        return await base.ExecuteAsync(testMethod);\n    }\n}\n","csharp",[1179],{"type":40,"tag":80,"props":1180,"children":1181},{"__ignoreMap":621},[1182,1190,1198,1207,1216,1225,1234,1243,1252,1261,1271,1280,1288,1296,1305,1313,1322,1331,1339,1347,1355,1364,1372,1380,1389,1397,1406,1415,1423],{"type":40,"tag":627,"props":1183,"children":1184},{"class":629,"line":630},[1185],{"type":40,"tag":627,"props":1186,"children":1187},{},[1188],{"type":46,"value":1189},"\u002F\u002F Before (v3)\n",{"type":40,"tag":627,"props":1191,"children":1192},{"class":629,"line":661},[1193],{"type":40,"tag":627,"props":1194,"children":1195},{},[1196],{"type":46,"value":1197},"public sealed class MyTestMethodAttribute : TestMethodAttribute\n",{"type":40,"tag":627,"props":1199,"children":1201},{"class":629,"line":1200},3,[1202],{"type":40,"tag":627,"props":1203,"children":1204},{},[1205],{"type":46,"value":1206},"{\n",{"type":40,"tag":627,"props":1208,"children":1210},{"class":629,"line":1209},4,[1211],{"type":40,"tag":627,"props":1212,"children":1213},{},[1214],{"type":46,"value":1215},"    public override TestResult[] Execute(ITestMethod testMethod)\n",{"type":40,"tag":627,"props":1217,"children":1219},{"class":629,"line":1218},5,[1220],{"type":40,"tag":627,"props":1221,"children":1222},{},[1223],{"type":46,"value":1224},"    {\n",{"type":40,"tag":627,"props":1226,"children":1228},{"class":629,"line":1227},6,[1229],{"type":40,"tag":627,"props":1230,"children":1231},{},[1232],{"type":46,"value":1233},"        \u002F\u002F custom logic\n",{"type":40,"tag":627,"props":1235,"children":1237},{"class":629,"line":1236},7,[1238],{"type":40,"tag":627,"props":1239,"children":1240},{},[1241],{"type":46,"value":1242},"        return result;\n",{"type":40,"tag":627,"props":1244,"children":1246},{"class":629,"line":1245},8,[1247],{"type":40,"tag":627,"props":1248,"children":1249},{},[1250],{"type":46,"value":1251},"    }\n",{"type":40,"tag":627,"props":1253,"children":1255},{"class":629,"line":1254},9,[1256],{"type":40,"tag":627,"props":1257,"children":1258},{},[1259],{"type":46,"value":1260},"}\n",{"type":40,"tag":627,"props":1262,"children":1264},{"class":629,"line":1263},10,[1265],{"type":40,"tag":627,"props":1266,"children":1268},{"emptyLinePlaceholder":1267},true,[1269],{"type":46,"value":1270},"\n",{"type":40,"tag":627,"props":1272,"children":1274},{"class":629,"line":1273},11,[1275],{"type":40,"tag":627,"props":1276,"children":1277},{},[1278],{"type":46,"value":1279},"\u002F\u002F After (v4) -- Option A: wrap synchronous logic with Task.FromResult\n",{"type":40,"tag":627,"props":1281,"children":1283},{"class":629,"line":1282},12,[1284],{"type":40,"tag":627,"props":1285,"children":1286},{},[1287],{"type":46,"value":1197},{"type":40,"tag":627,"props":1289,"children":1291},{"class":629,"line":1290},13,[1292],{"type":40,"tag":627,"props":1293,"children":1294},{},[1295],{"type":46,"value":1206},{"type":40,"tag":627,"props":1297,"children":1299},{"class":629,"line":1298},14,[1300],{"type":40,"tag":627,"props":1301,"children":1302},{},[1303],{"type":46,"value":1304},"    public override Task\u003CTestResult[]> ExecuteAsync(ITestMethod testMethod)\n",{"type":40,"tag":627,"props":1306,"children":1308},{"class":629,"line":1307},15,[1309],{"type":40,"tag":627,"props":1310,"children":1311},{},[1312],{"type":46,"value":1224},{"type":40,"tag":627,"props":1314,"children":1316},{"class":629,"line":1315},16,[1317],{"type":40,"tag":627,"props":1318,"children":1319},{},[1320],{"type":46,"value":1321},"        \u002F\u002F custom logic (synchronous)\n",{"type":40,"tag":627,"props":1323,"children":1325},{"class":629,"line":1324},17,[1326],{"type":40,"tag":627,"props":1327,"children":1328},{},[1329],{"type":46,"value":1330},"        return Task.FromResult(result);\n",{"type":40,"tag":627,"props":1332,"children":1334},{"class":629,"line":1333},18,[1335],{"type":40,"tag":627,"props":1336,"children":1337},{},[1338],{"type":46,"value":1251},{"type":40,"tag":627,"props":1340,"children":1342},{"class":629,"line":1341},19,[1343],{"type":40,"tag":627,"props":1344,"children":1345},{},[1346],{"type":46,"value":1260},{"type":40,"tag":627,"props":1348,"children":1350},{"class":629,"line":1349},20,[1351],{"type":40,"tag":627,"props":1352,"children":1353},{"emptyLinePlaceholder":1267},[1354],{"type":46,"value":1270},{"type":40,"tag":627,"props":1356,"children":1358},{"class":629,"line":1357},21,[1359],{"type":40,"tag":627,"props":1360,"children":1361},{},[1362],{"type":46,"value":1363},"\u002F\u002F After (v4) -- Option B: make properly async\n",{"type":40,"tag":627,"props":1365,"children":1367},{"class":629,"line":1366},22,[1368],{"type":40,"tag":627,"props":1369,"children":1370},{},[1371],{"type":46,"value":1197},{"type":40,"tag":627,"props":1373,"children":1375},{"class":629,"line":1374},23,[1376],{"type":40,"tag":627,"props":1377,"children":1378},{},[1379],{"type":46,"value":1206},{"type":40,"tag":627,"props":1381,"children":1383},{"class":629,"line":1382},24,[1384],{"type":40,"tag":627,"props":1385,"children":1386},{},[1387],{"type":46,"value":1388},"    public override async Task\u003CTestResult[]> ExecuteAsync(ITestMethod testMethod)\n",{"type":40,"tag":627,"props":1390,"children":1392},{"class":629,"line":1391},25,[1393],{"type":40,"tag":627,"props":1394,"children":1395},{},[1396],{"type":46,"value":1224},{"type":40,"tag":627,"props":1398,"children":1400},{"class":629,"line":1399},26,[1401],{"type":40,"tag":627,"props":1402,"children":1403},{},[1404],{"type":46,"value":1405},"        \u002F\u002F custom async logic\n",{"type":40,"tag":627,"props":1407,"children":1409},{"class":629,"line":1408},27,[1410],{"type":40,"tag":627,"props":1411,"children":1412},{},[1413],{"type":46,"value":1414},"        return await base.ExecuteAsync(testMethod);\n",{"type":40,"tag":627,"props":1416,"children":1418},{"class":629,"line":1417},28,[1419],{"type":40,"tag":627,"props":1420,"children":1421},{},[1422],{"type":46,"value":1251},{"type":40,"tag":627,"props":1424,"children":1426},{"class":629,"line":1425},29,[1427],{"type":40,"tag":627,"props":1428,"children":1429},{},[1430],{"type":46,"value":1260},{"type":40,"tag":49,"props":1432,"children":1433},{},[1434,1435,1441,1443,1448,1449,1454,1456,1462],{"type":46,"value":818},{"type":40,"tag":80,"props":1436,"children":1438},{"className":1437},[],[1439],{"type":46,"value":1440},"Task.FromResult",{"type":46,"value":1442}," when your override logic is purely synchronous. Use ",{"type":40,"tag":80,"props":1444,"children":1446},{"className":1445},[],[1447],{"type":46,"value":1162},{"type":46,"value":1164},{"type":40,"tag":80,"props":1450,"children":1452},{"className":1451},[],[1453],{"type":46,"value":1170},{"type":46,"value":1455}," when you call ",{"type":40,"tag":80,"props":1457,"children":1459},{"className":1458},[],[1460],{"type":46,"value":1461},"base.ExecuteAsync",{"type":46,"value":1463}," or other async methods.",{"type":40,"tag":1118,"props":1465,"children":1467},{"id":1466},"_32-testmethodattribute-callerinfo-constructor",[1468],{"type":46,"value":1469},"3.2 TestMethodAttribute CallerInfo constructor",{"type":40,"tag":49,"props":1471,"children":1472},{},[1473,1478,1480,1486,1488,1494],{"type":40,"tag":80,"props":1474,"children":1476},{"className":1475},[],[1477],{"type":46,"value":136},{"type":46,"value":1479}," now uses ",{"type":40,"tag":80,"props":1481,"children":1483},{"className":1482},[],[1484],{"type":46,"value":1485},"[CallerFilePath]",{"type":46,"value":1487}," and ",{"type":40,"tag":80,"props":1489,"children":1491},{"className":1490},[],[1492],{"type":46,"value":1493},"[CallerLineNumber]",{"type":46,"value":1495}," parameters in its constructor.",{"type":40,"tag":49,"props":1497,"children":1498},{},[1499,1504],{"type":40,"tag":55,"props":1500,"children":1501},{},[1502],{"type":46,"value":1503},"If you inherit from TestMethodAttribute",{"type":46,"value":1505},", propagate caller info to the base class:",{"type":40,"tag":616,"props":1507,"children":1509},{"className":1175,"code":1508,"language":1177,"meta":621,"style":621},"public class MyTestMethodAttribute : TestMethodAttribute\n{\n    public MyTestMethodAttribute(\n        [CallerFilePath] string callerFilePath = \"\",\n        [CallerLineNumber] int callerLineNumber = -1)\n        : base(callerFilePath, callerLineNumber)\n    {\n    }\n}\n",[1510],{"type":40,"tag":80,"props":1511,"children":1512},{"__ignoreMap":621},[1513,1521,1528,1536,1544,1552,1560,1567,1574],{"type":40,"tag":627,"props":1514,"children":1515},{"class":629,"line":630},[1516],{"type":40,"tag":627,"props":1517,"children":1518},{},[1519],{"type":46,"value":1520},"public class MyTestMethodAttribute : TestMethodAttribute\n",{"type":40,"tag":627,"props":1522,"children":1523},{"class":629,"line":661},[1524],{"type":40,"tag":627,"props":1525,"children":1526},{},[1527],{"type":46,"value":1206},{"type":40,"tag":627,"props":1529,"children":1530},{"class":629,"line":1200},[1531],{"type":40,"tag":627,"props":1532,"children":1533},{},[1534],{"type":46,"value":1535},"    public MyTestMethodAttribute(\n",{"type":40,"tag":627,"props":1537,"children":1538},{"class":629,"line":1209},[1539],{"type":40,"tag":627,"props":1540,"children":1541},{},[1542],{"type":46,"value":1543},"        [CallerFilePath] string callerFilePath = \"\",\n",{"type":40,"tag":627,"props":1545,"children":1546},{"class":629,"line":1218},[1547],{"type":40,"tag":627,"props":1548,"children":1549},{},[1550],{"type":46,"value":1551},"        [CallerLineNumber] int callerLineNumber = -1)\n",{"type":40,"tag":627,"props":1553,"children":1554},{"class":629,"line":1227},[1555],{"type":40,"tag":627,"props":1556,"children":1557},{},[1558],{"type":46,"value":1559},"        : base(callerFilePath, callerLineNumber)\n",{"type":40,"tag":627,"props":1561,"children":1562},{"class":629,"line":1236},[1563],{"type":40,"tag":627,"props":1564,"children":1565},{},[1566],{"type":46,"value":1224},{"type":40,"tag":627,"props":1568,"children":1569},{"class":629,"line":1245},[1570],{"type":40,"tag":627,"props":1571,"children":1572},{},[1573],{"type":46,"value":1251},{"type":40,"tag":627,"props":1575,"children":1576},{"class":629,"line":1254},[1577],{"type":40,"tag":627,"props":1578,"children":1579},{},[1580],{"type":46,"value":1260},{"type":40,"tag":49,"props":1582,"children":1583},{},[1584,1595],{"type":40,"tag":55,"props":1585,"children":1586},{},[1587,1589],{"type":46,"value":1588},"If you use ",{"type":40,"tag":80,"props":1590,"children":1592},{"className":1591},[],[1593],{"type":46,"value":1594},"[TestMethodAttribute(\"Custom display name\")]",{"type":46,"value":1596},", switch to the named parameter syntax:",{"type":40,"tag":616,"props":1598,"children":1600},{"className":1175,"code":1599,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\n[TestMethodAttribute(\"Custom display name\")]\n\n\u002F\u002F After (v4)\n[TestMethodAttribute(DisplayName = \"Custom display name\")]\n",[1601],{"type":40,"tag":80,"props":1602,"children":1603},{"__ignoreMap":621},[1604,1611,1619,1626,1634],{"type":40,"tag":627,"props":1605,"children":1606},{"class":629,"line":630},[1607],{"type":40,"tag":627,"props":1608,"children":1609},{},[1610],{"type":46,"value":1189},{"type":40,"tag":627,"props":1612,"children":1613},{"class":629,"line":661},[1614],{"type":40,"tag":627,"props":1615,"children":1616},{},[1617],{"type":46,"value":1618},"[TestMethodAttribute(\"Custom display name\")]\n",{"type":40,"tag":627,"props":1620,"children":1621},{"class":629,"line":1200},[1622],{"type":40,"tag":627,"props":1623,"children":1624},{"emptyLinePlaceholder":1267},[1625],{"type":46,"value":1270},{"type":40,"tag":627,"props":1627,"children":1628},{"class":629,"line":1209},[1629],{"type":40,"tag":627,"props":1630,"children":1631},{},[1632],{"type":46,"value":1633},"\u002F\u002F After (v4)\n",{"type":40,"tag":627,"props":1635,"children":1636},{"class":629,"line":1218},[1637],{"type":40,"tag":627,"props":1638,"children":1639},{},[1640],{"type":46,"value":1641},"[TestMethodAttribute(DisplayName = \"Custom display name\")]\n",{"type":40,"tag":1118,"props":1643,"children":1645},{"id":1644},"_33-classcleanupbehavior-enum-removed",[1646],{"type":46,"value":1647},"3.3 ClassCleanupBehavior enum removed",{"type":40,"tag":49,"props":1649,"children":1650},{},[1651,1652,1658,1660,1666,1668,1674],{"type":46,"value":240},{"type":40,"tag":80,"props":1653,"children":1655},{"className":1654},[],[1656],{"type":46,"value":1657},"ClassCleanupBehavior",{"type":46,"value":1659}," enum is removed. In v3, this enum controlled whether class cleanup ran at end of class (",{"type":40,"tag":80,"props":1661,"children":1663},{"className":1662},[],[1664],{"type":46,"value":1665},"EndOfClass",{"type":46,"value":1667},") or end of assembly (",{"type":40,"tag":80,"props":1669,"children":1671},{"className":1670},[],[1672],{"type":46,"value":1673},"EndOfAssembly",{"type":46,"value":1675},"). In v4, class cleanup always runs at end of class. Remove the enum argument:",{"type":40,"tag":616,"props":1677,"children":1679},{"className":1175,"code":1678,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\n[ClassCleanup(ClassCleanupBehavior.EndOfClass)]\npublic static void ClassCleanup(TestContext testContext) { }\n\n\u002F\u002F After (v4)\n[ClassCleanup]\npublic static void ClassCleanup(TestContext testContext) { }\n",[1680],{"type":40,"tag":80,"props":1681,"children":1682},{"__ignoreMap":621},[1683,1690,1698,1706,1713,1720,1728],{"type":40,"tag":627,"props":1684,"children":1685},{"class":629,"line":630},[1686],{"type":40,"tag":627,"props":1687,"children":1688},{},[1689],{"type":46,"value":1189},{"type":40,"tag":627,"props":1691,"children":1692},{"class":629,"line":661},[1693],{"type":40,"tag":627,"props":1694,"children":1695},{},[1696],{"type":46,"value":1697},"[ClassCleanup(ClassCleanupBehavior.EndOfClass)]\n",{"type":40,"tag":627,"props":1699,"children":1700},{"class":629,"line":1200},[1701],{"type":40,"tag":627,"props":1702,"children":1703},{},[1704],{"type":46,"value":1705},"public static void ClassCleanup(TestContext testContext) { }\n",{"type":40,"tag":627,"props":1707,"children":1708},{"class":629,"line":1209},[1709],{"type":40,"tag":627,"props":1710,"children":1711},{"emptyLinePlaceholder":1267},[1712],{"type":46,"value":1270},{"type":40,"tag":627,"props":1714,"children":1715},{"class":629,"line":1218},[1716],{"type":40,"tag":627,"props":1717,"children":1718},{},[1719],{"type":46,"value":1633},{"type":40,"tag":627,"props":1721,"children":1722},{"class":629,"line":1227},[1723],{"type":40,"tag":627,"props":1724,"children":1725},{},[1726],{"type":46,"value":1727},"[ClassCleanup]\n",{"type":40,"tag":627,"props":1729,"children":1730},{"class":629,"line":1236},[1731],{"type":40,"tag":627,"props":1732,"children":1733},{},[1734],{"type":46,"value":1705},{"type":40,"tag":49,"props":1736,"children":1737},{},[1738,1740,1746,1748,1754],{"type":46,"value":1739},"If you previously used ",{"type":40,"tag":80,"props":1741,"children":1743},{"className":1742},[],[1744],{"type":46,"value":1745},"ClassCleanupBehavior.EndOfAssembly",{"type":46,"value":1747},", move that cleanup logic to an ",{"type":40,"tag":80,"props":1749,"children":1751},{"className":1750},[],[1752],{"type":46,"value":1753},"[AssemblyCleanup]",{"type":46,"value":1755}," method instead.",{"type":40,"tag":1118,"props":1757,"children":1759},{"id":1758},"_34-testcontextproperties-type-change",[1760],{"type":46,"value":1761},"3.4 TestContext.Properties type change",{"type":40,"tag":49,"props":1763,"children":1764},{},[1765,1771,1773,1779,1781,1786,1788,1794,1796,1802],{"type":40,"tag":80,"props":1766,"children":1768},{"className":1767},[],[1769],{"type":46,"value":1770},"TestContext.Properties",{"type":46,"value":1772}," changed from ",{"type":40,"tag":80,"props":1774,"children":1776},{"className":1775},[],[1777],{"type":46,"value":1778},"IDictionary",{"type":46,"value":1780}," to ",{"type":40,"tag":80,"props":1782,"children":1784},{"className":1783},[],[1785],{"type":46,"value":885},{"type":46,"value":1787},". Update any ",{"type":40,"tag":80,"props":1789,"children":1791},{"className":1790},[],[1792],{"type":46,"value":1793},"Contains",{"type":46,"value":1795}," calls to ",{"type":40,"tag":80,"props":1797,"children":1799},{"className":1798},[],[1800],{"type":46,"value":1801},"ContainsKey",{"type":46,"value":1803},":",{"type":40,"tag":616,"props":1805,"children":1807},{"className":1175,"code":1806,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\ntestContext.Properties.Contains(\"key\");\n\n\u002F\u002F After (v4)\ntestContext.Properties.ContainsKey(\"key\");\n",[1808],{"type":40,"tag":80,"props":1809,"children":1810},{"__ignoreMap":621},[1811,1818,1826,1833,1840],{"type":40,"tag":627,"props":1812,"children":1813},{"class":629,"line":630},[1814],{"type":40,"tag":627,"props":1815,"children":1816},{},[1817],{"type":46,"value":1189},{"type":40,"tag":627,"props":1819,"children":1820},{"class":629,"line":661},[1821],{"type":40,"tag":627,"props":1822,"children":1823},{},[1824],{"type":46,"value":1825},"testContext.Properties.Contains(\"key\");\n",{"type":40,"tag":627,"props":1827,"children":1828},{"class":629,"line":1200},[1829],{"type":40,"tag":627,"props":1830,"children":1831},{"emptyLinePlaceholder":1267},[1832],{"type":46,"value":1270},{"type":40,"tag":627,"props":1834,"children":1835},{"class":629,"line":1209},[1836],{"type":40,"tag":627,"props":1837,"children":1838},{},[1839],{"type":46,"value":1633},{"type":40,"tag":627,"props":1841,"children":1842},{"class":629,"line":1218},[1843],{"type":40,"tag":627,"props":1844,"children":1845},{},[1846],{"type":46,"value":1847},"testContext.Properties.ContainsKey(\"key\");\n",{"type":40,"tag":1118,"props":1849,"children":1851},{"id":1850},"_35-testtimeout-enum-removed",[1852],{"type":46,"value":1853},"3.5 TestTimeout enum removed",{"type":40,"tag":49,"props":1855,"children":1856},{},[1857,1858,1863,1865,1871,1873,1879],{"type":46,"value":240},{"type":40,"tag":80,"props":1859,"children":1861},{"className":1860},[],[1862],{"type":46,"value":918},{"type":46,"value":1864}," enum (with only ",{"type":40,"tag":80,"props":1866,"children":1868},{"className":1867},[],[1869],{"type":46,"value":1870},"TestTimeout.Infinite",{"type":46,"value":1872},") is removed. Replace with ",{"type":40,"tag":80,"props":1874,"children":1876},{"className":1875},[],[1877],{"type":46,"value":1878},"int.MaxValue",{"type":46,"value":1803},{"type":40,"tag":616,"props":1881,"children":1883},{"className":1175,"code":1882,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\n[Timeout(TestTimeout.Infinite)]\n\n\u002F\u002F After (v4)\n[Timeout(int.MaxValue)]\n",[1884],{"type":40,"tag":80,"props":1885,"children":1886},{"__ignoreMap":621},[1887,1894,1902,1909,1916],{"type":40,"tag":627,"props":1888,"children":1889},{"class":629,"line":630},[1890],{"type":40,"tag":627,"props":1891,"children":1892},{},[1893],{"type":46,"value":1189},{"type":40,"tag":627,"props":1895,"children":1896},{"class":629,"line":661},[1897],{"type":40,"tag":627,"props":1898,"children":1899},{},[1900],{"type":46,"value":1901},"[Timeout(TestTimeout.Infinite)]\n",{"type":40,"tag":627,"props":1903,"children":1904},{"class":629,"line":1200},[1905],{"type":40,"tag":627,"props":1906,"children":1907},{"emptyLinePlaceholder":1267},[1908],{"type":46,"value":1270},{"type":40,"tag":627,"props":1910,"children":1911},{"class":629,"line":1209},[1912],{"type":40,"tag":627,"props":1913,"children":1914},{},[1915],{"type":46,"value":1633},{"type":40,"tag":627,"props":1917,"children":1918},{"class":629,"line":1218},[1919],{"type":40,"tag":627,"props":1920,"children":1921},{},[1922],{"type":46,"value":1923},"[Timeout(int.MaxValue)]\n",{"type":40,"tag":1118,"props":1925,"children":1927},{"id":1926},"_36-testcontextmanagedtype-removed",[1928],{"type":46,"value":1929},"3.6 TestContext.ManagedType removed",{"type":40,"tag":49,"props":1931,"children":1932},{},[1933,1934,1939,1941,1947],{"type":46,"value":240},{"type":40,"tag":80,"props":1935,"children":1937},{"className":1936},[],[1938],{"type":46,"value":945},{"type":46,"value":1940}," property is removed. Use ",{"type":40,"tag":80,"props":1942,"children":1944},{"className":1943},[],[1945],{"type":46,"value":1946},"TestContext.FullyQualifiedTestClassName",{"type":46,"value":1948}," instead.",{"type":40,"tag":1118,"props":1950,"children":1952},{"id":1951},"_37-assert-api-signature-changes",[1953],{"type":46,"value":1954},"3.7 Assert API signature changes",{"type":40,"tag":70,"props":1956,"children":1957},{},[1958],{"type":40,"tag":74,"props":1959,"children":1960},{},[1961,1966,1968,1974,1975,1981,1983,1988],{"type":40,"tag":55,"props":1962,"children":1963},{},[1964],{"type":46,"value":1965},"Message + params removed",{"type":46,"value":1967},": Assert methods that accepted both ",{"type":40,"tag":80,"props":1969,"children":1971},{"className":1970},[],[1972],{"type":46,"value":1973},"message",{"type":46,"value":1487},{"type":40,"tag":80,"props":1976,"children":1978},{"className":1977},[],[1979],{"type":46,"value":1980},"object[]",{"type":46,"value":1982}," parameters now accept only ",{"type":40,"tag":80,"props":1984,"children":1986},{"className":1985},[],[1987],{"type":46,"value":1973},{"type":46,"value":1989},". Use string interpolation instead of format strings:",{"type":40,"tag":616,"props":1991,"children":1993},{"className":1175,"code":1992,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\nAssert.AreEqual(expected, actual, \"Expected {0} but got {1}\", expected, actual);\n\n\u002F\u002F After (v4)\nAssert.AreEqual(expected, actual, $\"Expected {expected} but got {actual}\");\n",[1994],{"type":40,"tag":80,"props":1995,"children":1996},{"__ignoreMap":621},[1997,2004,2012,2019,2026],{"type":40,"tag":627,"props":1998,"children":1999},{"class":629,"line":630},[2000],{"type":40,"tag":627,"props":2001,"children":2002},{},[2003],{"type":46,"value":1189},{"type":40,"tag":627,"props":2005,"children":2006},{"class":629,"line":661},[2007],{"type":40,"tag":627,"props":2008,"children":2009},{},[2010],{"type":46,"value":2011},"Assert.AreEqual(expected, actual, \"Expected {0} but got {1}\", expected, actual);\n",{"type":40,"tag":627,"props":2013,"children":2014},{"class":629,"line":1200},[2015],{"type":40,"tag":627,"props":2016,"children":2017},{"emptyLinePlaceholder":1267},[2018],{"type":46,"value":1270},{"type":40,"tag":627,"props":2020,"children":2021},{"class":629,"line":1209},[2022],{"type":40,"tag":627,"props":2023,"children":2024},{},[2025],{"type":46,"value":1633},{"type":40,"tag":627,"props":2027,"children":2028},{"class":629,"line":1218},[2029],{"type":40,"tag":627,"props":2030,"children":2031},{},[2032],{"type":46,"value":2033},"Assert.AreEqual(expected, actual, $\"Expected {expected} but got {actual}\");\n",{"type":40,"tag":70,"props":2035,"children":2036},{},[2037],{"type":40,"tag":74,"props":2038,"children":2039},{},[2040,2045,2047,2052,2054,2060,2062,2068],{"type":40,"tag":55,"props":2041,"children":2042},{},[2043],{"type":46,"value":2044},"Assert.ThrowsException renamed",{"type":46,"value":2046},": The ",{"type":40,"tag":80,"props":2048,"children":2050},{"className":2049},[],[2051],{"type":46,"value":593},{"type":46,"value":2053}," APIs are renamed. Use ",{"type":40,"tag":80,"props":2055,"children":2057},{"className":2056},[],[2058],{"type":46,"value":2059},"Assert.ThrowsExactly",{"type":46,"value":2061}," (strict type match) or ",{"type":40,"tag":80,"props":2063,"children":2065},{"className":2064},[],[2066],{"type":46,"value":2067},"Assert.Throws",{"type":46,"value":2069}," (accepts derived exception types):",{"type":40,"tag":616,"props":2071,"children":2073},{"className":1175,"code":2072,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\nAssert.ThrowsException\u003CInvalidOperationException>(() => DoSomething());\n\n\u002F\u002F After (v4) -- exact type match (same behavior as old ThrowsException)\nAssert.ThrowsExactly\u003CInvalidOperationException>(() => DoSomething());\n\n\u002F\u002F After (v4) -- also catches derived exception types\nAssert.Throws\u003CInvalidOperationException>(() => DoSomething());\n",[2074],{"type":40,"tag":80,"props":2075,"children":2076},{"__ignoreMap":621},[2077,2084,2092,2099,2107,2115,2122,2130],{"type":40,"tag":627,"props":2078,"children":2079},{"class":629,"line":630},[2080],{"type":40,"tag":627,"props":2081,"children":2082},{},[2083],{"type":46,"value":1189},{"type":40,"tag":627,"props":2085,"children":2086},{"class":629,"line":661},[2087],{"type":40,"tag":627,"props":2088,"children":2089},{},[2090],{"type":46,"value":2091},"Assert.ThrowsException\u003CInvalidOperationException>(() => DoSomething());\n",{"type":40,"tag":627,"props":2093,"children":2094},{"class":629,"line":1200},[2095],{"type":40,"tag":627,"props":2096,"children":2097},{"emptyLinePlaceholder":1267},[2098],{"type":46,"value":1270},{"type":40,"tag":627,"props":2100,"children":2101},{"class":629,"line":1209},[2102],{"type":40,"tag":627,"props":2103,"children":2104},{},[2105],{"type":46,"value":2106},"\u002F\u002F After (v4) -- exact type match (same behavior as old ThrowsException)\n",{"type":40,"tag":627,"props":2108,"children":2109},{"class":629,"line":1218},[2110],{"type":40,"tag":627,"props":2111,"children":2112},{},[2113],{"type":46,"value":2114},"Assert.ThrowsExactly\u003CInvalidOperationException>(() => DoSomething());\n",{"type":40,"tag":627,"props":2116,"children":2117},{"class":629,"line":1227},[2118],{"type":40,"tag":627,"props":2119,"children":2120},{"emptyLinePlaceholder":1267},[2121],{"type":46,"value":1270},{"type":40,"tag":627,"props":2123,"children":2124},{"class":629,"line":1236},[2125],{"type":40,"tag":627,"props":2126,"children":2127},{},[2128],{"type":46,"value":2129},"\u002F\u002F After (v4) -- also catches derived exception types\n",{"type":40,"tag":627,"props":2131,"children":2132},{"class":629,"line":1245},[2133],{"type":40,"tag":627,"props":2134,"children":2135},{},[2136],{"type":46,"value":2137},"Assert.Throws\u003CInvalidOperationException>(() => DoSomething());\n",{"type":40,"tag":70,"props":2139,"children":2140},{},[2141],{"type":40,"tag":74,"props":2142,"children":2143},{},[2144,2149,2151,2157,2159,2165],{"type":40,"tag":55,"props":2145,"children":2146},{},[2147],{"type":46,"value":2148},"Assert.IsInstanceOfType out parameter changed",{"type":46,"value":2150},": ",{"type":40,"tag":80,"props":2152,"children":2154},{"className":2153},[],[2155],{"type":46,"value":2156},"Assert.IsInstanceOfType\u003CT>(x, out var t)",{"type":46,"value":2158}," changes to ",{"type":40,"tag":80,"props":2160,"children":2162},{"className":2161},[],[2163],{"type":46,"value":2164},"var t = Assert.IsInstanceOfType\u003CT>(x)",{"type":46,"value":1803},{"type":40,"tag":616,"props":2167,"children":2169},{"className":1175,"code":2168,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\nAssert.IsInstanceOfType\u003CMyType>(obj, out var typed);\n\n\u002F\u002F After (v4)\nvar typed = Assert.IsInstanceOfType\u003CMyType>(obj);\n",[2170],{"type":40,"tag":80,"props":2171,"children":2172},{"__ignoreMap":621},[2173,2180,2188,2195,2202],{"type":40,"tag":627,"props":2174,"children":2175},{"class":629,"line":630},[2176],{"type":40,"tag":627,"props":2177,"children":2178},{},[2179],{"type":46,"value":1189},{"type":40,"tag":627,"props":2181,"children":2182},{"class":629,"line":661},[2183],{"type":40,"tag":627,"props":2184,"children":2185},{},[2186],{"type":46,"value":2187},"Assert.IsInstanceOfType\u003CMyType>(obj, out var typed);\n",{"type":40,"tag":627,"props":2189,"children":2190},{"class":629,"line":1200},[2191],{"type":40,"tag":627,"props":2192,"children":2193},{"emptyLinePlaceholder":1267},[2194],{"type":46,"value":1270},{"type":40,"tag":627,"props":2196,"children":2197},{"class":629,"line":1209},[2198],{"type":40,"tag":627,"props":2199,"children":2200},{},[2201],{"type":46,"value":1633},{"type":40,"tag":627,"props":2203,"children":2204},{"class":629,"line":1218},[2205],{"type":40,"tag":627,"props":2206,"children":2207},{},[2208],{"type":46,"value":2209},"var typed = Assert.IsInstanceOfType\u003CMyType>(obj);\n",{"type":40,"tag":70,"props":2211,"children":2212},{},[2213],{"type":40,"tag":74,"props":2214,"children":2215},{},[2216,2221,2223,2229],{"type":40,"tag":55,"props":2217,"children":2218},{},[2219],{"type":46,"value":2220},"Assert.AreEqual for IEquatable\u003CT> removed",{"type":46,"value":2222},": If you get generic type inference errors, explicitly specify the type argument as ",{"type":40,"tag":80,"props":2224,"children":2226},{"className":2225},[],[2227],{"type":46,"value":2228},"object",{"type":46,"value":501},{"type":40,"tag":1118,"props":2231,"children":2233},{"id":2232},"_38-expectedexceptionattribute-removed",[2234],{"type":46,"value":2235},"3.8 ExpectedExceptionAttribute removed",{"type":40,"tag":49,"props":2237,"children":2238},{},[2239,2240,2246,2248,2254,2256,2261,2263,2268,2270,2275],{"type":46,"value":240},{"type":40,"tag":80,"props":2241,"children":2243},{"className":2242},[],[2244],{"type":46,"value":2245},"[ExpectedException]",{"type":46,"value":2247}," attribute is removed in v4. In MSTest 3.2, the ",{"type":40,"tag":80,"props":2249,"children":2251},{"className":2250},[],[2252],{"type":46,"value":2253},"MSTEST0006",{"type":46,"value":2255}," analyzer was introduced to flag ",{"type":40,"tag":80,"props":2257,"children":2259},{"className":2258},[],[2260],{"type":46,"value":2245},{"type":46,"value":2262}," usage and suggest migrating to ",{"type":40,"tag":80,"props":2264,"children":2266},{"className":2265},[],[2267],{"type":46,"value":2059},{"type":46,"value":2269}," while still on v3 (a non-breaking change). In v4, the attribute is gone entirely. Migrate to ",{"type":40,"tag":80,"props":2271,"children":2273},{"className":2272},[],[2274],{"type":46,"value":2059},{"type":46,"value":1803},{"type":40,"tag":616,"props":2277,"children":2279},{"className":1175,"code":2278,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\n[ExpectedException(typeof(InvalidOperationException))]\n[TestMethod]\npublic void TestMethod()\n{\n    MyCall();\n}\n\n\u002F\u002F After (v4)\n[TestMethod]\npublic void TestMethod()\n{\n    Assert.ThrowsExactly\u003CInvalidOperationException>(() => MyCall());\n}\n",[2280],{"type":40,"tag":80,"props":2281,"children":2282},{"__ignoreMap":621},[2283,2290,2298,2306,2314,2321,2329,2336,2343,2350,2357,2364,2371,2379],{"type":40,"tag":627,"props":2284,"children":2285},{"class":629,"line":630},[2286],{"type":40,"tag":627,"props":2287,"children":2288},{},[2289],{"type":46,"value":1189},{"type":40,"tag":627,"props":2291,"children":2292},{"class":629,"line":661},[2293],{"type":40,"tag":627,"props":2294,"children":2295},{},[2296],{"type":46,"value":2297},"[ExpectedException(typeof(InvalidOperationException))]\n",{"type":40,"tag":627,"props":2299,"children":2300},{"class":629,"line":1200},[2301],{"type":40,"tag":627,"props":2302,"children":2303},{},[2304],{"type":46,"value":2305},"[TestMethod]\n",{"type":40,"tag":627,"props":2307,"children":2308},{"class":629,"line":1209},[2309],{"type":40,"tag":627,"props":2310,"children":2311},{},[2312],{"type":46,"value":2313},"public void TestMethod()\n",{"type":40,"tag":627,"props":2315,"children":2316},{"class":629,"line":1218},[2317],{"type":40,"tag":627,"props":2318,"children":2319},{},[2320],{"type":46,"value":1206},{"type":40,"tag":627,"props":2322,"children":2323},{"class":629,"line":1227},[2324],{"type":40,"tag":627,"props":2325,"children":2326},{},[2327],{"type":46,"value":2328},"    MyCall();\n",{"type":40,"tag":627,"props":2330,"children":2331},{"class":629,"line":1236},[2332],{"type":40,"tag":627,"props":2333,"children":2334},{},[2335],{"type":46,"value":1260},{"type":40,"tag":627,"props":2337,"children":2338},{"class":629,"line":1245},[2339],{"type":40,"tag":627,"props":2340,"children":2341},{"emptyLinePlaceholder":1267},[2342],{"type":46,"value":1270},{"type":40,"tag":627,"props":2344,"children":2345},{"class":629,"line":1254},[2346],{"type":40,"tag":627,"props":2347,"children":2348},{},[2349],{"type":46,"value":1633},{"type":40,"tag":627,"props":2351,"children":2352},{"class":629,"line":1263},[2353],{"type":40,"tag":627,"props":2354,"children":2355},{},[2356],{"type":46,"value":2305},{"type":40,"tag":627,"props":2358,"children":2359},{"class":629,"line":1273},[2360],{"type":40,"tag":627,"props":2361,"children":2362},{},[2363],{"type":46,"value":2313},{"type":40,"tag":627,"props":2365,"children":2366},{"class":629,"line":1282},[2367],{"type":40,"tag":627,"props":2368,"children":2369},{},[2370],{"type":46,"value":1206},{"type":40,"tag":627,"props":2372,"children":2373},{"class":629,"line":1290},[2374],{"type":40,"tag":627,"props":2375,"children":2376},{},[2377],{"type":46,"value":2378},"    Assert.ThrowsExactly\u003CInvalidOperationException>(() => MyCall());\n",{"type":40,"tag":627,"props":2380,"children":2381},{"class":629,"line":1298},[2382],{"type":40,"tag":627,"props":2383,"children":2384},{},[2385],{"type":46,"value":1260},{"type":40,"tag":49,"props":2387,"children":2388},{},[2389,2394],{"type":40,"tag":55,"props":2390,"children":2391},{},[2392],{"type":46,"value":2393},"When the test has setup code before the throwing call",{"type":46,"value":2395},", wrap only the throwing call in the lambda -- keep Arrange\u002FAct separation clear:",{"type":40,"tag":616,"props":2397,"children":2399},{"className":1175,"code":2398,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\n[ExpectedException(typeof(ArgumentNullException))]\n[TestMethod]\npublic void Validate_NullInput_Throws()\n{\n    var service = new ValidationService();\n    service.Validate(null);  \u002F\u002F throws here\n}\n\n\u002F\u002F After (v4)\n[TestMethod]\npublic void Validate_NullInput_Throws()\n{\n    var service = new ValidationService();\n    Assert.ThrowsExactly\u003CArgumentNullException>(() => service.Validate(null));\n}\n",[2400],{"type":40,"tag":80,"props":2401,"children":2402},{"__ignoreMap":621},[2403,2410,2418,2425,2433,2440,2448,2456,2463,2470,2477,2484,2491,2498,2505,2513],{"type":40,"tag":627,"props":2404,"children":2405},{"class":629,"line":630},[2406],{"type":40,"tag":627,"props":2407,"children":2408},{},[2409],{"type":46,"value":1189},{"type":40,"tag":627,"props":2411,"children":2412},{"class":629,"line":661},[2413],{"type":40,"tag":627,"props":2414,"children":2415},{},[2416],{"type":46,"value":2417},"[ExpectedException(typeof(ArgumentNullException))]\n",{"type":40,"tag":627,"props":2419,"children":2420},{"class":629,"line":1200},[2421],{"type":40,"tag":627,"props":2422,"children":2423},{},[2424],{"type":46,"value":2305},{"type":40,"tag":627,"props":2426,"children":2427},{"class":629,"line":1209},[2428],{"type":40,"tag":627,"props":2429,"children":2430},{},[2431],{"type":46,"value":2432},"public void Validate_NullInput_Throws()\n",{"type":40,"tag":627,"props":2434,"children":2435},{"class":629,"line":1218},[2436],{"type":40,"tag":627,"props":2437,"children":2438},{},[2439],{"type":46,"value":1206},{"type":40,"tag":627,"props":2441,"children":2442},{"class":629,"line":1227},[2443],{"type":40,"tag":627,"props":2444,"children":2445},{},[2446],{"type":46,"value":2447},"    var service = new ValidationService();\n",{"type":40,"tag":627,"props":2449,"children":2450},{"class":629,"line":1236},[2451],{"type":40,"tag":627,"props":2452,"children":2453},{},[2454],{"type":46,"value":2455},"    service.Validate(null);  \u002F\u002F throws here\n",{"type":40,"tag":627,"props":2457,"children":2458},{"class":629,"line":1245},[2459],{"type":40,"tag":627,"props":2460,"children":2461},{},[2462],{"type":46,"value":1260},{"type":40,"tag":627,"props":2464,"children":2465},{"class":629,"line":1254},[2466],{"type":40,"tag":627,"props":2467,"children":2468},{"emptyLinePlaceholder":1267},[2469],{"type":46,"value":1270},{"type":40,"tag":627,"props":2471,"children":2472},{"class":629,"line":1263},[2473],{"type":40,"tag":627,"props":2474,"children":2475},{},[2476],{"type":46,"value":1633},{"type":40,"tag":627,"props":2478,"children":2479},{"class":629,"line":1273},[2480],{"type":40,"tag":627,"props":2481,"children":2482},{},[2483],{"type":46,"value":2305},{"type":40,"tag":627,"props":2485,"children":2486},{"class":629,"line":1282},[2487],{"type":40,"tag":627,"props":2488,"children":2489},{},[2490],{"type":46,"value":2432},{"type":40,"tag":627,"props":2492,"children":2493},{"class":629,"line":1290},[2494],{"type":40,"tag":627,"props":2495,"children":2496},{},[2497],{"type":46,"value":1206},{"type":40,"tag":627,"props":2499,"children":2500},{"class":629,"line":1298},[2501],{"type":40,"tag":627,"props":2502,"children":2503},{},[2504],{"type":46,"value":2447},{"type":40,"tag":627,"props":2506,"children":2507},{"class":629,"line":1307},[2508],{"type":40,"tag":627,"props":2509,"children":2510},{},[2511],{"type":46,"value":2512},"    Assert.ThrowsExactly\u003CArgumentNullException>(() => service.Validate(null));\n",{"type":40,"tag":627,"props":2514,"children":2515},{"class":629,"line":1315},[2516],{"type":40,"tag":627,"props":2517,"children":2518},{},[2519],{"type":46,"value":1260},{"type":40,"tag":49,"props":2521,"children":2522},{},[2523,2528,2530,2536],{"type":40,"tag":55,"props":2524,"children":2525},{},[2526],{"type":46,"value":2527},"For async test methods",{"type":46,"value":2529},", use ",{"type":40,"tag":80,"props":2531,"children":2533},{"className":2532},[],[2534],{"type":46,"value":2535},"Assert.ThrowsExactlyAsync",{"type":46,"value":1803},{"type":40,"tag":616,"props":2538,"children":2540},{"className":1175,"code":2539,"language":1177,"meta":621,"style":621},"\u002F\u002F Before (v3)\n[ExpectedException(typeof(HttpRequestException))]\n[TestMethod]\npublic async Task FetchData_BadUrl_Throws()\n{\n    await client.GetAsync(\"https:\u002F\u002Flocalhost:0\");\n}\n\n\u002F\u002F After (v4)\n[TestMethod]\npublic async Task FetchData_BadUrl_Throws()\n{\n    await Assert.ThrowsExactlyAsync\u003CHttpRequestException>(\n        () => client.GetAsync(\"https:\u002F\u002Flocalhost:0\"));\n}\n",[2541],{"type":40,"tag":80,"props":2542,"children":2543},{"__ignoreMap":621},[2544,2551,2559,2566,2574,2581,2589,2596,2603,2610,2617,2624,2631,2639,2647],{"type":40,"tag":627,"props":2545,"children":2546},{"class":629,"line":630},[2547],{"type":40,"tag":627,"props":2548,"children":2549},{},[2550],{"type":46,"value":1189},{"type":40,"tag":627,"props":2552,"children":2553},{"class":629,"line":661},[2554],{"type":40,"tag":627,"props":2555,"children":2556},{},[2557],{"type":46,"value":2558},"[ExpectedException(typeof(HttpRequestException))]\n",{"type":40,"tag":627,"props":2560,"children":2561},{"class":629,"line":1200},[2562],{"type":40,"tag":627,"props":2563,"children":2564},{},[2565],{"type":46,"value":2305},{"type":40,"tag":627,"props":2567,"children":2568},{"class":629,"line":1209},[2569],{"type":40,"tag":627,"props":2570,"children":2571},{},[2572],{"type":46,"value":2573},"public async Task FetchData_BadUrl_Throws()\n",{"type":40,"tag":627,"props":2575,"children":2576},{"class":629,"line":1218},[2577],{"type":40,"tag":627,"props":2578,"children":2579},{},[2580],{"type":46,"value":1206},{"type":40,"tag":627,"props":2582,"children":2583},{"class":629,"line":1227},[2584],{"type":40,"tag":627,"props":2585,"children":2586},{},[2587],{"type":46,"value":2588},"    await client.GetAsync(\"https:\u002F\u002Flocalhost:0\");\n",{"type":40,"tag":627,"props":2590,"children":2591},{"class":629,"line":1236},[2592],{"type":40,"tag":627,"props":2593,"children":2594},{},[2595],{"type":46,"value":1260},{"type":40,"tag":627,"props":2597,"children":2598},{"class":629,"line":1245},[2599],{"type":40,"tag":627,"props":2600,"children":2601},{"emptyLinePlaceholder":1267},[2602],{"type":46,"value":1270},{"type":40,"tag":627,"props":2604,"children":2605},{"class":629,"line":1254},[2606],{"type":40,"tag":627,"props":2607,"children":2608},{},[2609],{"type":46,"value":1633},{"type":40,"tag":627,"props":2611,"children":2612},{"class":629,"line":1263},[2613],{"type":40,"tag":627,"props":2614,"children":2615},{},[2616],{"type":46,"value":2305},{"type":40,"tag":627,"props":2618,"children":2619},{"class":629,"line":1273},[2620],{"type":40,"tag":627,"props":2621,"children":2622},{},[2623],{"type":46,"value":2573},{"type":40,"tag":627,"props":2625,"children":2626},{"class":629,"line":1282},[2627],{"type":40,"tag":627,"props":2628,"children":2629},{},[2630],{"type":46,"value":1206},{"type":40,"tag":627,"props":2632,"children":2633},{"class":629,"line":1290},[2634],{"type":40,"tag":627,"props":2635,"children":2636},{},[2637],{"type":46,"value":2638},"    await Assert.ThrowsExactlyAsync\u003CHttpRequestException>(\n",{"type":40,"tag":627,"props":2640,"children":2641},{"class":629,"line":1298},[2642],{"type":40,"tag":627,"props":2643,"children":2644},{},[2645],{"type":46,"value":2646},"        () => client.GetAsync(\"https:\u002F\u002Flocalhost:0\"));\n",{"type":40,"tag":627,"props":2648,"children":2649},{"class":629,"line":1307},[2650],{"type":40,"tag":627,"props":2651,"children":2652},{},[2653],{"type":46,"value":1260},{"type":40,"tag":49,"props":2655,"children":2656},{},[2657,2677,2678,2684,2686,2692],{"type":40,"tag":55,"props":2658,"children":2659},{},[2660,2662,2667,2669,2675],{"type":46,"value":2661},"If ",{"type":40,"tag":80,"props":2663,"children":2665},{"className":2664},[],[2666],{"type":46,"value":2245},{"type":46,"value":2668}," used the ",{"type":40,"tag":80,"props":2670,"children":2672},{"className":2671},[],[2673],{"type":46,"value":2674},"AllowDerivedTypes",{"type":46,"value":2676}," property",{"type":46,"value":2529},{"type":40,"tag":80,"props":2679,"children":2681},{"className":2680},[],[2682],{"type":46,"value":2683},"Assert.ThrowsAsync\u003CT>",{"type":46,"value":2685}," (base type matching) instead of ",{"type":40,"tag":80,"props":2687,"children":2689},{"className":2688},[],[2690],{"type":46,"value":2691},"Assert.ThrowsExactlyAsync\u003CT>",{"type":46,"value":2693}," (exact type matching).",{"type":40,"tag":1118,"props":2695,"children":2697},{"id":2696},"_39-dropped-target-frameworks",[2698],{"type":46,"value":2699},"3.9 Dropped target frameworks",{"type":40,"tag":49,"props":2701,"children":2702},{},[2703,2705,2709,2710,2714,2715,2719,2720,2724,2725,2729,2730,2734],{"type":46,"value":2704},"MSTest v4 supports: ",{"type":40,"tag":55,"props":2706,"children":2707},{},[2708],{"type":46,"value":523},{"type":46,"value":87},{"type":40,"tag":55,"props":2711,"children":2712},{},[2713],{"type":46,"value":529},{"type":46,"value":87},{"type":40,"tag":55,"props":2716,"children":2717},{},[2718],{"type":46,"value":535},{"type":46,"value":537},{"type":40,"tag":55,"props":2721,"children":2722},{},[2723],{"type":46,"value":542},{"type":46,"value":544},{"type":40,"tag":55,"props":2726,"children":2727},{},[2728],{"type":46,"value":549},{"type":46,"value":551},{"type":40,"tag":55,"props":2731,"children":2732},{},[2733],{"type":46,"value":556},{"type":46,"value":2735}," (WinUI). All other frameworks are dropped -- including net5.0, net6.0, net7.0, and netcoreapp3.1.",{"type":40,"tag":49,"props":2737,"children":2738},{},[2739,2741,2747],{"type":46,"value":2740},"If the test project targets an unsupported framework, update ",{"type":40,"tag":80,"props":2742,"children":2744},{"className":2743},[],[2745],{"type":46,"value":2746},"TargetFramework",{"type":46,"value":1803},{"type":40,"tag":616,"props":2749,"children":2751},{"className":618,"code":2750,"language":620,"meta":621,"style":621},"\u003C!-- Before -->\n\u003CTargetFramework>net6.0\u003C\u002FTargetFramework>\n\n\u003C!-- After -->\n\u003CTargetFramework>net8.0\u003C\u002FTargetFramework>\n",[2752],{"type":40,"tag":80,"props":2753,"children":2754},{"__ignoreMap":621},[2755,2763,2771,2778,2786],{"type":40,"tag":627,"props":2756,"children":2757},{"class":629,"line":630},[2758],{"type":40,"tag":627,"props":2759,"children":2760},{},[2761],{"type":46,"value":2762},"\u003C!-- Before -->\n",{"type":40,"tag":627,"props":2764,"children":2765},{"class":629,"line":661},[2766],{"type":40,"tag":627,"props":2767,"children":2768},{},[2769],{"type":46,"value":2770},"\u003CTargetFramework>net6.0\u003C\u002FTargetFramework>\n",{"type":40,"tag":627,"props":2772,"children":2773},{"class":629,"line":1200},[2774],{"type":40,"tag":627,"props":2775,"children":2776},{"emptyLinePlaceholder":1267},[2777],{"type":46,"value":1270},{"type":40,"tag":627,"props":2779,"children":2780},{"class":629,"line":1209},[2781],{"type":40,"tag":627,"props":2782,"children":2783},{},[2784],{"type":46,"value":2785},"\u003C!-- After -->\n",{"type":40,"tag":627,"props":2787,"children":2788},{"class":629,"line":1218},[2789],{"type":40,"tag":627,"props":2790,"children":2791},{},[2792],{"type":46,"value":2793},"\u003CTargetFramework>net8.0\u003C\u002FTargetFramework>\n",{"type":40,"tag":1118,"props":2795,"children":2797},{"id":2796},"_310-unfolding-strategy-moved-to-testmethodattribute",[2798],{"type":46,"value":2799},"3.10 Unfolding strategy moved to TestMethodAttribute",{"type":40,"tag":49,"props":2801,"children":2802},{},[2803,2804,2810,2812,2818,2819,2825,2827,2832],{"type":46,"value":240},{"type":40,"tag":80,"props":2805,"children":2807},{"className":2806},[],[2808],{"type":46,"value":2809},"UnfoldingStrategy",{"type":46,"value":2811}," property (introduced in MSTest 3.7) has moved from individual data source attributes (",{"type":40,"tag":80,"props":2813,"children":2815},{"className":2814},[],[2816],{"type":46,"value":2817},"DataRowAttribute",{"type":46,"value":87},{"type":40,"tag":80,"props":2820,"children":2822},{"className":2821},[],[2823],{"type":46,"value":2824},"DynamicDataAttribute",{"type":46,"value":2826},") to ",{"type":40,"tag":80,"props":2828,"children":2830},{"className":2829},[],[2831],{"type":46,"value":136},{"type":46,"value":501},{"type":40,"tag":1118,"props":2834,"children":2836},{"id":2835},"_311-conditionbaseattributeshouldrun-renamed",[2837],{"type":46,"value":2838},"3.11 ConditionBaseAttribute.ShouldRun renamed",{"type":40,"tag":49,"props":2840,"children":2841},{},[2842,2843,2849,2851,2857],{"type":46,"value":240},{"type":40,"tag":80,"props":2844,"children":2846},{"className":2845},[],[2847],{"type":46,"value":2848},"ConditionBaseAttribute.ShouldRun",{"type":46,"value":2850}," property is renamed to ",{"type":40,"tag":80,"props":2852,"children":2854},{"className":2853},[],[2855],{"type":46,"value":2856},"IsConditionMet",{"type":46,"value":501},{"type":40,"tag":1118,"props":2859,"children":2861},{"id":2860},"_312-internalremoved-types",[2862],{"type":46,"value":2863},"3.12 Internal\u002Fremoved types",{"type":40,"tag":49,"props":2865,"children":2866},{},[2867],{"type":46,"value":2868},"Several types previously public are now internal or removed:",{"type":40,"tag":70,"props":2870,"children":2871},{},[2872,2902,2925,2941],{"type":40,"tag":74,"props":2873,"children":2874},{},[2875,2881,2882,2888,2889,2895,2896],{"type":40,"tag":80,"props":2876,"children":2878},{"className":2877},[],[2879],{"type":46,"value":2880},"MSTestDiscoverer",{"type":46,"value":87},{"type":40,"tag":80,"props":2883,"children":2885},{"className":2884},[],[2886],{"type":46,"value":2887},"MSTestExecutor",{"type":46,"value":87},{"type":40,"tag":80,"props":2890,"children":2892},{"className":2891},[],[2893],{"type":46,"value":2894},"AssemblyResolver",{"type":46,"value":87},{"type":40,"tag":80,"props":2897,"children":2899},{"className":2898},[],[2900],{"type":46,"value":2901},"LogMessageListener",{"type":40,"tag":74,"props":2903,"children":2904},{},[2905,2911,2912,2918,2919],{"type":40,"tag":80,"props":2906,"children":2908},{"className":2907},[],[2909],{"type":46,"value":2910},"TestExecutionManager",{"type":46,"value":87},{"type":40,"tag":80,"props":2913,"children":2915},{"className":2914},[],[2916],{"type":46,"value":2917},"TestMethodInfo",{"type":46,"value":87},{"type":40,"tag":80,"props":2920,"children":2922},{"className":2921},[],[2923],{"type":46,"value":2924},"TestResultExtensions",{"type":40,"tag":74,"props":2926,"children":2927},{},[2928,2934,2935],{"type":40,"tag":80,"props":2929,"children":2931},{"className":2930},[],[2932],{"type":46,"value":2933},"UnitTestOutcomeExtensions",{"type":46,"value":87},{"type":40,"tag":80,"props":2936,"children":2938},{"className":2937},[],[2939],{"type":46,"value":2940},"GenericParameterHelper",{"type":40,"tag":74,"props":2942,"children":2943},{},[2944,2950],{"type":40,"tag":80,"props":2945,"children":2947},{"className":2946},[],[2948],{"type":46,"value":2949},"ITestMethod",{"type":46,"value":2951}," in PlatformServices assembly (the one in TestFramework is unchanged)",{"type":40,"tag":49,"props":2953,"children":2954},{},[2955],{"type":46,"value":2956},"If your code references any of these, find alternative approaches or remove the dependency.",{"type":40,"tag":441,"props":2958,"children":2960},{"id":2959},"step-4-address-behavioral-changes",[2961],{"type":46,"value":2962},"Step 4: Address behavioral changes",{"type":40,"tag":49,"props":2964,"children":2965},{},[2966],{"type":46,"value":2967},"These changes won't cause build errors but may affect test runtime behavior.",{"type":40,"tag":191,"props":2969,"children":2970},{},[2971,2991],{"type":40,"tag":195,"props":2972,"children":2973},{},[2974],{"type":40,"tag":199,"props":2975,"children":2976},{},[2977,2982,2987],{"type":40,"tag":203,"props":2978,"children":2979},{},[2980],{"type":46,"value":2981},"Symptom",{"type":40,"tag":203,"props":2983,"children":2984},{},[2985],{"type":46,"value":2986},"Cause",{"type":40,"tag":203,"props":2988,"children":2989},{},[2990],{"type":46,"value":744},{"type":40,"tag":219,"props":2992,"children":2993},{},[2994,3018,3056,3080,3112,3143],{"type":40,"tag":199,"props":2995,"children":2996},{},[2997,3002,3013],{"type":40,"tag":226,"props":2998,"children":2999},{},[3000],{"type":46,"value":3001},"Tests show as new in Azure DevOps \u002F test history lost",{"type":40,"tag":226,"props":3003,"children":3004},{},[3005,3011],{"type":40,"tag":80,"props":3006,"children":3008},{"className":3007},[],[3009],{"type":46,"value":3010},"TestCase.Id",{"type":46,"value":3012}," generation changed (4.3)",{"type":40,"tag":226,"props":3014,"children":3015},{},[3016],{"type":46,"value":3017},"No code fix; history will re-baseline",{"type":40,"tag":199,"props":3019,"children":3020},{},[3021,3038,3043],{"type":40,"tag":226,"props":3022,"children":3023},{},[3024,3030,3032],{"type":40,"tag":80,"props":3025,"children":3027},{"className":3026},[],[3028],{"type":46,"value":3029},"TestContext.TestName",{"type":46,"value":3031}," throws in ",{"type":40,"tag":80,"props":3033,"children":3035},{"className":3034},[],[3036],{"type":46,"value":3037},"[ClassInitialize]",{"type":40,"tag":226,"props":3039,"children":3040},{},[3041],{"type":46,"value":3042},"v4 enforces lifecycle scope (4.2)",{"type":40,"tag":226,"props":3044,"children":3045},{},[3046,3048,3054],{"type":46,"value":3047},"Move access to ",{"type":40,"tag":80,"props":3049,"children":3051},{"className":3050},[],[3052],{"type":46,"value":3053},"[TestInitialize]",{"type":46,"value":3055}," or test methods",{"type":40,"tag":199,"props":3057,"children":3058},{},[3059,3064,3075],{"type":40,"tag":226,"props":3060,"children":3061},{},[3062],{"type":46,"value":3063},"Tests not discovered \u002F discovery failures",{"type":40,"tag":226,"props":3065,"children":3066},{},[3067,3073],{"type":40,"tag":80,"props":3068,"children":3070},{"className":3069},[],[3071],{"type":46,"value":3072},"TreatDiscoveryWarningsAsErrors",{"type":46,"value":3074}," now true (4.4)",{"type":40,"tag":226,"props":3076,"children":3077},{},[3078],{"type":46,"value":3079},"Fix warnings, or set to false in .runsettings",{"type":40,"tag":199,"props":3081,"children":3082},{},[3083,3088,3093],{"type":40,"tag":226,"props":3084,"children":3085},{},[3086],{"type":46,"value":3087},"Tests hang that didn't before",{"type":40,"tag":226,"props":3089,"children":3090},{},[3091],{"type":46,"value":3092},"AppDomain disabled by default (4.1)",{"type":40,"tag":226,"props":3094,"children":3095},{},[3096,3098,3104,3106],{"type":46,"value":3097},"Set ",{"type":40,"tag":80,"props":3099,"children":3101},{"className":3100},[],[3102],{"type":46,"value":3103},"DisableAppDomain",{"type":46,"value":3105}," to false in .runsettings ",{"type":40,"tag":80,"props":3107,"children":3109},{"className":3108},[],[3110],{"type":46,"value":3111},"RunConfiguration",{"type":40,"tag":199,"props":3113,"children":3114},{},[3115,3120,3133],{"type":40,"tag":226,"props":3116,"children":3117},{},[3118],{"type":46,"value":3119},"vstest.console can't find tests with MSTest.Sdk",{"type":40,"tag":226,"props":3121,"children":3122},{},[3123,3125,3131],{"type":46,"value":3124},"MSTest.Sdk defaults to MTP; ",{"type":40,"tag":80,"props":3126,"children":3128},{"className":3127},[],[3129],{"type":46,"value":3130},"Microsoft.NET.Test.Sdk",{"type":46,"value":3132}," only added in VSTest mode (4.5)",{"type":40,"tag":226,"props":3134,"children":3135},{},[3136,3138],{"type":46,"value":3137},"Add explicit package reference or switch to ",{"type":40,"tag":80,"props":3139,"children":3141},{"className":3140},[],[3142],{"type":46,"value":317},{"type":40,"tag":199,"props":3144,"children":3145},{},[3146,3151,3156],{"type":40,"tag":226,"props":3147,"children":3148},{},[3149],{"type":46,"value":3150},"New warnings from analyzers",{"type":40,"tag":226,"props":3152,"children":3153},{},[3154],{"type":46,"value":3155},"Analyzer severities upgraded (4.6)",{"type":40,"tag":226,"props":3157,"children":3158},{},[3159],{"type":46,"value":3160},"Fix warnings or suppress in .editorconfig",{"type":40,"tag":1118,"props":3162,"children":3164},{"id":3163},"_41-disableappdomain-defaults-to-true",[3165],{"type":46,"value":3166},"4.1 DisableAppDomain defaults to true",{"type":40,"tag":49,"props":3168,"children":3169},{},[3170,3172,3177,3179,3185],{"type":46,"value":3171},"AppDomains are disabled by default. On .NET Framework, when running inside testhost (the default for ",{"type":40,"tag":80,"props":3173,"children":3175},{"className":3174},[],[3176],{"type":46,"value":317},{"type":46,"value":3178}," and VS), MSTest re-enables AppDomains automatically. If you need to explicitly control AppDomain isolation, set it via ",{"type":40,"tag":80,"props":3180,"children":3182},{"className":3181},[],[3183],{"type":46,"value":3184},".runsettings",{"type":46,"value":1803},{"type":40,"tag":616,"props":3187,"children":3189},{"className":618,"code":3188,"language":620,"meta":621,"style":621},"\u003CRunSettings>\n  \u003CRunConfiguration>\n    \u003CDisableAppDomain>false\u003C\u002FDisableAppDomain>\n  \u003C\u002FRunConfiguration>\n\u003C\u002FRunSettings>\n",[3190],{"type":40,"tag":80,"props":3191,"children":3192},{"__ignoreMap":621},[3193,3201,3209,3217,3225],{"type":40,"tag":627,"props":3194,"children":3195},{"class":629,"line":630},[3196],{"type":40,"tag":627,"props":3197,"children":3198},{},[3199],{"type":46,"value":3200},"\u003CRunSettings>\n",{"type":40,"tag":627,"props":3202,"children":3203},{"class":629,"line":661},[3204],{"type":40,"tag":627,"props":3205,"children":3206},{},[3207],{"type":46,"value":3208},"  \u003CRunConfiguration>\n",{"type":40,"tag":627,"props":3210,"children":3211},{"class":629,"line":1200},[3212],{"type":40,"tag":627,"props":3213,"children":3214},{},[3215],{"type":46,"value":3216},"    \u003CDisableAppDomain>false\u003C\u002FDisableAppDomain>\n",{"type":40,"tag":627,"props":3218,"children":3219},{"class":629,"line":1209},[3220],{"type":40,"tag":627,"props":3221,"children":3222},{},[3223],{"type":46,"value":3224},"  \u003C\u002FRunConfiguration>\n",{"type":40,"tag":627,"props":3226,"children":3227},{"class":629,"line":1218},[3228],{"type":40,"tag":627,"props":3229,"children":3230},{},[3231],{"type":46,"value":3232},"\u003C\u002FRunSettings>\n",{"type":40,"tag":1118,"props":3234,"children":3236},{"id":3235},"_42-testcontext-throws-when-used-incorrectly",[3237],{"type":46,"value":3238},"4.2 TestContext throws when used incorrectly",{"type":40,"tag":49,"props":3240,"children":3241},{},[3242],{"type":46,"value":3243},"MSTest v4 now throws when accessing test-specific properties in the wrong lifecycle stage:",{"type":40,"tag":70,"props":3245,"children":3246},{},[3247,3263],{"type":40,"tag":74,"props":3248,"children":3249},{},[3250,3255,3257],{"type":40,"tag":80,"props":3251,"children":3253},{"className":3252},[],[3254],{"type":46,"value":1946},{"type":46,"value":3256}," -- cannot be accessed in ",{"type":40,"tag":80,"props":3258,"children":3260},{"className":3259},[],[3261],{"type":46,"value":3262},"[AssemblyInitialize]",{"type":40,"tag":74,"props":3264,"children":3265},{},[3266,3271,3272,3277,3278],{"type":40,"tag":80,"props":3267,"children":3269},{"className":3268},[],[3270],{"type":46,"value":3029},{"type":46,"value":3256},{"type":40,"tag":80,"props":3273,"children":3275},{"className":3274},[],[3276],{"type":46,"value":3262},{"type":46,"value":138},{"type":40,"tag":80,"props":3279,"children":3281},{"className":3280},[],[3282],{"type":46,"value":3037},{"type":40,"tag":49,"props":3284,"children":3285},{},[3286,3290,3292,3297,3299,3304,3305,3310,3312,3318,3320,3325],{"type":40,"tag":55,"props":3287,"children":3288},{},[3289],{"type":46,"value":744},{"type":46,"value":3291},": Move any code that accesses ",{"type":40,"tag":80,"props":3293,"children":3295},{"className":3294},[],[3296],{"type":46,"value":3029},{"type":46,"value":3298}," from ",{"type":40,"tag":80,"props":3300,"children":3302},{"className":3301},[],[3303],{"type":46,"value":3037},{"type":46,"value":1780},{"type":40,"tag":80,"props":3306,"children":3308},{"className":3307},[],[3309],{"type":46,"value":3053},{"type":46,"value":3311}," or individual test methods, where per-test context is available. Do not replace ",{"type":40,"tag":80,"props":3313,"children":3315},{"className":3314},[],[3316],{"type":46,"value":3317},"TestName",{"type":46,"value":3319}," with ",{"type":40,"tag":80,"props":3321,"children":3323},{"className":3322},[],[3324],{"type":46,"value":960},{"type":46,"value":3326}," as a workaround -- they have different semantics.",{"type":40,"tag":1118,"props":3328,"children":3330},{"id":3329},"_43-testcaseid-generation-changed",[3331],{"type":46,"value":3332},"4.3 TestCase.Id generation changed",{"type":40,"tag":49,"props":3334,"children":3335},{},[3336,3338,3343],{"type":46,"value":3337},"The generation algorithm for ",{"type":40,"tag":80,"props":3339,"children":3341},{"className":3340},[],[3342],{"type":46,"value":3010},{"type":46,"value":3344}," has changed to fix long-standing bugs. This may affect Azure DevOps test result tracking (e.g., test failure tracking over time). There is no code fix needed, but be aware of test result history discontinuity.",{"type":40,"tag":1118,"props":3346,"children":3348},{"id":3347},"_44-treatdiscoverywarningsaserrors-defaults-to-true",[3349],{"type":46,"value":3350},"4.4 TreatDiscoveryWarningsAsErrors defaults to true",{"type":40,"tag":49,"props":3352,"children":3353},{},[3354],{"type":46,"value":3355},"v4 uses stricter defaults. Discovery warnings are now treated as errors, which means tests that previously ran despite discovery issues may now fail entirely. If you see unexpected test failures after upgrading (not build errors, but tests not being discovered), check for discovery warnings. To restore v3 behavior while you investigate:",{"type":40,"tag":616,"props":3357,"children":3359},{"className":618,"code":3358,"language":620,"meta":621,"style":621},"\u003CRunSettings>\n  \u003CMSTest>\n    \u003CTreatDiscoveryWarningsAsErrors>false\u003C\u002FTreatDiscoveryWarningsAsErrors>\n  \u003C\u002FMSTest>\n\u003C\u002FRunSettings>\n",[3360],{"type":40,"tag":80,"props":3361,"children":3362},{"__ignoreMap":621},[3363,3370,3378,3386,3394],{"type":40,"tag":627,"props":3364,"children":3365},{"class":629,"line":630},[3366],{"type":40,"tag":627,"props":3367,"children":3368},{},[3369],{"type":46,"value":3200},{"type":40,"tag":627,"props":3371,"children":3372},{"class":629,"line":661},[3373],{"type":40,"tag":627,"props":3374,"children":3375},{},[3376],{"type":46,"value":3377},"  \u003CMSTest>\n",{"type":40,"tag":627,"props":3379,"children":3380},{"class":629,"line":1200},[3381],{"type":40,"tag":627,"props":3382,"children":3383},{},[3384],{"type":46,"value":3385},"    \u003CTreatDiscoveryWarningsAsErrors>false\u003C\u002FTreatDiscoveryWarningsAsErrors>\n",{"type":40,"tag":627,"props":3387,"children":3388},{"class":629,"line":1209},[3389],{"type":40,"tag":627,"props":3390,"children":3391},{},[3392],{"type":46,"value":3393},"  \u003C\u002FMSTest>\n",{"type":40,"tag":627,"props":3395,"children":3396},{"class":629,"line":1218},[3397],{"type":40,"tag":627,"props":3398,"children":3399},{},[3400],{"type":46,"value":3232},{"type":40,"tag":427,"props":3402,"children":3403},{},[3404],{"type":40,"tag":49,"props":3405,"children":3406},{},[3407,3412],{"type":40,"tag":55,"props":3408,"children":3409},{},[3410],{"type":46,"value":3411},"Recommended",{"type":46,"value":3413},": Fix the underlying discovery warnings rather than suppressing this setting.",{"type":40,"tag":1118,"props":3415,"children":3417},{"id":3416},"_45-mstestsdk-and-vstestconsole-compatibility",[3418],{"type":46,"value":3419},"4.5 MSTest.Sdk and vstest.console compatibility",{"type":40,"tag":49,"props":3421,"children":3422},{},[3423,3425,3430,3432,3437,3439,3444,3445,3451,3453,3458],{"type":46,"value":3424},"MSTest.Sdk defaults to Microsoft.Testing.Platform (MTP) mode. In MTP mode, MSTest.Sdk does ",{"type":40,"tag":55,"props":3426,"children":3427},{},[3428],{"type":46,"value":3429},"not",{"type":46,"value":3431}," add a reference to ",{"type":40,"tag":80,"props":3433,"children":3435},{"className":3434},[],[3436],{"type":46,"value":3130},{"type":46,"value":3438}," -- it only adds it in VSTest mode. This is not a v4-specific change; it applies to MSTest.Sdk v3 as well. Without ",{"type":40,"tag":80,"props":3440,"children":3442},{"className":3441},[],[3443],{"type":46,"value":3130},{"type":46,"value":87},{"type":40,"tag":80,"props":3446,"children":3448},{"className":3447},[],[3449],{"type":46,"value":3450},"vstest.console",{"type":46,"value":3452}," cannot discover or run tests and will silently find zero tests. This commonly surfaces during migration when a CI pipeline uses ",{"type":40,"tag":80,"props":3454,"children":3456},{"className":3455},[],[3457],{"type":46,"value":3450},{"type":46,"value":3459}," but the project uses MSTest.Sdk in its default MTP mode.",{"type":40,"tag":49,"props":3461,"children":3462},{},[3463,3468,3470,3476,3478,3483],{"type":40,"tag":55,"props":3464,"children":3465},{},[3466],{"type":46,"value":3467},"Option A -- Switch to VSTest mode",{"type":46,"value":3469},": Set the ",{"type":40,"tag":80,"props":3471,"children":3473},{"className":3472},[],[3474],{"type":46,"value":3475},"UseVSTest",{"type":46,"value":3477}," property. MSTest.Sdk will then automatically add ",{"type":40,"tag":80,"props":3479,"children":3481},{"className":3480},[],[3482],{"type":46,"value":3130},{"type":46,"value":1803},{"type":40,"tag":616,"props":3485,"children":3487},{"className":618,"code":3486,"language":620,"meta":621,"style":621},"\u003CProject Sdk=\"MSTest.Sdk\u002F4.1.0\">\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net8.0\u003C\u002FTargetFramework>\n    \u003CUseVSTest>true\u003C\u002FUseVSTest>\n  \u003C\u002FPropertyGroup>\n\u003C\u002FProject>\n",[3488],{"type":40,"tag":80,"props":3489,"children":3490},{"__ignoreMap":621},[3491,3498,3506,3514,3522,3530],{"type":40,"tag":627,"props":3492,"children":3493},{"class":629,"line":630},[3494],{"type":40,"tag":627,"props":3495,"children":3496},{},[3497],{"type":46,"value":678},{"type":40,"tag":627,"props":3499,"children":3500},{"class":629,"line":661},[3501],{"type":40,"tag":627,"props":3502,"children":3503},{},[3504],{"type":46,"value":3505},"  \u003CPropertyGroup>\n",{"type":40,"tag":627,"props":3507,"children":3508},{"class":629,"line":1200},[3509],{"type":40,"tag":627,"props":3510,"children":3511},{},[3512],{"type":46,"value":3513},"    \u003CTargetFramework>net8.0\u003C\u002FTargetFramework>\n",{"type":40,"tag":627,"props":3515,"children":3516},{"class":629,"line":1209},[3517],{"type":40,"tag":627,"props":3518,"children":3519},{},[3520],{"type":46,"value":3521},"    \u003CUseVSTest>true\u003C\u002FUseVSTest>\n",{"type":40,"tag":627,"props":3523,"children":3524},{"class":629,"line":1218},[3525],{"type":40,"tag":627,"props":3526,"children":3527},{},[3528],{"type":46,"value":3529},"  \u003C\u002FPropertyGroup>\n",{"type":40,"tag":627,"props":3531,"children":3532},{"class":629,"line":1227},[3533],{"type":40,"tag":627,"props":3534,"children":3535},{},[3536],{"type":46,"value":3537},"\u003C\u002FProject>\n",{"type":40,"tag":49,"props":3539,"children":3540},{},[3541,3551,3553,3558,3560,3565],{"type":40,"tag":55,"props":3542,"children":3543},{},[3544,3546],{"type":46,"value":3545},"Option B -- Switch CI to ",{"type":40,"tag":80,"props":3547,"children":3549},{"className":3548},[],[3550],{"type":46,"value":317},{"type":46,"value":3552},": Replace ",{"type":40,"tag":80,"props":3554,"children":3556},{"className":3555},[],[3557],{"type":46,"value":3450},{"type":46,"value":3559}," invocations in your CI pipeline with ",{"type":40,"tag":80,"props":3561,"children":3563},{"className":3562},[],[3564],{"type":46,"value":317},{"type":46,"value":3566},". This works natively with MTP and is the recommended long-term approach for MSTest.Sdk projects.",{"type":40,"tag":49,"props":3568,"children":3569},{},[3570],{"type":46,"value":3571},"If you need VSTest during a transition period, Option A works without changing CI pipelines.",{"type":40,"tag":1118,"props":3573,"children":3575},{"id":3574},"_46-analyzer-severity-changes",[3576],{"type":46,"value":3577},"4.6 Analyzer severity changes",{"type":40,"tag":49,"props":3579,"children":3580},{},[3581],{"type":46,"value":3582},"Multiple analyzers have been upgraded from Info to Warning by default:",{"type":40,"tag":70,"props":3584,"children":3585},{},[3586,3591],{"type":40,"tag":74,"props":3587,"children":3588},{},[3589],{"type":46,"value":3590},"MSTEST0001, MSTEST0007, MSTEST0017, MSTEST0023, MSTEST0024, MSTEST0025",{"type":40,"tag":74,"props":3592,"children":3593},{},[3594],{"type":46,"value":3595},"MSTEST0030, MSTEST0031, MSTEST0032, MSTEST0035, MSTEST0037, MSTEST0045",{"type":40,"tag":49,"props":3597,"children":3598},{},[3599,3601,3607],{"type":46,"value":3600},"Review and fix any new warnings, or suppress them in ",{"type":40,"tag":80,"props":3602,"children":3604},{"className":3603},[],[3605],{"type":46,"value":3606},".editorconfig",{"type":46,"value":3608}," if intentional.",{"type":40,"tag":441,"props":3610,"children":3612},{"id":3611},"step-5-verify",[3613],{"type":46,"value":3614},"Step 5: Verify",{"type":40,"tag":448,"props":3616,"children":3617},{},[3618,3629,3640,3645,3657],{"type":40,"tag":74,"props":3619,"children":3620},{},[3621,3622,3627],{"type":46,"value":694},{"type":40,"tag":80,"props":3623,"children":3625},{"className":3624},[],[3626],{"type":46,"value":292},{"type":46,"value":3628}," -- confirm zero errors and review any new warnings",{"type":40,"tag":74,"props":3630,"children":3631},{},[3632,3633,3638],{"type":46,"value":694},{"type":40,"tag":80,"props":3634,"children":3636},{"className":3635},[],[3637],{"type":46,"value":317},{"type":46,"value":3639}," -- confirm all tests pass",{"type":40,"tag":74,"props":3641,"children":3642},{},[3643],{"type":46,"value":3644},"Compare test results (pass\u002Ffail counts) to the pre-migration baseline",{"type":40,"tag":74,"props":3646,"children":3647},{},[3648,3650,3655],{"type":46,"value":3649},"If using Azure DevOps test tracking, be aware that ",{"type":40,"tag":80,"props":3651,"children":3653},{"className":3652},[],[3654],{"type":46,"value":3010},{"type":46,"value":3656}," changes may affect history continuity",{"type":40,"tag":74,"props":3658,"children":3659},{},[3660],{"type":46,"value":3661},"Check that no tests were silently dropped due to stricter discovery",{"type":40,"tag":63,"props":3663,"children":3665},{"id":3664},"validation",[3666],{"type":46,"value":3667},"Validation",{"type":40,"tag":70,"props":3669,"children":3672},{"className":3670},[3671],"contains-task-list",[3673,3685,3694,3708,3731,3752,3780,3795,3816,3825,3834],{"type":40,"tag":74,"props":3674,"children":3677},{"className":3675},[3676],"task-list-item",[3678,3683],{"type":40,"tag":3679,"props":3680,"children":3682},"input",{"disabled":1267,"type":3681},"checkbox",[],{"type":46,"value":3684}," All MSTest packages updated to 4.x",{"type":40,"tag":74,"props":3686,"children":3688},{"className":3687},[3676],[3689,3692],{"type":40,"tag":3679,"props":3690,"children":3691},{"disabled":1267,"type":3681},[],{"type":46,"value":3693}," Project builds with zero errors",{"type":40,"tag":74,"props":3695,"children":3697},{"className":3696},[3676],[3698,3701,3703],{"type":40,"tag":3679,"props":3699,"children":3700},{"disabled":1267,"type":3681},[],{"type":46,"value":3702}," All tests pass with ",{"type":40,"tag":80,"props":3704,"children":3706},{"className":3705},[],[3707],{"type":46,"value":317},{"type":40,"tag":74,"props":3709,"children":3711},{"className":3710},[3676],[3712,3715,3717,3722,3724,3729],{"type":40,"tag":3679,"props":3713,"children":3714},{"disabled":1267,"type":3681},[],{"type":46,"value":3716}," Custom ",{"type":40,"tag":80,"props":3718,"children":3720},{"className":3719},[],[3721],{"type":46,"value":136},{"type":46,"value":3723}," subclasses updated for ",{"type":40,"tag":80,"props":3725,"children":3727},{"className":3726},[],[3728],{"type":46,"value":784},{"type":46,"value":3730}," and CallerInfo",{"type":40,"tag":74,"props":3732,"children":3734},{"className":3733},[3676],[3735,3738,3740,3745,3747],{"type":40,"tag":3679,"props":3736,"children":3737},{"disabled":1267,"type":3681},[],{"type":46,"value":3739}," ",{"type":40,"tag":80,"props":3741,"children":3743},{"className":3742},[],[3744],{"type":46,"value":581},{"type":46,"value":3746}," replaced with ",{"type":40,"tag":80,"props":3748,"children":3750},{"className":3749},[],[3751],{"type":46,"value":2059},{"type":40,"tag":74,"props":3753,"children":3755},{"className":3754},[3676],[3756,3759,3760,3765,3766,3771,3773,3778],{"type":40,"tag":3679,"props":3757,"children":3758},{"disabled":1267,"type":3681},[],{"type":46,"value":3739},{"type":40,"tag":80,"props":3761,"children":3763},{"className":3762},[],[3764],{"type":46,"value":593},{"type":46,"value":3746},{"type":40,"tag":80,"props":3767,"children":3769},{"className":3768},[],[3770],{"type":46,"value":2059},{"type":46,"value":3772}," (or ",{"type":40,"tag":80,"props":3774,"children":3776},{"className":3775},[],[3777],{"type":46,"value":2067},{"type":46,"value":3779},")",{"type":40,"tag":74,"props":3781,"children":3783},{"className":3782},[3676],[3784,3787,3788,3793],{"type":40,"tag":3679,"props":3785,"children":3786},{"disabled":1267,"type":3681},[],{"type":46,"value":3739},{"type":40,"tag":80,"props":3789,"children":3791},{"className":3790},[],[3792],{"type":46,"value":1657},{"type":46,"value":3794}," enum usages removed",{"type":40,"tag":74,"props":3796,"children":3798},{"className":3797},[3676],[3799,3802,3803,3809,3811],{"type":40,"tag":3679,"props":3800,"children":3801},{"disabled":1267,"type":3681},[],{"type":46,"value":3739},{"type":40,"tag":80,"props":3804,"children":3806},{"className":3805},[],[3807],{"type":46,"value":3808},"TestContext.Properties.Contains",{"type":46,"value":3810}," updated to ",{"type":40,"tag":80,"props":3812,"children":3814},{"className":3813},[],[3815],{"type":46,"value":1801},{"type":40,"tag":74,"props":3817,"children":3819},{"className":3818},[3676],[3820,3823],{"type":40,"tag":3679,"props":3821,"children":3822},{"disabled":1267,"type":3681},[],{"type":46,"value":3824}," All target frameworks are net8.0+, net9.0, net462+, uap10.0.16299, or WinUI",{"type":40,"tag":74,"props":3826,"children":3828},{"className":3827},[3676],[3829,3832],{"type":40,"tag":3679,"props":3830,"children":3831},{"disabled":1267,"type":3681},[],{"type":46,"value":3833}," Behavioral changes reviewed and addressed",{"type":40,"tag":74,"props":3835,"children":3837},{"className":3836},[3676],[3838,3841],{"type":40,"tag":3679,"props":3839,"children":3840},{"disabled":1267,"type":3681},[],{"type":46,"value":3842}," No tests were lost during migration (compare test counts)",{"type":40,"tag":63,"props":3844,"children":3846},{"id":3845},"related-skills",[3847],{"type":46,"value":3848},"Related Skills",{"type":40,"tag":70,"props":3850,"children":3851},{},[3852,3863],{"type":40,"tag":74,"props":3853,"children":3854},{},[3855,3861],{"type":40,"tag":80,"props":3856,"children":3858},{"className":3857},[],[3859],{"type":46,"value":3860},"writing-mstest-tests",{"type":46,"value":3862}," -- for modern MSTest v4 assertion APIs and test authoring best practices",{"type":40,"tag":74,"props":3864,"children":3865},{},[3866,3872],{"type":40,"tag":80,"props":3867,"children":3869},{"className":3868},[],[3870],{"type":46,"value":3871},"run-tests",{"type":46,"value":3873}," -- for running tests after migration",{"type":40,"tag":63,"props":3875,"children":3877},{"id":3876},"common-pitfalls",[3878],{"type":46,"value":3879},"Common Pitfalls",{"type":40,"tag":191,"props":3881,"children":3882},{},[3883,3899],{"type":40,"tag":195,"props":3884,"children":3885},{},[3886],{"type":40,"tag":199,"props":3887,"children":3888},{},[3889,3894],{"type":40,"tag":203,"props":3890,"children":3891},{},[3892],{"type":46,"value":3893},"Pitfall",{"type":40,"tag":203,"props":3895,"children":3896},{},[3897],{"type":46,"value":3898},"Solution",{"type":40,"tag":219,"props":3900,"children":3901},{},[3902,3936,3960,3990,4026,4051,4081,4105,4117,4137,4155],{"type":40,"tag":199,"props":3903,"children":3904},{},[3905,3921],{"type":40,"tag":226,"props":3906,"children":3907},{},[3908,3909,3914,3916],{"type":46,"value":755},{"type":40,"tag":80,"props":3910,"children":3912},{"className":3911},[],[3913],{"type":46,"value":136},{"type":46,"value":3915}," still overrides ",{"type":40,"tag":80,"props":3917,"children":3919},{"className":3918},[],[3920],{"type":46,"value":768},{"type":40,"tag":226,"props":3922,"children":3923},{},[3924,3925,3930,3931],{"type":46,"value":778},{"type":40,"tag":80,"props":3926,"children":3928},{"className":3927},[],[3929],{"type":46,"value":784},{"type":46,"value":786},{"type":40,"tag":80,"props":3932,"children":3934},{"className":3933},[],[3935],{"type":46,"value":792},{"type":40,"tag":199,"props":3937,"children":3938},{},[3939,3950],{"type":40,"tag":226,"props":3940,"children":3941},{},[3942,3948],{"type":40,"tag":80,"props":3943,"children":3945},{"className":3944},[],[3946],{"type":46,"value":3947},"TestMethodAttribute(\"display name\")",{"type":46,"value":3949}," no longer compiles",{"type":40,"tag":226,"props":3951,"children":3952},{},[3953,3954],{"type":46,"value":818},{"type":40,"tag":80,"props":3955,"children":3957},{"className":3956},[],[3958],{"type":46,"value":3959},"TestMethodAttribute(DisplayName = \"display name\")",{"type":40,"tag":199,"props":3961,"children":3962},{},[3963,3973],{"type":40,"tag":226,"props":3964,"children":3965},{},[3966,3971],{"type":40,"tag":80,"props":3967,"children":3969},{"className":3968},[],[3970],{"type":46,"value":1657},{"type":46,"value":3972}," enum not found",{"type":40,"tag":226,"props":3974,"children":3975},{},[3976,3978,3983,3985],{"type":46,"value":3977},"Remove the enum argument; ",{"type":40,"tag":80,"props":3979,"children":3981},{"className":3980},[],[3982],{"type":46,"value":854},{"type":46,"value":3984}," now always runs at end of class. For end-of-assembly cleanup, use ",{"type":40,"tag":80,"props":3986,"children":3988},{"className":3987},[],[3989],{"type":46,"value":1753},{"type":40,"tag":199,"props":3991,"children":3992},{},[3993,4003],{"type":40,"tag":226,"props":3994,"children":3995},{},[3996,4001],{"type":40,"tag":80,"props":3997,"children":3999},{"className":3998},[],[4000],{"type":46,"value":3808},{"type":46,"value":4002}," missing",{"type":40,"tag":226,"props":4004,"children":4005},{},[4006,4007,4012,4014,4019,4021],{"type":46,"value":818},{"type":40,"tag":80,"props":4008,"children":4010},{"className":4009},[],[4011],{"type":46,"value":1801},{"type":46,"value":4013}," -- ",{"type":40,"tag":80,"props":4015,"children":4017},{"className":4016},[],[4018],{"type":46,"value":877},{"type":46,"value":4020}," is now ",{"type":40,"tag":80,"props":4022,"children":4024},{"className":4023},[],[4025],{"type":46,"value":885},{"type":40,"tag":199,"props":4027,"children":4028},{},[4029,4040],{"type":40,"tag":226,"props":4030,"children":4031},{},[4032,4038],{"type":40,"tag":80,"props":4033,"children":4035},{"className":4034},[],[4036],{"type":46,"value":4037},"ExpectedException",{"type":46,"value":4039}," attribute not found",{"type":40,"tag":226,"props":4041,"children":4042},{},[4043,4044,4049],{"type":46,"value":925},{"type":40,"tag":80,"props":4045,"children":4047},{"className":4046},[],[4048],{"type":46,"value":1083},{"type":46,"value":4050}," inside the test body",{"type":40,"tag":199,"props":4052,"children":4053},{},[4054,4064],{"type":40,"tag":226,"props":4055,"children":4056},{},[4057,4062],{"type":40,"tag":80,"props":4058,"children":4060},{"className":4059},[],[4061],{"type":46,"value":593},{"type":46,"value":4063}," not found",{"type":40,"tag":226,"props":4065,"children":4066},{},[4067,4068,4073,4074,4079],{"type":46,"value":925},{"type":40,"tag":80,"props":4069,"children":4071},{"className":4070},[],[4072],{"type":46,"value":2059},{"type":46,"value":3772},{"type":40,"tag":80,"props":4075,"children":4077},{"className":4076},[],[4078],{"type":46,"value":2067},{"type":46,"value":4080}," for derived types)",{"type":40,"tag":199,"props":4082,"children":4083},{},[4084,4095],{"type":40,"tag":226,"props":4085,"children":4086},{},[4087,4093],{"type":40,"tag":80,"props":4088,"children":4090},{"className":4089},[],[4091],{"type":46,"value":4092},"Assert.AreEqual",{"type":46,"value":4094}," with format string args fails",{"type":40,"tag":226,"props":4096,"children":4097},{},[4098,4099],{"type":46,"value":984},{"type":40,"tag":80,"props":4100,"children":4102},{"className":4101},[],[4103],{"type":46,"value":4104},"$\"message {value}\"",{"type":40,"tag":199,"props":4106,"children":4107},{},[4108,4112],{"type":40,"tag":226,"props":4109,"children":4110},{},[4111],{"type":46,"value":3087},{"type":40,"tag":226,"props":4113,"children":4114},{},[4115],{"type":46,"value":4116},"AppDomain is disabled by default; on .NET Fx in testhost it is re-enabled automatically",{"type":40,"tag":199,"props":4118,"children":4119},{},[4120,4125],{"type":40,"tag":226,"props":4121,"children":4122},{},[4123],{"type":46,"value":4124},"Azure DevOps test history breaks",{"type":40,"tag":226,"props":4126,"children":4127},{},[4128,4130,4135],{"type":46,"value":4129},"Expected -- ",{"type":40,"tag":80,"props":4131,"children":4133},{"className":4132},[],[4134],{"type":46,"value":3010},{"type":46,"value":4136}," generation changed; no code fix, results will re-baseline",{"type":40,"tag":199,"props":4138,"children":4139},{},[4140,4145],{"type":40,"tag":226,"props":4141,"children":4142},{},[4143],{"type":46,"value":4144},"Discovery warnings now fail the run",{"type":40,"tag":226,"props":4146,"children":4147},{},[4148,4153],{"type":40,"tag":80,"props":4149,"children":4151},{"className":4150},[],[4152],{"type":46,"value":3072},{"type":46,"value":4154}," is true by default; fix the discovery warnings",{"type":40,"tag":199,"props":4156,"children":4157},{},[4158,4163],{"type":40,"tag":226,"props":4159,"children":4160},{},[4161],{"type":46,"value":4162},"Net6.0\u002Fnet7.0 targets don't compile",{"type":40,"tag":226,"props":4164,"children":4165},{},[4166],{"type":46,"value":4167},"Update to net8.0 -- MSTest v4 supports net8.0, net9.0, net462, uap10.0.16299, modern UWP, and WinUI",{"type":40,"tag":4169,"props":4170,"children":4171},"style",{},[4172],{"type":46,"value":4173},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":4175,"total":4281},[4176,4193,4208,4226,4238,4257,4267],{"slug":4177,"name":4177,"fn":4178,"description":4179,"org":4180,"tags":4181,"stars":22,"repoUrl":23,"updatedAt":4192},"analyzing-dotnet-performance","analyze .NET code for performance anti-patterns","Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I\u002FO with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4182,4183,4186,4189],{"name":13,"slug":14,"type":15},{"name":4184,"slug":4185,"type":15},"Code Analysis","code-analysis",{"name":4187,"slug":4188,"type":15},"Debugging","debugging",{"name":4190,"slug":4191,"type":15},"Performance","performance","2026-07-12T08:23:25.400375",{"slug":4194,"name":4194,"fn":4195,"description":4196,"org":4197,"tags":4198,"stars":22,"repoUrl":23,"updatedAt":4207},"android-tombstone-symbolication","symbolicate .NET runtime frames in Android tombstones","Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java\u002FKotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4199,4200,4203,4204],{"name":13,"slug":14,"type":15},{"name":4201,"slug":4202,"type":15},"Android","android",{"name":4187,"slug":4188,"type":15},{"name":4205,"slug":4206,"type":15},"Microsoft","microsoft","2026-07-12T08:23:21.595572",{"slug":4209,"name":4209,"fn":4210,"description":4211,"org":4212,"tags":4213,"stars":22,"repoUrl":23,"updatedAt":4225},"apple-crash-symbolication","symbolicate .NET runtime frames in crash logs","Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift\u002FObjective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4214,4215,4216,4219,4222],{"name":13,"slug":14,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4217,"slug":4218,"type":15},"iOS","ios",{"name":4220,"slug":4221,"type":15},"macOS","macos",{"name":4223,"slug":4224,"type":15},"Observability","observability","2026-07-12T08:23:20.369986",{"slug":4227,"name":4227,"fn":4228,"description":4229,"org":4230,"tags":4231,"stars":22,"repoUrl":23,"updatedAt":4237},"assertion-quality","evaluate assertion quality in test suites","Analyzes the variety and depth of assertions across test suites in any language. Use when the user asks to evaluate assertion quality, find shallow tests, identify assertion-free tests (no assertions or only trivial ones like Assert.IsNotNull \u002F toBeTruthy()), flag self-referential or tautological assertions, measure assertion diversity, or audit whether tests verify different facets of behavior. Polyglot: .NET, Python, TS\u002FJS, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. DO NOT USE FOR: writing new tests (use code-testing-agent \u002F writing-mstest-tests), mutation reasoning about whether tests would catch a bug (use test-gap-analysis), or a general severity-ranked anti-pattern audit (use test-anti-patterns), fixing or rewriting assertions, or writing, fixing, or modernizing MSTest tests, assertions, or attributes (use writing-mstest-tests).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4232,4233,4236],{"name":4184,"slug":4185,"type":15},{"name":4234,"slug":4235,"type":15},"QA","qa",{"name":20,"slug":21,"type":15},"2026-07-12T08:23:51.277743",{"slug":4239,"name":4239,"fn":4240,"description":4241,"org":4242,"tags":4243,"stars":22,"repoUrl":23,"updatedAt":4256},"author-component","create and review Blazor components","Create or review Blazor components (.razor files) with correct architecture. USE FOR: writing new Blazor components that do NOT involve JavaScript interop, implementing parameters and EventCallback, RenderFragment slots, component lifecycle (OnInitializedAsync, OnParametersSet), async patterns, IAsyncDisposable, CancellationToken, CSS isolation, code-behind. DO NOT USE FOR: creating new projects (use create-blazor-project), JavaScript interop or calling browser APIs from Blazor (use use-js-interop), forms and validation (use collect-user-input), prerendering issues (use support-prerendering), HTTP data fetching patterns (use fetch-and-send-data), coordinating state between unrelated components (use coordinate-components).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4244,4245,4248,4250,4253],{"name":13,"slug":14,"type":15},{"name":4246,"slug":4247,"type":15},"Blazor","blazor",{"name":4249,"slug":1177,"type":15},"C#",{"name":4251,"slug":4252,"type":15},"UI Components","ui-components",{"name":4254,"slug":4255,"type":15},"Web Development","web-development","2026-07-15T06:03:29.216359",{"slug":4258,"name":4258,"fn":4259,"description":4260,"org":4261,"tags":4262,"stars":22,"repoUrl":23,"updatedAt":4266},"binlog-failure-analysis","analyze MSBuild binary logs","Analyze MSBuild binary logs to diagnose build failures. USE FOR: build errors that are unclear from console output, diagnosing cascading failures across multi-project builds, tracing MSBuild target execution order, and generally any MSBuild build issues. Requires an existing .binlog file. DO NOT USE FOR: generating binlogs (use binlog-generation), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4263,4264,4265],{"name":4184,"slug":4185,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4205,"slug":4206,"type":15},"2026-07-12T08:21:34.637923",{"slug":4268,"name":4268,"fn":4269,"description":4270,"org":4271,"tags":4272,"stars":22,"repoUrl":23,"updatedAt":4280},"binlog-generation","generate MSBuild binary logs for diagnostics","Generate MSBuild binary logs (binlogs) for build diagnostics and analysis. USE FOR: adding \u002Fbl:{} to any dotnet build, test, pack, publish, or restore command to capture a full build execution trace, prerequisite for binlog-failure-analysis and build-perf-diagnostics skills, enabling post-build investigation of errors or performance. Requires MSBuild 17.8+ \u002F .NET 8 SDK+ for {} placeholder; PowerShell needs -bl:{{}}. DO NOT USE FOR: non-MSBuild build systems (npm, Maven, CMake), analyzing an existing binlog (use binlog-failure-analysis instead).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4273,4276,4277],{"name":4274,"slug":4275,"type":15},"Build","build",{"name":4187,"slug":4188,"type":15},{"name":4278,"slug":4279,"type":15},"Engineering","engineering","2026-07-19T05:38:19.340791",96,{"items":4283,"total":4388},[4284,4296,4303,4310,4318,4324,4332,4338,4344,4354,4367,4378],{"slug":4285,"name":4285,"fn":4286,"description":4287,"org":4288,"tags":4289,"stars":4293,"repoUrl":4294,"updatedAt":4295},"multithreaded-task-migration","migrate MSBuild tasks to multithreaded mode","Guide for migrating MSBuild tasks to multithreaded mode support, including compatibility red-team review. Use this when converting tasks to thread-safe versions, implementing IMultiThreadableTask, adding TaskEnvironment support, or auditing migrations for behavioral compatibility.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4290,4291,4292],{"name":13,"slug":14,"type":15},{"name":4278,"slug":4279,"type":15},{"name":4190,"slug":4191,"type":15},5535,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fmsbuild","2026-07-22T05:37:33.965588",{"slug":4177,"name":4177,"fn":4178,"description":4179,"org":4297,"tags":4298,"stars":22,"repoUrl":23,"updatedAt":4192},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4299,4300,4301,4302],{"name":13,"slug":14,"type":15},{"name":4184,"slug":4185,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4190,"slug":4191,"type":15},{"slug":4194,"name":4194,"fn":4195,"description":4196,"org":4304,"tags":4305,"stars":22,"repoUrl":23,"updatedAt":4207},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4306,4307,4308,4309],{"name":13,"slug":14,"type":15},{"name":4201,"slug":4202,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4205,"slug":4206,"type":15},{"slug":4209,"name":4209,"fn":4210,"description":4211,"org":4311,"tags":4312,"stars":22,"repoUrl":23,"updatedAt":4225},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4313,4314,4315,4316,4317],{"name":13,"slug":14,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4217,"slug":4218,"type":15},{"name":4220,"slug":4221,"type":15},{"name":4223,"slug":4224,"type":15},{"slug":4227,"name":4227,"fn":4228,"description":4229,"org":4319,"tags":4320,"stars":22,"repoUrl":23,"updatedAt":4237},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4321,4322,4323],{"name":4184,"slug":4185,"type":15},{"name":4234,"slug":4235,"type":15},{"name":20,"slug":21,"type":15},{"slug":4239,"name":4239,"fn":4240,"description":4241,"org":4325,"tags":4326,"stars":22,"repoUrl":23,"updatedAt":4256},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4327,4328,4329,4330,4331],{"name":13,"slug":14,"type":15},{"name":4246,"slug":4247,"type":15},{"name":4249,"slug":1177,"type":15},{"name":4251,"slug":4252,"type":15},{"name":4254,"slug":4255,"type":15},{"slug":4258,"name":4258,"fn":4259,"description":4260,"org":4333,"tags":4334,"stars":22,"repoUrl":23,"updatedAt":4266},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4335,4336,4337],{"name":4184,"slug":4185,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4205,"slug":4206,"type":15},{"slug":4268,"name":4268,"fn":4269,"description":4270,"org":4339,"tags":4340,"stars":22,"repoUrl":23,"updatedAt":4280},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4341,4342,4343],{"name":4274,"slug":4275,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4278,"slug":4279,"type":15},{"slug":4345,"name":4345,"fn":4346,"description":4347,"org":4348,"tags":4349,"stars":22,"repoUrl":23,"updatedAt":4353},"build-parallelism","optimize MSBuild build parallelism","Diagnose and fix under-parallelized MSBuild builds. USE WHEN a multi-project solution build is slower than expected, doesn't speed up when you add cores, pegs a single core while others idle, or you want to know why `-m` isn't helping. Note: `\u002Fmaxcpucount` default is 1 (sequential) — always pass `-m` for parallel builds. Covers finding the critical path (longest serial ProjectReference chain), graph build (`\u002Fgraph`), BuildInParallel, and solution filters (`.slnf`). DO NOT USE FOR: single-project builds, incremental issues (use incremental-build), compilation slowness inside one project (use build-perf-diagnostics), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4350,4351,4352],{"name":13,"slug":14,"type":15},{"name":4278,"slug":4279,"type":15},{"name":4190,"slug":4191,"type":15},"2026-07-19T05:38:18.364937",{"slug":4355,"name":4355,"fn":4356,"description":4357,"org":4358,"tags":4359,"stars":22,"repoUrl":23,"updatedAt":4366},"build-perf-baseline","establish and optimize build performance baselines","Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before\u002Fafter measurements (cold, warm, no-op scenarios), applying optimization strategies like MSBuild Server, static graph builds, artifacts output, and dependency graph trimming. Start here before diving into build-perf-diagnostics, incremental-build, or build-parallelism. DO NOT USE FOR: non-MSBuild build systems, detailed bottleneck analysis (use build-perf-diagnostics after baselining).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4360,4361,4364,4365],{"name":4278,"slug":4279,"type":15},{"name":4362,"slug":4363,"type":15},"Monitoring","monitoring",{"name":4190,"slug":4191,"type":15},{"name":20,"slug":21,"type":15},"2026-07-12T08:21:35.865649",{"slug":4368,"name":4368,"fn":4369,"description":4370,"org":4371,"tags":4372,"stars":22,"repoUrl":23,"updatedAt":4377},"build-perf-diagnostics","diagnose MSBuild build performance bottlenecks","Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target\u002FTask Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4373,4374,4375,4376],{"name":13,"slug":14,"type":15},{"name":4187,"slug":4188,"type":15},{"name":4278,"slug":4279,"type":15},{"name":4190,"slug":4191,"type":15},"2026-07-12T08:21:40.961722",{"slug":4379,"name":4379,"fn":4380,"description":4381,"org":4382,"tags":4383,"stars":22,"repoUrl":23,"updatedAt":4387},"check-bin-obj-clash","detect MSBuild output path conflicts","Detects MSBuild projects with conflicting OutputPath or IntermediateOutputPath. USE FOR: builds failing with 'Cannot create a file when that file already exists', 'The process cannot access the file because it is being used by another process', intermittent build failures that succeed on retry, or missing\u002Foverwritten outputs in multi-project or multi-targeting builds where bin\u002Fobj (or project.assets.json) collide. Common causes: shared OutputPath, missing AppendTargetFrameworkToOutputPath, extra global properties (e.g. PublishReadyToRun), or SetTargetFramework on a ProjectReference to a single-targeting project. DO NOT USE FOR: file access errors unrelated to MSBuild (OS-level locking), single-project single-TFM builds, non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4384,4385,4386],{"name":4187,"slug":4188,"type":15},{"name":4278,"slug":4279,"type":15},{"name":4234,"slug":4235,"type":15},"2026-07-19T05:38:14.336279",144]