Microsoft now provides official MSTest analyzers that cover most of the functionality provided by these Philips analyzers. Migration Recommendation: Use Microsoft's official analyzers for overlapping functionality and keep only Philips-specific rules that provide unique value (like PH2000, PH2012, PH2015, etc.). 📖 Read the Migration Guide for detailed mapping and migration steps.
| Rule ID | Title | Description | Migration Status |
|---|---|---|---|
| PH2000 | Avoid test method prefix | Test Method names must not start with 'Test', 'Ensure', or 'Verify'. Otherwise, they are more difficult to find in sorted lists in Test Explorer. | 📌 Keep (Unique) |
| PH2003 | Assert.AreEqual usage | Assert.AreEqual/AreNotEqual should be of the form AreEqual(<Expected Non-Null Literal>, <Actual Expression>) | |
| PH2004 | Avoid ExpectedException attribute | The [ExpectedException()] attribute does not have line number granularity. It also trips the debugger. Use Assert.Throws() instead. | ✅ Use MSTEST0006 |
| PH2005 | TestContext usage | TestContext should be used or removed. | ✅ Use MSTEST0005 |
| PH2008 | Assert.AreEqual parameter types must match | The types of the parameters of Are[Not]Equal must match. The AssertNotEqual pattern is particularly insidious, as the Assert may pass because they types unknowingly differ, even though the values are the same. | |
| PH2009 | Assert.IsTrue/IsFalse Usage | Do not call IsTrue/IsFalse if AreEqual/AreNotEqual will suffice. E.g., avoid Assert.IsTrue(x ==5). Use AreEqual(5, x) instead for clearer error messages. | |
| PH2010 | Assert.IsTrue/IsFalse Usage | Avoid unnecessary parantheses around <expected> and <actual> | 📌 Keep (Style) |
| PH2011 | Description Attribute usage | Description works awkwardly with Test Explorer. Avoid literal strings and keep them short. | 📌 Keep (Unique) |
| PH2012 | TestTimeout required | Every unit test must have a test timeout to help prevent CI/CD bloat. Consider something such as the following to exempt active debugging and to give the CI/CD pipeline a small buffer: // Uncomment to run tests locally in a debugger. //#define DEBUGGING_TESTS public sealed class TestTimeouts { #if DEBUGGING_TESTS public const bool IsDebuggingTests = true; public const int Ci = int.MaxValue; public const int Integration = int.MaxValue; public const int Smoke = int.MaxValue; #else public const bool IsDebuggingTests = false; #if BUILDSERVER public const int Ci = 1000; #else public const int Ci = 200; #endif public const int Integration = Durations.TenSecondsInMilliseconds; public const int Smoke = Durations.FifteenMinutesInMilliseconds; #endif |
📌 Keep (Unique) |
| PH2013 | Avoid Ignore attribute | Tests marked as Ignore are dead code. | ✅ Use MSTEST0015 |
| PH2014 | Avoid Owner attribute | Experience showed that this attribute's contents languished. This could be enhanced to mimic PH2011. | 📌 Keep (Unique) |
| PH2015 | Allowed Categories attribute | Tests must have a category, and the allowed category must match options specified in the .editorconfig. E.g., dotnet_code_quality.PH2015.allowed_test_categories=Unit,IntegrationWhitelisted tests are supported. E.g., perhaps you have 100 legacy Nightly tests. They run too slowly to be Unit tests. You do not want to delete them; however, you do not want any more created. Create a file named TestsWithUnsupportedCategory.Allowed.txtin the project. Mark the file as an AdditionalInclude such that the Analyzer can see it. Specify one TestMethod name per line. |
📌 Keep (Unique) |
| PH2016 | Avoid TestInitialize attribute | This attribute is unnecessary in the C# programming language. They circumvent TestTimeouts, allowing for slower Test Runs. They can contribute to non-deterministic test execution order, resulting complications while debugging. | ✅ Use MSTEST0008 |
| PH2017 | Avoid ClassInitialize attribute | This attribute is unnecessary in the C# programming language. They circumvent TestTimeouts, allowing for slower Test Runs. They can contribute to non-deterministic test execution order, resulting complications while debugging. | ✅ Use MSTEST0010 |
| PH2018 | Avoid ClassCleanup attribute | This attribute is unnecessary in the C# programming language. They circumvent TestTimeouts, allowing for slower Test Runs. They can contribute to non-deterministic test execution order, resulting complications while debugging. | ✅ Use MSTEST0011 |
| PH2019 | Avoid TestCleanup attribute | This attribute is unnecessary in the C# programming language. They circumvent TestTimeouts, allowing for slower Test Runs. They can contribute to non-deterministic test execution order, resulting complications while debugging. | ✅ Use MSTEST0009 |
| PH2034 | TestMethod requires TestClass | TestMethods not inside a TestClass are not executed. They are dead code. | ✅ Use MSTEST0030 |
| PH2036 | TestMethod is public | TestMethods must be public. | ✅ Use MSTEST0003 |
| PH2037 | TestMethods and DataTestMethods require unique names | TestMethods and DataTestMethods require unique names. | |
| PH2038 | TestClass is public | TestClass must be public. | ✅ Use MSTEST0002 |
| PH2041 | Avoid MS Fakes | Do not use MS Fakes as a Dependency Injection solution. Use Moq instead for example. | 📌 Keep (Unique) |
| PH2050 | Avoid empty TestMethod | TestMethods must not be empty. | |
| PH2055 | Avoid Assert.IsTrue(true) | Do not call IsTrue/IsFalse with a literal true/false. | |
| PH2056 | Avoid Assert.AreEqual(true, true) | Do not call AreEqual with a literal true/false. | |
| PH2058 | Avoid Assert conditional check | Do not use an inline null check while asserting. Use a different Assert.IsNotNull check. Assert.AreEqual(<actual>?.attribute, <expected>) => Assert.IsNotNull(<actual>); Assert.AreEqual(<actual>.attribute, <expected>) | ✅ Use MSTEST0026 |
| PH2059 | Public Method should be TestMethod | Public methods inside a TestClass should either be a test method or non-public. | ✅ Use MSTEST0029 |
| PH2076 | Assert.Fail alternatives | Assert.Fail should not be used if an alternative is more appropriate | |
| PH2095 | TestMethods must return void/Task for async methods | TestMethods must return Task if they are async methods, or void if not |