Philips.CodeAnalysis.MsTestAnalyzers.md

August 12, 2025 · View on GitHub

⚠️ Important Notice: Microsoft's Official MSTest Analyzers

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.


Philips MSTest Analyzer Rules

Rule IDTitleDescriptionMigration Status
PH2000Avoid test method prefixTest 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)
PH2003Assert.AreEqual usageAssert.AreEqual/AreNotEqual should be of the form AreEqual(<Expected Non-Null Literal>, <Actual Expression>)⚠️ Consider MSTEST0037
PH2004Avoid ExpectedException attributeThe [ExpectedException()] attribute does not have line number granularity. It also trips the debugger. Use Assert.Throws() instead.Use MSTEST0006
PH2005TestContext usageTestContext should be used or removed.Use MSTEST0005
PH2008Assert.AreEqual parameter types must matchThe 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.⚠️ Consider MSTEST0037
PH2009Assert.IsTrue/IsFalse UsageDo 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.⚠️ Consider MSTEST0037
PH2010Assert.IsTrue/IsFalse UsageAvoid unnecessary parantheses around <expected> and <actual>📌 Keep (Style)
PH2011Description Attribute usageDescription works awkwardly with Test Explorer. Avoid literal strings and keep them short.📌 Keep (Unique)
PH2012TestTimeout requiredEvery 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)
PH2013Avoid Ignore attributeTests marked as Ignore are dead code.Use MSTEST0015
PH2014Avoid Owner attributeExperience showed that this attribute's contents languished. This could be enhanced to mimic PH2011.📌 Keep (Unique)
PH2015Allowed Categories attributeTests 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,Integration
Whitelisted 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)
PH2016Avoid TestInitialize attributeThis 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
PH2017Avoid ClassInitialize attributeThis 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
PH2018Avoid ClassCleanup attributeThis 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
PH2019Avoid TestCleanup attributeThis 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
PH2034TestMethod requires TestClassTestMethods not inside a TestClass are not executed. They are dead code.Use MSTEST0030
PH2036TestMethod is publicTestMethods must be public.Use MSTEST0003
PH2037TestMethods and DataTestMethods require unique namesTestMethods and DataTestMethods require unique names.⚠️ Consider MSTEST0003
PH2038TestClass is publicTestClass must be public.Use MSTEST0002
PH2041Avoid MS FakesDo not use MS Fakes as a Dependency Injection solution. Use Moq instead for example.📌 Keep (Unique)
PH2050Avoid empty TestMethodTestMethods must not be empty.⚠️ Consider MSTEST0003
PH2055Avoid Assert.IsTrue(true)Do not call IsTrue/IsFalse with a literal true/false.⚠️ Consider MSTEST0037
PH2056Avoid Assert.AreEqual(true, true)Do not call AreEqual with a literal true/false.⚠️ Consider MSTEST0037
PH2058Avoid Assert conditional checkDo 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
PH2059Public Method should be TestMethodPublic methods inside a TestClass should either be a test method or non-public.Use MSTEST0029
PH2076Assert.Fail alternativesAssert.Fail should not be used if an alternative is more appropriate⚠️ Consider MSTEST0025
PH2095TestMethods must return void/Task for async methodsTestMethods must return Task if they are async methods, or void if not⚠️ Consider MSTEST0003