# .NET Test Clinic > Review a C# test class, or port it between MSTest, xUnit, NUnit and TUnit. Paste the class and get a free scan with > every test and data row counted; a metered run rewrites the class in its framework or ports it, and the page > re-scans the result. Built on the SkillSafe skills @github/csharp-mstest, @github/csharp-xunit, @github/csharp-nunit > and @github/csharp-tunit (github/awesome-copilot, MIT). An independent tool, not affiliated with Microsoft, the .NET Foundation or the framework authors. https://dotnet-test-clinic.skillsafe.ai/ ## What is free - Framework detection (MSTest, xUnit, NUnit, TUnit) and an inventory of every test: line, data rows (DataRow, InlineData, TestCase, Arguments) or data source, assertion count, async. - Every framework: async void tests; data rows whose arity does not match the parameters; tests with no assertion; Thread.Sleep and long Task.Delay; .Result / .Wait() / GetResult(); exceptions swallowed in try/catch; three or more near-duplicate tests that differ only in literals; DateTime.Now, Guid.NewGuid, new Random(); Console output; test names without MethodName_Scenario_ExpectedBehavior. - MSTest: [ExpectedException]; Assert.AreEqual(actual, expected); Assert.IsTrue(a == b); unsealed class; [DataTestMethod]; [TestInitialize] that only sets fields; nullable or null! TestContext; .Single(). - xUnit: Assert.True(a == b); [Fact] with parameters; [Theory] with no data; setup attributes xUnit ignores. NUnit: classic asserts (moved to ClassicAssert in NUnit 4); [ExpectedException]. TUnit: un-awaited assertions; tests that are not async Task; xUnit/MSTest attributes. - The attribute map from the class's framework to any other: class, test, inline data, data source, setup, teardown, class setup, skip, equality, exceptions, categories, output. - Findings CSV and the scan as Markdown. ## The metered lanes Two tasks on gpt-terra, metered per run, routed by `task`. Input string fields: `task` (review or port), `code`, `framework` (detected), `target` (port only), `facts` (the page's scan) and the optional `focus`. - `review` replies with `verdict` (ship, refactor, rework), `summary`, `findings` (rule, severity, title, where, why, fix), `prescan`, `rewrite` (the whole class, same framework), `kept` and `caveats`. The page requires every scan error and warning answered by rule id, refuses ship over an undisputed error, and re-scans the rewrite: same framework, same class, no test name (or display name) or data row lost, nothing left unexplained. - `port` replies with `summary`, `file` (the whole class in the target), `map` (from -> to), `packages`, `notes` and `caveats`. The page re-scans it as the target framework, flags source attributes left behind, lost tests or data rows and errors carried over, and requires the target's own package. ## Links - API tutorial: https://dotnet-test-clinic.skillsafe.ai/api.html - Source skills: https://skillsafe.ai/skill/@github/csharp-mstest, https://skillsafe.ai/skill/@github/csharp-xunit, https://skillsafe.ai/skill/@github/csharp-nunit, https://skillsafe.ai/skill/@github/csharp-tunit