[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M699GPB2HTB1Y4YY7Q6BSV3N":3,"skill-same:01M699GPB2HTB1Y4YY7Q6BSV3N":95},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":31,"publishedAt":56,"linkOnly":57,"markdownContent":58,"markdownBody":59,"frontMatter":60,"refName":66,"commitSha":67,"skillDirectory":68,"forksCount":69,"repositoryHtmlUrl":70,"repositoryHomepageUrl":6,"ownerHtmlUrl":71,"ownerBlogUrl":72,"ownerDescription":73,"updatedAt":74,"install":75,"resources":88},"01M699GPB2HTB1Y4YY7Q6BSV3N","writing-mstest-tests",null,"ALWAYS USE when asked to fix, rewrite, update, improve, modernize, show corrected code for, or explain existing MSTest tests or MSTest-specific configuration. Use for \"review\" when corrected code or edits are wanted, even for one pasted assertion or passing tests with bad failure output. Covers expected\u002Factual labels; generic Boolean, collection, string, numeric, null, identity, exception, hard-cast, and object[] checks; TestContext\u002Flifecycle; timeout\u002Fcancellation; OS\u002FCI conditions, retry, cleanup, parallelization, MSTest.Sdk project setup, and MSTESTxxxx. Honor the installed MSTest version. DO NOT USE to design new test cases (code-testing), perform report-only audits, create project files rather than explain MSTest setup, run tests, migrate frameworks, or handle non-MSTest\u002Fnon-.NET code.","microsoft\u002Ftestfx","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M699C71CJ91B5WZ3AXG3GG2M",1059,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fwriting-mstest-tests\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55],"writing","tests","mstest","writing mstest tests","run","labels","os","project","show","files","fix","migrate","improve","ci","update","testing","create","cleanup","identity","run tests","test","sdk","review","design","2026-11-10 10:12:30",false,"---\nname: writing-mstest-tests\ndescription: >\n  ALWAYS USE when asked to fix, rewrite, update, improve, modernize, show\n  corrected code for, or explain existing MSTest tests or MSTest-specific\n  configuration. Use for \"review\" when corrected code or edits are wanted, even\n  for one pasted assertion or passing tests with bad failure output. Covers\n  expected\u002Factual labels; generic Boolean, collection, string, numeric, null,\n  identity, exception, hard-cast, and object[] checks;\n  TestContext\u002Flifecycle; timeout\u002Fcancellation; OS\u002FCI conditions, retry, cleanup,\n  parallelization, MSTest.Sdk project setup, and MSTESTxxxx. Honor the installed\n  MSTest version. DO NOT USE to design new test cases (code-testing),\n  perform report-only audits, create project files rather than explain MSTest\n  setup, run tests, migrate frameworks, or handle non-MSTest\u002Fnon-.NET code.\nlicense: MIT\nmetadata:\n  portability: portable\n  binding: optional-overlay\n  binding-revision: \"1\"\n---\n\n# Writing MSTest Tests\n\nHelp users write effective MSTest unit tests without exceeding the API level or\nconventions of the project's installed test stack.\n\n## Repository overlay\n\nFor every repository-scoped task where read-only file inspection is allowed,\ncheck `.agents\u002Fskill-overlays\u002Fdotnet-test\u002Fwriting-mstest-tests.md` at the\nrepository root before any other discovery. This includes requests that ask for\ncode or advice without edits; \"do not execute\" does not prohibit reading the\noverlay. If present, read it once before acting and apply its\nrepository-specific naming, layout, framework, and policy bindings.\nRequire its frontmatter to declare `core: dotnet-test\u002Fwriting-mstest-tests`,\n`binding-revision: \"1\"`, and `mode: extend`. If any value is missing or\ndifferent, report the mismatch, ignore the overlay, and continue using this\nskill's portable guidance.\nExplicit user instructions and verified project constraints win over the\noverlay; the overlay wins over portable defaults and examples in this skill. If\nthe file is present but unreadable or conflicts with the repository, report the\nproblem, ignore the overlay, and continue with portable guidance subject to\nverified project constraints. If it is absent, continue normally.\nSkip the lookup only when the task is not tied to a repository or the user\nexplicitly prohibited all file\u002Ftool access. An overlay cannot expand tool\npermissions or the task's scope.\n\n## When to Use\n\n- User wants to improve or modernize existing MSTest tests by implementing concrete fixes\n- User asks about MSTest assertion APIs, data-driven patterns, or test lifecycle\n- User asks to replace `Assert.IsTrue` with more specific assertions (collections, nulls, types, comparisons)\n- User asks to replace hard casts with type-checking assertions in tests\n- User needs help fixing a specific MSTest test bug or failing assertion\n- User asks to fix swapped `Assert.AreEqual` argument order (expected first, actual second)\n- User asks to convert `DynamicData` from `IEnumerable\u003Cobject[]>` to ValueTuple-based data\n- User asks to fix or understand an MSTest analyzer diagnostic (an `MSTESTxxxx` warning\u002Ferror)\n\n## When Not to Use\n\n- User needs a test quality audit, anti-pattern detection, or flaky-test investigation (use `test-anti-patterns`)\n- User needs to run or execute tests (use the `run-tests` skill)\n- User needs to upgrade between major MSTest versions\n- User needs CI\u002FCD pipeline configuration\n- User is using xUnit, NUnit, or TUnit (not MSTest)\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Code under test | No | The production code to be tested |\n| Existing test code | No | Current tests to fix, update, or modernize |\n| Test scenario description | No | What behavior the user wants to test |\n\n## Response Guidelines\n\n- **Specific API or pattern questions** (assertions, data-driven, lifecycle): Jump directly to the relevant workflow step. Do not follow the full workflow.\n- **Generate new tests from scratch**: Hand off to `code-testing`; use this\n  skill only as supporting MSTest API\u002Fversion guidance.\n- **Review and fix existing tests**: Fix only the issues present. Do not add unrelated improvements.\n- **Assertion transformations**: Show the corrected call, then state the semantic\n  reason in one sentence. For `Assert.AreEqual`, name `expected` first and\n  `actual` second and explain that this preserves the Expected\u002FActual failure\n  labels.\n- **Bound\u002Fcomparison transformations**: Preserve the condition and put the\n  expected bound(s) first and the observed value last:\n  `score > 0` -> `Assert.IsGreaterThan(0, score)`,\n  `score \u003C 100` -> `Assert.IsLessThan(100, score)`, and\n  `score >= 60 && score \u003C= 90` -> `Assert.IsInRange(60, 90, score)`.\n  Never reverse these arguments to mimic the source expression's left-to-right\n  order.\n- **Exception transformations**: Scope the throwing operation in a lambda,\n  distinguish `ThrowsExactly\u003CT>` (exact type) from `Throws\u003CT>` (type or derived\n  type), and capture the returned exception when properties such as `ParamName`\n  are part of the behavior.\n- **Supplied code requests**: Return complete representative method bodies, not\n  comment-only placeholders. Preserve the real operation and show symmetric\n  setup\u002Fcleanup when lifecycle or environment policy is part of the request.\n\n## Workflow\n\n### Step 1: Determine project setup\n\nCheck the test project, `packages.config`, and assembly reference `HintPath`\nvalues for the exact MSTest version and project system:\n\n- If using `MSTest.Sdk`: resolve its exact version from the project SDK\n  declaration or `global.json` `msbuild-sdks`; do not assume the latest APIs\n- If using `MSTest` metapackage: resolve its exact package version\n- If using `MSTest.TestFramework` + `MSTest.TestAdapter`: check version for feature availability\n- If using classic non-SDK XML (`ToolsVersion`, `Microsoft.CSharp.targets`,\n  explicit `\u003CCompile Include>`) and\u002For `packages.config`: preserve that project\n  system and add each new test file to `\u003CCompile Include>`.\n\nAlso inspect representative tests for custom base fixtures, helper libraries,\nmock syntax, naming, setup, and data builders. Existing conventions and installed\nversions win over the examples below. Do not upgrade MSTest, Moq, NBuilder, or\nthe project format unless the user explicitly asks for a migration.\n\n### MSTest API availability\n\n| API\u002Fpattern | Minimum version | Compatible fallback |\n|---|---:|---|\n| `Assert.ThrowsExactly*`, unified `Assert.Contains` \u002F `HasCount` \u002F `IsEmpty` \u002F `IsNotEmpty` | 3.8 | `Assert.ThrowsException*`, `CollectionAssert`, `StringAssert` |\n| `Assert.IsGreaterThan`, `IsLessThan`, `IsInRange`, `StartsWith`, `EndsWith`, `MatchesRegex` | 3.10 | `Assert.IsTrue` with a clear message, or `StringAssert` |\n| Generic `Assert.IsInstanceOfType\u003CT>(value, out var typed)` | 3.4-3.11 only | Non-generic assertion then post-assert cast on 3.0-3.3; v4 returns the typed value directly |\n| ValueTuple `DynamicData` | 3.7 | `IEnumerable\u003Cobject[]>` |\n| Constructor injection of `TestContext` | 3.6 | Instance `TestContext` property |\n| `[Retry]`, `[OSCondition]` | 3.8 | No built-in retry\u002FOS condition; fix flakiness or retain the existing condition mechanism |\n| `[CICondition]` | 3.10 | Existing project-specific condition mechanism |\n\nFor example, MSTest 3.5.x must not receive `Assert.ThrowsExactly`,\n`Assert.Contains`, ValueTuple `DynamicData`, or constructor-injected\n`TestContext`.\n\nTreat this as a hard gate: after determining the version, do not copy a later\nexample from this skill unless its minimum version is satisfied.\n\nRecommend MSTest.Sdk or the MSTest metapackage only for genuinely new projects:\n\n```xml\n\u003C!-- Option 1: MSTest SDK (simplest, recommended for new projects) -->\n\u003CProject Sdk=\"MSTest.Sdk\">\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net9.0\u003C\u002FTargetFramework>\n  \u003C\u002FPropertyGroup>\n\u003C\u002FProject>\n```\n\nWhen using `MSTest.Sdk`, put the version in `global.json` instead of the project file so all test projects get bumped together:\n\n```json\n{\n  \"msbuild-sdks\": {\n    \"MSTest.Sdk\": \"3.8.2\"\n  }\n}\n```\n\n```xml\n\u003C!-- Option 2: MSTest metapackage -->\n\u003CProject Sdk=\"Microsoft.NET.Sdk\">\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net9.0\u003C\u002FTargetFramework>\n  \u003C\u002FPropertyGroup>\n  \u003CItemGroup>\n    \u003CPackageReference Include=\"MSTest\" Version=\"3.8.2\" \u002F>\n  \u003C\u002FItemGroup>\n\u003C\u002FProject>\n```\n\n### Step 2: Write test classes following conventions\n\nApply these structural conventions only where they do not conflict with the\nsuite's established base classes and lifecycle:\n\n- **Seal test classes** with `sealed` for performance and design clarity\n- Use `[TestClass]` on the class and `[TestMethod]` on test methods\n- Follow the **Arrange-Act-Assert** (AAA) pattern\n- Name tests using `MethodName_Scenario_ExpectedBehavior`\n- Use separate test projects with naming convention `[ProjectName].Tests`\n\n```csharp\n[TestClass]\npublic sealed class OrderServiceTests\n{\n    [TestMethod]\n    public void CalculateTotal_WithDiscount_ReturnsReducedPrice()\n    {\n        \u002F\u002F Arrange\n        var service = new OrderService();\n        var order = new Order { Price = 100m, DiscountPercent = 10 };\n\n        \u002F\u002F Act\n        var total = service.CalculateTotal(order);\n\n        \u002F\u002F Assert\n        Assert.AreEqual(90m, total);\n    }\n}\n```\n\n### Step 3: Use version-compatible assertion APIs\n\nPick the most specific assertion supported by the installed MSTest version.\nMore specific assertions produce better failure messages and make the test's\nintent clear, but uncompilable \"modern\" assertions are worse than compatible\n`StringAssert`, `CollectionAssert`, or `Assert.IsTrue` calls.\n\n| What you are testing | Assertion |\n|---|---|\n| Two values are equal | `Assert.AreEqual(expected, actual)` |\n| Same object instance (reference identity) | `Assert.AreSame(expected, actual)` |\n| Value is null | `Assert.IsNull(value)` |\n| Value is not null | `Assert.IsNotNull(value)` |\n| Collection is empty | `Assert.IsEmpty(collection)` (3.8+) or `CollectionAssert` \u002F count assertion |\n| Collection is not empty | `Assert.IsNotEmpty(collection)` (3.8+) or count assertion |\n| Collection has exactly N items | `Assert.HasCount(N, collection)` (3.8+) or `Assert.AreEqual` on count |\n| Collection contains an item | `Assert.Contains(item, collection)` (3.8+) or `CollectionAssert.Contains` |\n| Collection does not contain an item | `Assert.DoesNotContain(item, collection)` (3.8+) or `CollectionAssert.DoesNotContain` |\n| Object is a specific type | `Assert.IsInstanceOfType\u003CT>(value)` |\n| Code throws an exception | `Assert.ThrowsExactly\u003CT>` (3.8+) or `Assert.ThrowsException\u003CT>` (earlier) |\n\nOn MSTest 3.8+, prefer `Assert` class methods over `StringAssert` or\n`CollectionAssert` where both exist. Older versions should keep the compatible\nspecialized classes.\nWhen several independent collection properties were requested, keep each\nsemantic check explicit even if another assertion happens to imply it. For\nexample, retain `IsNotEmpty` when the requested diagnostics distinguish\nempty\u002Fnon-empty, then use `HasCount` and `ContainsSingle` for their separate\ncardinality guarantees.\n\n#### Equality, null, and reference checks\n\n```csharp\nAssert.AreEqual(expected, actual);      \u002F\u002F Value equality\nAssert.AreSame(expected, actual);       \u002F\u002F Reference equality -- same object instance\nAssert.IsNull(value);\nAssert.IsNotNull(value);\n```\n\n#### Exception testing\n\n```csharp\n\u002F\u002F MSTest 3.8+\nvar ex = Assert.ThrowsExactly\u003CArgumentNullException>(() => service.Process(null));\nAssert.AreEqual(\"input\", ex.ParamName);\n\n\u002F\u002F Async\nvar ex = await Assert.ThrowsExactlyAsync\u003CInvalidOperationException>(\n    async () => await service.ProcessAsync(null));\n```\n\n- `Assert.Throws\u003CT>` matches `T` or any derived type\n- `Assert.ThrowsExactly\u003CT>` matches only the exact type `T`\n\nOn MSTest 3.7 and earlier, use the compatible API:\n\n```csharp\nvar ex = Assert.ThrowsException\u003CArgumentNullException>(\n    () => service.Process(null));\n```\n\n#### Collection assertions\n\n```csharp\n\u002F\u002F MSTest 3.8+\nAssert.Contains(expectedItem, collection);\nAssert.DoesNotContain(unexpectedItem, collection);\nvar single = Assert.ContainsSingle(collection);  \u002F\u002F Returns the single element\nAssert.HasCount(3, collection);\nAssert.IsEmpty(collection);\nAssert.IsNotEmpty(collection);\n```\n\nOn earlier versions use `CollectionAssert.Contains`,\n`CollectionAssert.DoesNotContain`, and `Assert.AreEqual(expectedCount,\ncollection.Count)`.\n\nReplace generic `Assert.IsTrue` with specialized assertions -- they give better failure messages:\n\n| Instead of | Use |\n|---|---|\n| `Assert.IsTrue(list.Count > 0)` | `Assert.IsNotEmpty(list)` |\n| `Assert.IsTrue(list.Count == 0)` | `Assert.IsEmpty(list)` |\n| `Assert.IsTrue(list.Count() == 3)` | `Assert.HasCount(3, list)` |\n| `Assert.IsTrue(x != null)` | `Assert.IsNotNull(x)` |\n| `Assert.IsTrue(x == null)` | `Assert.IsNull(x)` |\n| `Assert.AreEqual(a, b)` for same instance | `Assert.AreSame(a, b)` -- reference identity |\n| `Assert.IsTrue(!list.Contains(item))` | `Assert.DoesNotContain(item, list)` |\n| `list.Single(predicate)` + `Assert.IsNotNull` | `Assert.ContainsSingle(list)` |\n| `Assert.IsTrue(list.Contains(item))` | `Assert.Contains(item, list)` |\n\n#### String assertions\n\n```csharp\n\u002F\u002F MSTest 3.10+\nAssert.Contains(\"expected\", actualString);\nAssert.StartsWith(\"prefix\", actualString);\nAssert.EndsWith(\"suffix\", actualString);\nAssert.MatchesRegex(@\"\\d{3}-\\d{4}\", phoneNumber);\n```\n\nOn earlier versions use `StringAssert.Contains`, `StringAssert.StartsWith`,\n`StringAssert.EndsWith`, and `StringAssert.Matches`.\n\n#### Type assertions\n\nMSTest 3.x is not one API level. Pick the form supported by the installed\nminor version:\n\n```csharp\n\u002F\u002F MSTest 3.0-3.3\nAssert.IsInstanceOfType(result, typeof(MyHandler));\nvar typed = (MyHandler)result; \u002F\u002F Safe because the assertion stops a mismatch.\n```\n\n```csharp\n\u002F\u002F MSTest 3.4-3.11 -- out parameter\nAssert.IsInstanceOfType\u003CMyHandler>(result, out var typed);\ntyped.Handle();\n```\n\n```csharp\n\u002F\u002F MSTest 4.x -- returns the proven value directly\nvar typed = Assert.IsInstanceOfType\u003CMyHandler>(result);\n```\n\n#### Comparison assertions\n\n```csharp\nAssert.IsGreaterThan(lowerBound, actual);\nAssert.IsLessThan(upperBound, actual);\nAssert.IsInRange(low, high, actual);\n```\n\n### Step 4: Use data-driven tests for multiple inputs\n\n#### DataRow for inline values\n\n```csharp\n[TestMethod]\n[DataRow(1, 2, 3)]\n[DataRow(0, 0, 0, DisplayName = \"Zeros\")]\n[DataRow(-1, 1, 0)]\npublic void Add_ReturnsExpectedSum(int a, int b, int expected)\n{\n    Assert.AreEqual(expected, Calculator.Add(a, b));\n}\n```\n\n#### DynamicData with ValueTuples (preferred for complex data)\n\nOn MSTest 3.7+, prefer `ValueTuple` return types over\n`IEnumerable\u003Cobject[]>` for type safety. Keep `IEnumerable\u003Cobject[]>` on older\nversions.\n\nTuple element names document which position maps to which test parameter, and\ntuple element types catch incompatible values at compile time. They do **not**\nmake `DynamicData` position-independent, and swapping two same-typed elements\ncan still compile. Do not claim otherwise. When rows need custom display names\nor metadata rather than only typed positional data, use `TestDataRow\u003CT>` on\nMSTest 3.8+.\n\n```csharp\n[TestMethod]\n[DynamicData(nameof(DiscountTestData))]\npublic void ApplyDiscount_ReturnsExpectedPrice(decimal price, int percent, decimal expected)\n{\n    var result = PriceCalculator.ApplyDiscount(price, percent);\n    Assert.AreEqual(expected, result);\n}\n\n\u002F\u002F ValueTuple -- preferred (MSTest 3.7+)\npublic static IEnumerable\u003C(decimal price, int percent, decimal expected)> DiscountTestData =>\n[\n    (100m, 10, 90m),\n    (200m, 25, 150m),\n    (50m, 0, 50m),\n];\n```\n\nWhen you need metadata per test case on MSTest 3.8+, use `TestDataRow\u003CT>`:\n\n```csharp\npublic static IEnumerable\u003CTestDataRow\u003C(decimal price, int percent, decimal expected)>> DiscountTestDataWithMetadata =>\n[\n    new((100m, 10, 90m)) { DisplayName = \"10% discount\" },\n    new((200m, 25, 150m)) { DisplayName = \"25% discount\" },\n    new((50m, 0, 50m)) { DisplayName = \"No discount\" },\n];\n```\n\n### Step 5: Handle test lifecycle correctly\n\n- Prefer constructor initialization when the existing suite supports it; retain a shared `FixtureBase\u003CTSut>` or established `[TestInitialize]` lifecycle rather than rewriting the fixture architecture incidentally.\n- Use `[TestInitialize]` **only** for async initialization, combined with the constructor for sync parts\n- Use `[TestCleanup]` for cleanup that must run even on failure\n- Inject `TestContext` via constructor only on MSTest 3.6+; otherwise use the instance property.\n\n```csharp\n[TestClass]\npublic sealed class RepositoryTests\n{\n    private readonly TestContext _testContext;\n    private readonly FakeDatabase _db;  \u002F\u002F readonly -- guaranteed by constructor\n\n    public RepositoryTests(TestContext testContext)\n    {\n        _testContext = testContext;\n        _db = new FakeDatabase();  \u002F\u002F sync init in ctor\n    }\n\n    [TestInitialize]\n    public async Task InitAsync()\n    {\n        \u002F\u002F Use TestInitialize ONLY for async setup\n        await _db.SeedAsync();\n    }\n\n    [TestCleanup]\n    public void Cleanup() => _db.Reset();\n}\n```\n\n#### Execution order\n\n1. `[AssemblyInitialize]` -- once per assembly\n2. `[ClassInitialize]` -- once per class\n3. Per test:\n   - With `TestContext` property injection: Constructor -> set `TestContext` property -> `[TestInitialize]`\n   - With constructor injection of `TestContext`: Constructor (receives `TestContext`) -> `[TestInitialize]`\n4. Test method\n5. `[TestCleanup]` -> `DisposeAsync` -> `Dispose` -- per test\n6. `[ClassCleanup]` -- once per class\n7. `[AssemblyCleanup]` -- once per assembly\n\n### Step 6: Apply cancellation and timeout patterns\n\nUse `TestContext.CancellationToken` with\n`[Timeout(milliseconds, CooperativeCancellation = true)]` when the installed\nMSTest version exposes the token directly (3.11+). On MSTest 3.6.4-3.10, use\n`TestContext.CancellationTokenSource.Token` with cooperative cancellation\ninstead. A plain `[Timeout]` does not establish that the framework token will\nstop in-flight work. On older versions, use a test-owned\n`CancellationTokenSource` where cancellation itself is under test.\n\n```csharp\n\u002F\u002F MSTest 3.11+\n[TestMethod]\n[Timeout(5000, CooperativeCancellation = true)]\npublic async Task FetchData_ReturnsWithinTimeout()\n{\n    var result = await _client.GetDataAsync(_testContext.CancellationToken);\n    Assert.IsNotNull(result);\n}\n```\n\n### Step 7: Use advanced features where appropriate\n\nFor retry, conditional execution, or parallelization requests, read\n[Advanced MSTest patterns](references\u002Fadvanced-mstest-patterns.md). Apply its\nversion gates and keep the real tested operation, assertion, and cleanup.\n\n### Step 8: Fix MSTest analyzer diagnostics (MSTESTxxxx)\n\nFor an `MSTESTxxxx` request, read\n[Advanced MSTest patterns](references\u002Fadvanced-mstest-patterns.md), apply the\nversion-compatible fix for that rule, and rebuild to confirm the diagnostic is\ngone. Prefer fixing the code over suppressing the rule.\n\n### Step 9: Verify file-backed corrections\n\nWhen the user asked for repository edits and did not prohibit execution, run the\nnarrowest affected `dotnet test` command after editing. A successful process with\nno discovered-test count is not verification. Require the intended test cases to\nbe discovered and pass.\n\nIf compilation exposes a directly coupled source issue that prevents the\ncorrected existing suite from running (for example, a missing namespace import\nin the supplied production file), make only that minimum fix and rerun. Do not\nupgrade packages or broaden the modernization. Report the actual test count and\nthe fixes made; never present unrun or output-free tests as passing.\nIn the final handoff, map every requested modernization to the exact corrected\nconstruct and cite the passing test command. Do not rely on a generic \"modernized\"\nsummary when expected\u002Factual order, exact type checks, data discovery, or class\nshape were explicit requirements.\n","# Writing MSTest Tests\n\nHelp users write effective MSTest unit tests without exceeding the API level or\nconventions of the project's installed test stack.\n\n## Repository overlay\n\nFor every repository-scoped task where read-only file inspection is allowed,\ncheck `.agents\u002Fskill-overlays\u002Fdotnet-test\u002Fwriting-mstest-tests.md` at the\nrepository root before any other discovery. This includes requests that ask for\ncode or advice without edits; \"do not execute\" does not prohibit reading the\noverlay. If present, read it once before acting and apply its\nrepository-specific naming, layout, framework, and policy bindings.\nRequire its frontmatter to declare `core: dotnet-test\u002Fwriting-mstest-tests`,\n`binding-revision: \"1\"`, and `mode: extend`. If any value is missing or\ndifferent, report the mismatch, ignore the overlay, and continue using this\nskill's portable guidance.\nExplicit user instructions and verified project constraints win over the\noverlay; the overlay wins over portable defaults and examples in this skill. If\nthe file is present but unreadable or conflicts with the repository, report the\nproblem, ignore the overlay, and continue with portable guidance subject to\nverified project constraints. If it is absent, continue normally.\nSkip the lookup only when the task is not tied to a repository or the user\nexplicitly prohibited all file\u002Ftool access. An overlay cannot expand tool\npermissions or the task's scope.\n\n## When to Use\n\n- User wants to improve or modernize existing MSTest tests by implementing concrete fixes\n- User asks about MSTest assertion APIs, data-driven patterns, or test lifecycle\n- User asks to replace `Assert.IsTrue` with more specific assertions (collections, nulls, types, comparisons)\n- User asks to replace hard casts with type-checking assertions in tests\n- User needs help fixing a specific MSTest test bug or failing assertion\n- User asks to fix swapped `Assert.AreEqual` argument order (expected first, actual second)\n- User asks to convert `DynamicData` from `IEnumerable\u003Cobject[]>` to ValueTuple-based data\n- User asks to fix or understand an MSTest analyzer diagnostic (an `MSTESTxxxx` warning\u002Ferror)\n\n## When Not to Use\n\n- User needs a test quality audit, anti-pattern detection, or flaky-test investigation (use `test-anti-patterns`)\n- User needs to run or execute tests (use the `run-tests` skill)\n- User needs to upgrade between major MSTest versions\n- User needs CI\u002FCD pipeline configuration\n- User is using xUnit, NUnit, or TUnit (not MSTest)\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Code under test | No | The production code to be tested |\n| Existing test code | No | Current tests to fix, update, or modernize |\n| Test scenario description | No | What behavior the user wants to test |\n\n## Response Guidelines\n\n- **Specific API or pattern questions** (assertions, data-driven, lifecycle): Jump directly to the relevant workflow step. Do not follow the full workflow.\n- **Generate new tests from scratch**: Hand off to `code-testing`; use this\n  skill only as supporting MSTest API\u002Fversion guidance.\n- **Review and fix existing tests**: Fix only the issues present. Do not add unrelated improvements.\n- **Assertion transformations**: Show the corrected call, then state the semantic\n  reason in one sentence. For `Assert.AreEqual`, name `expected` first and\n  `actual` second and explain that this preserves the Expected\u002FActual failure\n  labels.\n- **Bound\u002Fcomparison transformations**: Preserve the condition and put the\n  expected bound(s) first and the observed value last:\n  `score > 0` -> `Assert.IsGreaterThan(0, score)`,\n  `score \u003C 100` -> `Assert.IsLessThan(100, score)`, and\n  `score >= 60 && score \u003C= 90` -> `Assert.IsInRange(60, 90, score)`.\n  Never reverse these arguments to mimic the source expression's left-to-right\n  order.\n- **Exception transformations**: Scope the throwing operation in a lambda,\n  distinguish `ThrowsExactly\u003CT>` (exact type) from `Throws\u003CT>` (type or derived\n  type), and capture the returned exception when properties such as `ParamName`\n  are part of the behavior.\n- **Supplied code requests**: Return complete representative method bodies, not\n  comment-only placeholders. Preserve the real operation and show symmetric\n  setup\u002Fcleanup when lifecycle or environment policy is part of the request.\n\n## Workflow\n\n### Step 1: Determine project setup\n\nCheck the test project, `packages.config`, and assembly reference `HintPath`\nvalues for the exact MSTest version and project system:\n\n- If using `MSTest.Sdk`: resolve its exact version from the project SDK\n  declaration or `global.json` `msbuild-sdks`; do not assume the latest APIs\n- If using `MSTest` metapackage: resolve its exact package version\n- If using `MSTest.TestFramework` + `MSTest.TestAdapter`: check version for feature availability\n- If using classic non-SDK XML (`ToolsVersion`, `Microsoft.CSharp.targets`,\n  explicit `\u003CCompile Include>`) and\u002For `packages.config`: preserve that project\n  system and add each new test file to `\u003CCompile Include>`.\n\nAlso inspect representative tests for custom base fixtures, helper libraries,\nmock syntax, naming, setup, and data builders. Existing conventions and installed\nversions win over the examples below. Do not upgrade MSTest, Moq, NBuilder, or\nthe project format unless the user explicitly asks for a migration.\n\n### MSTest API availability\n\n| API\u002Fpattern | Minimum version | Compatible fallback |\n|---|---:|---|\n| `Assert.ThrowsExactly*`, unified `Assert.Contains` \u002F `HasCount` \u002F `IsEmpty` \u002F `IsNotEmpty` | 3.8 | `Assert.ThrowsException*`, `CollectionAssert`, `StringAssert` |\n| `Assert.IsGreaterThan`, `IsLessThan`, `IsInRange`, `StartsWith`, `EndsWith`, `MatchesRegex` | 3.10 | `Assert.IsTrue` with a clear message, or `StringAssert` |\n| Generic `Assert.IsInstanceOfType\u003CT>(value, out var typed)` | 3.4-3.11 only | Non-generic assertion then post-assert cast on 3.0-3.3; v4 returns the typed value directly |\n| ValueTuple `DynamicData` | 3.7 | `IEnumerable\u003Cobject[]>` |\n| Constructor injection of `TestContext` | 3.6 | Instance `TestContext` property |\n| `[Retry]`, `[OSCondition]` | 3.8 | No built-in retry\u002FOS condition; fix flakiness or retain the existing condition mechanism |\n| `[CICondition]` | 3.10 | Existing project-specific condition mechanism |\n\nFor example, MSTest 3.5.x must not receive `Assert.ThrowsExactly`,\n`Assert.Contains`, ValueTuple `DynamicData`, or constructor-injected\n`TestContext`.\n\nTreat this as a hard gate: after determining the version, do not copy a later\nexample from this skill unless its minimum version is satisfied.\n\nRecommend MSTest.Sdk or the MSTest metapackage only for genuinely new projects:\n\n```xml\n\u003C!-- Option 1: MSTest SDK (simplest, recommended for new projects) -->\n\u003CProject Sdk=\"MSTest.Sdk\">\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net9.0\u003C\u002FTargetFramework>\n  \u003C\u002FPropertyGroup>\n\u003C\u002FProject>\n```\n\nWhen using `MSTest.Sdk`, put the version in `global.json` instead of the project file so all test projects get bumped together:\n\n```json\n{\n  \"msbuild-sdks\": {\n    \"MSTest.Sdk\": \"3.8.2\"\n  }\n}\n```\n\n```xml\n\u003C!-- Option 2: MSTest metapackage -->\n\u003CProject Sdk=\"Microsoft.NET.Sdk\">\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net9.0\u003C\u002FTargetFramework>\n  \u003C\u002FPropertyGroup>\n  \u003CItemGroup>\n    \u003CPackageReference Include=\"MSTest\" Version=\"3.8.2\" \u002F>\n  \u003C\u002FItemGroup>\n\u003C\u002FProject>\n```\n\n### Step 2: Write test classes following conventions\n\nApply these structural conventions only where they do not conflict with the\nsuite's established base classes and lifecycle:\n\n- **Seal test classes** with `sealed` for performance and design clarity\n- Use `[TestClass]` on the class and `[TestMethod]` on test methods\n- Follow the **Arrange-Act-Assert** (AAA) pattern\n- Name tests using `MethodName_Scenario_ExpectedBehavior`\n- Use separate test projects with naming convention `[ProjectName].Tests`\n\n```csharp\n[TestClass]\npublic sealed class OrderServiceTests\n{\n    [TestMethod]\n    public void CalculateTotal_WithDiscount_ReturnsReducedPrice()\n    {\n        \u002F\u002F Arrange\n        var service = new OrderService();\n        var order = new Order { Price = 100m, DiscountPercent = 10 };\n\n        \u002F\u002F Act\n        var total = service.CalculateTotal(order);\n\n        \u002F\u002F Assert\n        Assert.AreEqual(90m, total);\n    }\n}\n```\n\n### Step 3: Use version-compatible assertion APIs\n\nPick the most specific assertion supported by the installed MSTest version.\nMore specific assertions produce better failure messages and make the test's\nintent clear, but uncompilable \"modern\" assertions are worse than compatible\n`StringAssert`, `CollectionAssert`, or `Assert.IsTrue` calls.\n\n| What you are testing | Assertion |\n|---|---|\n| Two values are equal | `Assert.AreEqual(expected, actual)` |\n| Same object instance (reference identity) | `Assert.AreSame(expected, actual)` |\n| Value is null | `Assert.IsNull(value)` |\n| Value is not null | `Assert.IsNotNull(value)` |\n| Collection is empty | `Assert.IsEmpty(collection)` (3.8+) or `CollectionAssert` \u002F count assertion |\n| Collection is not empty | `Assert.IsNotEmpty(collection)` (3.8+) or count assertion |\n| Collection has exactly N items | `Assert.HasCount(N, collection)` (3.8+) or `Assert.AreEqual` on count |\n| Collection contains an item | `Assert.Contains(item, collection)` (3.8+) or `CollectionAssert.Contains` |\n| Collection does not contain an item | `Assert.DoesNotContain(item, collection)` (3.8+) or `CollectionAssert.DoesNotContain` |\n| Object is a specific type | `Assert.IsInstanceOfType\u003CT>(value)` |\n| Code throws an exception | `Assert.ThrowsExactly\u003CT>` (3.8+) or `Assert.ThrowsException\u003CT>` (earlier) |\n\nOn MSTest 3.8+, prefer `Assert` class methods over `StringAssert` or\n`CollectionAssert` where both exist. Older versions should keep the compatible\nspecialized classes.\nWhen several independent collection properties were requested, keep each\nsemantic check explicit even if another assertion happens to imply it. For\nexample, retain `IsNotEmpty` when the requested diagnostics distinguish\nempty\u002Fnon-empty, then use `HasCount` and `ContainsSingle` for their separate\ncardinality guarantees.\n\n#### Equality, null, and reference checks\n\n```csharp\nAssert.AreEqual(expected, actual);      \u002F\u002F Value equality\nAssert.AreSame(expected, actual);       \u002F\u002F Reference equality -- same object instance\nAssert.IsNull(value);\nAssert.IsNotNull(value);\n```\n\n#### Exception testing\n\n```csharp\n\u002F\u002F MSTest 3.8+\nvar ex = Assert.ThrowsExactly\u003CArgumentNullException>(() => service.Process(null));\nAssert.AreEqual(\"input\", ex.ParamName);\n\n\u002F\u002F Async\nvar ex = await Assert.ThrowsExactlyAsync\u003CInvalidOperationException>(\n    async () => await service.ProcessAsync(null));\n```\n\n- `Assert.Throws\u003CT>` matches `T` or any derived type\n- `Assert.ThrowsExactly\u003CT>` matches only the exact type `T`\n\nOn MSTest 3.7 and earlier, use the compatible API:\n\n```csharp\nvar ex = Assert.ThrowsException\u003CArgumentNullException>(\n    () => service.Process(null));\n```\n\n#### Collection assertions\n\n```csharp\n\u002F\u002F MSTest 3.8+\nAssert.Contains(expectedItem, collection);\nAssert.DoesNotContain(unexpectedItem, collection);\nvar single = Assert.ContainsSingle(collection);  \u002F\u002F Returns the single element\nAssert.HasCount(3, collection);\nAssert.IsEmpty(collection);\nAssert.IsNotEmpty(collection);\n```\n\nOn earlier versions use `CollectionAssert.Contains`,\n`CollectionAssert.DoesNotContain`, and `Assert.AreEqual(expectedCount,\ncollection.Count)`.\n\nReplace generic `Assert.IsTrue` with specialized assertions -- they give better failure messages:\n\n| Instead of | Use |\n|---|---|\n| `Assert.IsTrue(list.Count > 0)` | `Assert.IsNotEmpty(list)` |\n| `Assert.IsTrue(list.Count == 0)` | `Assert.IsEmpty(list)` |\n| `Assert.IsTrue(list.Count() == 3)` | `Assert.HasCount(3, list)` |\n| `Assert.IsTrue(x != null)` | `Assert.IsNotNull(x)` |\n| `Assert.IsTrue(x == null)` | `Assert.IsNull(x)` |\n| `Assert.AreEqual(a, b)` for same instance | `Assert.AreSame(a, b)` -- reference identity |\n| `Assert.IsTrue(!list.Contains(item))` | `Assert.DoesNotContain(item, list)` |\n| `list.Single(predicate)` + `Assert.IsNotNull` | `Assert.ContainsSingle(list)` |\n| `Assert.IsTrue(list.Contains(item))` | `Assert.Contains(item, list)` |\n\n#### String assertions\n\n```csharp\n\u002F\u002F MSTest 3.10+\nAssert.Contains(\"expected\", actualString);\nAssert.StartsWith(\"prefix\", actualString);\nAssert.EndsWith(\"suffix\", actualString);\nAssert.MatchesRegex(@\"\\d{3}-\\d{4}\", phoneNumber);\n```\n\nOn earlier versions use `StringAssert.Contains`, `StringAssert.StartsWith`,\n`StringAssert.EndsWith`, and `StringAssert.Matches`.\n\n#### Type assertions\n\nMSTest 3.x is not one API level. Pick the form supported by the installed\nminor version:\n\n```csharp\n\u002F\u002F MSTest 3.0-3.3\nAssert.IsInstanceOfType(result, typeof(MyHandler));\nvar typed = (MyHandler)result; \u002F\u002F Safe because the assertion stops a mismatch.\n```\n\n```csharp\n\u002F\u002F MSTest 3.4-3.11 -- out parameter\nAssert.IsInstanceOfType\u003CMyHandler>(result, out var typed);\ntyped.Handle();\n```\n\n```csharp\n\u002F\u002F MSTest 4.x -- returns the proven value directly\nvar typed = Assert.IsInstanceOfType\u003CMyHandler>(result);\n```\n\n#### Comparison assertions\n\n```csharp\nAssert.IsGreaterThan(lowerBound, actual);\nAssert.IsLessThan(upperBound, actual);\nAssert.IsInRange(low, high, actual);\n```\n\n### Step 4: Use data-driven tests for multiple inputs\n\n#### DataRow for inline values\n\n```csharp\n[TestMethod]\n[DataRow(1, 2, 3)]\n[DataRow(0, 0, 0, DisplayName = \"Zeros\")]\n[DataRow(-1, 1, 0)]\npublic void Add_ReturnsExpectedSum(int a, int b, int expected)\n{\n    Assert.AreEqual(expected, Calculator.Add(a, b));\n}\n```\n\n#### DynamicData with ValueTuples (preferred for complex data)\n\nOn MSTest 3.7+, prefer `ValueTuple` return types over\n`IEnumerable\u003Cobject[]>` for type safety. Keep `IEnumerable\u003Cobject[]>` on older\nversions.\n\nTuple element names document which position maps to which test parameter, and\ntuple element types catch incompatible values at compile time. They do **not**\nmake `DynamicData` position-independent, and swapping two same-typed elements\ncan still compile. Do not claim otherwise. When rows need custom display names\nor metadata rather than only typed positional data, use `TestDataRow\u003CT>` on\nMSTest 3.8+.\n\n```csharp\n[TestMethod]\n[DynamicData(nameof(DiscountTestData))]\npublic void ApplyDiscount_ReturnsExpectedPrice(decimal price, int percent, decimal expected)\n{\n    var result = PriceCalculator.ApplyDiscount(price, percent);\n    Assert.AreEqual(expected, result);\n}\n\n\u002F\u002F ValueTuple -- preferred (MSTest 3.7+)\npublic static IEnumerable\u003C(decimal price, int percent, decimal expected)> DiscountTestData =>\n[\n    (100m, 10, 90m),\n    (200m, 25, 150m),\n    (50m, 0, 50m),\n];\n```\n\nWhen you need metadata per test case on MSTest 3.8+, use `TestDataRow\u003CT>`:\n\n```csharp\npublic static IEnumerable\u003CTestDataRow\u003C(decimal price, int percent, decimal expected)>> DiscountTestDataWithMetadata =>\n[\n    new((100m, 10, 90m)) { DisplayName = \"10% discount\" },\n    new((200m, 25, 150m)) { DisplayName = \"25% discount\" },\n    new((50m, 0, 50m)) { DisplayName = \"No discount\" },\n];\n```\n\n### Step 5: Handle test lifecycle correctly\n\n- Prefer constructor initialization when the existing suite supports it; retain a shared `FixtureBase\u003CTSut>` or established `[TestInitialize]` lifecycle rather than rewriting the fixture architecture incidentally.\n- Use `[TestInitialize]` **only** for async initialization, combined with the constructor for sync parts\n- Use `[TestCleanup]` for cleanup that must run even on failure\n- Inject `TestContext` via constructor only on MSTest 3.6+; otherwise use the instance property.\n\n```csharp\n[TestClass]\npublic sealed class RepositoryTests\n{\n    private readonly TestContext _testContext;\n    private readonly FakeDatabase _db;  \u002F\u002F readonly -- guaranteed by constructor\n\n    public RepositoryTests(TestContext testContext)\n    {\n        _testContext = testContext;\n        _db = new FakeDatabase();  \u002F\u002F sync init in ctor\n    }\n\n    [TestInitialize]\n    public async Task InitAsync()\n    {\n        \u002F\u002F Use TestInitialize ONLY for async setup\n        await _db.SeedAsync();\n    }\n\n    [TestCleanup]\n    public void Cleanup() => _db.Reset();\n}\n```\n\n#### Execution order\n\n1. `[AssemblyInitialize]` -- once per assembly\n2. `[ClassInitialize]` -- once per class\n3. Per test:\n   - With `TestContext` property injection: Constructor -> set `TestContext` property -> `[TestInitialize]`\n   - With constructor injection of `TestContext`: Constructor (receives `TestContext`) -> `[TestInitialize]`\n4. Test method\n5. `[TestCleanup]` -> `DisposeAsync` -> `Dispose` -- per test\n6. `[ClassCleanup]` -- once per class\n7. `[AssemblyCleanup]` -- once per assembly\n\n### Step 6: Apply cancellation and timeout patterns\n\nUse `TestContext.CancellationToken` with\n`[Timeout(milliseconds, CooperativeCancellation = true)]` when the installed\nMSTest version exposes the token directly (3.11+). On MSTest 3.6.4-3.10, use\n`TestContext.CancellationTokenSource.Token` with cooperative cancellation\ninstead. A plain `[Timeout]` does not establish that the framework token will\nstop in-flight work. On older versions, use a test-owned\n`CancellationTokenSource` where cancellation itself is under test.\n\n```csharp\n\u002F\u002F MSTest 3.11+\n[TestMethod]\n[Timeout(5000, CooperativeCancellation = true)]\npublic async Task FetchData_ReturnsWithinTimeout()\n{\n    var result = await _client.GetDataAsync(_testContext.CancellationToken);\n    Assert.IsNotNull(result);\n}\n```\n\n### Step 7: Use advanced features where appropriate\n\nFor retry, conditional execution, or parallelization requests, read\n[Advanced MSTest patterns](references\u002Fadvanced-mstest-patterns.md). Apply its\nversion gates and keep the real tested operation, assertion, and cleanup.\n\n### Step 8: Fix MSTest analyzer diagnostics (MSTESTxxxx)\n\nFor an `MSTESTxxxx` request, read\n[Advanced MSTest patterns](references\u002Fadvanced-mstest-patterns.md), apply the\nversion-compatible fix for that rule, and rebuild to confirm the diagnostic is\ngone. Prefer fixing the code over suppressing the rule.\n\n### Step 9: Verify file-backed corrections\n\nWhen the user asked for repository edits and did not prohibit execution, run the\nnarrowest affected `dotnet test` command after editing. A successful process with\nno discovered-test count is not verification. Require the intended test cases to\nbe discovered and pass.\n\nIf compilation exposes a directly coupled source issue that prevents the\ncorrected existing suite from running (for example, a missing namespace import\nin the supplied production file), make only that minimum fix and rerun. Do not\nupgrade packages or broaden the modernization. Report the actual test count and\nthe fixes made; never present unrun or output-free tests as passing.\nIn the final handoff, map every requested modernization to the exact corrected\nconstruct and cite the passing test command. Do not rely on a generic \"modernized\"\nsummary when expected\u002Factual order, exact type checks, data discovery, or class\nshape were explicit requirements.\n",{"name":5,"license":15,"metadata":61,"description":65},{"binding":62,"portability":63,"binding-revision":64},"optional-overlay","portable","1","ALWAYS USE when asked to fix, rewrite, update, improve, modernize, show corrected code for, or explain existing MSTest tests or MSTest-specific configuration. Use for \"review\" when corrected code or edits are wanted, even for one pasted assertion or passing tests with bad failure output. Covers expected\u002Factual labels; generic Boolean, collection, string, numeric, null, identity, exception, hard-cast, and object[] checks; TestContext\u002Flifecycle; timeout\u002Fcancellation; OS\u002FCI conditions, retry, cleanup, parallelization, MSTest.Sdk project setup, and MSTESTxxxx. Honor the installed MSTest version. DO NOT USE to design new test cases (code-testing), perform report-only audits, create project files rather than explain MSTest setup, run tests, migrate frameworks, or handle non-MSTest\u002Fnon-.NET code.\n","main","f8f2bfd77819bb789d4736ce7db4a567a37fcf7e",".agents\u002Fskills\u002Fwriting-mstest-tests",315,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-12 02:10:33",{"source":8,"skillName":5,"commands":76,"prompt":86,"zipAvailable":87},[77,80,83],{"tool":78,"command":79},"npx","npx skills add microsoft\u002Ftestfx --skill writing-mstest-tests",{"tool":81,"command":82},"bunx","bunx skills add microsoft\u002Ftestfx --skill writing-mstest-tests",{"tool":84,"command":85},"pnpm","pnpm dlx skills add microsoft\u002Ftestfx --skill writing-mstest-tests","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fwriting-mstest-tests\u002FSKILL.md",true,[89],{"path":90,"resourceType":91,"resourceTypeDesc":92,"sizeBytes":93,"sourceHtmlUrl":94},"references\u002Fadvanced-mstest-patterns.md","REFERENCE","参考文档",4369,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fwriting-mstest-tests\u002Freferences\u002Fadvanced-mstest-patterns.md",[96,122,147,167,187,211],{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":102,"publishedAt":56},"01M70GYY0AV0JGMTHE2CN6TNYK","dotnet-breaking-changes","Use before changing .NET\u002FC# code whose callers you do not control: a NuGet library, or a repo with PublicAPI.Shipped\u002FUnshipped.txt, ApiCompat, multiple TargetFrameworks, InternalsVisibleTo, or generated (.g.cs) partials. Green builds miss silent breaks from optional parameters, parameter names, sealing, const values, nullability, and #if. USE FOR: \"without breaking existing callers\"; adding a public method, overload, optional parameter, or interface member to a shipped library; renaming a public method or an internal helper a friend assembly uses; consolidating duplicated logic; changing a signature, nullability, default, or attribute; adding a target framework; RS0016\u002FRS0017 or ApiCompat errors; \"is this PR breaking?\". For a rename, extract, or merge, also invoke csharp-refactoring. DO NOT USE FOR: an executable whose callers all live in one project; renaming a local variable or private member, even in a shipped library; formatting; framework\u002FSDK\u002FNuGet upgrades (use dotnet-upgrade).","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fdotnet-breaking-changes\u002FSKILL.md",[],[103,104,105,106,39,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121],"dotnet","dotnet breaking changes","changes","green","default","local","errors","interface","generated","upgrade","invoke","you","code","refactoring","merge","rename","library","live","assembly",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":129,"publishedAt":56},"01M699HHE9DTZWAMPB1ZR7ZKSW","testfx-test-value-evidence","Apply the vendored test-value-evidence skill with TestFx toolchain, assertion, parallel-safety, packed-consumer, and review conventions. Use when implementing or reviewing behavior-changing TestFx PRs or commits, including source-only changes.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-test-value-evidence\u002FSKILL.md",[128],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[52,130,131,132,133,54,134,135,136,137,138,139,140,141,142,143,105,144,145,146],"testfx test value evidence","test value evidence","evidence","value","parallel","toolchain","commits","implementing","prs","only","use","when","apply","with","behavior","Safety","conventions",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":153,"publishedAt":56},"01M699HEYWH1B9XGNBFY8J2FZS","testfx-parallel-safety-preflight","Check concurrency and isolation before implementing or refactoring TestFx tests, lifecycle code, helpers, shared-state declarations, or production paths those tests exercise. Use before narrowing DoNotParallelize or ResourceLock, changing parallelization settings, or introducing asynchronous workers; repeat against the final diff.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-parallel-safety-preflight\u002FSKILL.md",[],[154,145,155,134,156,157,137,158,115,159,116,160,161,162,163,164,165,33,140,166],"testfx parallel safety preflight","preflight","shared","asynchronous","exercise","concurrency","paths","isolation","helpers","diff","parallelization","lifecycle","workers",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":173,"publishedAt":56},"01M699HC61ZV9GJ2C8JV03951G","testfx-localization-validation","Validate generated TestFx localization catalogs, resource IDs, placeholders and locked tokens. Use for RESX\u002FXLF changes, OneLoc PRs, localization build failures, duplicate targets or units, and orphaned translation entries.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-localization-validation\u002FSKILL.md",[],[174,175,176,177,178,179,180,181,182,138,111,140,183,184,105,185,186],"testfx localization validation","validation","localization","validate","translation","ids","for","tokens","entries","units","duplicate","targets","build",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":194,"publishedAt":56},"01M699H9J2NWQXE4BDT6R0FH7S","testfx-acceptance-validation","Validate TestFx shipping paths and capable CI execution using packed consumers, package\u002Fcache provenance, exact exits and artifacts, and selected-versus-executed test evidence. Use for acceptance regressions, package\u002FMSBuild\u002Flauncher\u002Fprotocol changes, or skipped-only CI results; not for unrelated unit-only or documentation changes.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-acceptance-validation\u002FSKILL.md",[193],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[195,196,175,52,197,132,198,45,199,139,200,140,201,202,203,204,205,206,207,208,209,210,160,180],"testfx acceptance validation","acceptance","msbuild","launcher","execution","package","cache","artifacts","provenance","documentation","unrelated","shipping","results","protocol","unit","ci results",{"skillNo":212,"name":213,"nameZh":6,"description":214,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":215,"categories":216,"keywords":217,"publishedAt":56},"01M699H70BB999BT4F627T1PHE","session-delivery-hygiene","Gate local-session scope, persistent delivery, live PR state, integration provenance, and owned cleanup. Use before implementing guidance changes, changing or pushing PR work, diagnosing package\u002Foutput mismatches, or handing off implementation.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fsession-delivery-hygiene\u002FSKILL.md",[],[218,219,220,221,222,137,108,49,223,120,224,200,140,203,225,226,105,227,228,229],"session delivery hygiene","delivery","session","hygiene","scope","work","pr","output","state","integration","guidance","gate"]