CommentSense uses NUnit as its testing framework, along with the Roslyn analyzer testing infrastructure to validate diagnostic analyzers and code fixes.
Test Projects
The test suite is organized into three projects:
- CommentSense.Analyzers.Tests - Tests for diagnostic analyzers
- CommentSense.CodeFixes.Tests - Tests for code fix providers
- CommentSense.TestHelpers - Shared test utilities and base classes
Running Tests
To run all tests in the solution:
To run tests for a specific project:
dotnet test tests/CommentSense.Analyzers.Tests
To run a specific test class or method:
dotnet test --filter "FullyQualifiedName~CommentSenseAnalyzerTests"
Ensure all tests pass before submitting a pull request. The CI pipeline will automatically run the test suite on all PRs.
Writing Analyzer Tests
Analyzer tests verify that diagnostics are correctly reported for specific code patterns.
Test Structure
All analyzer tests inherit from CommentSenseAnalyzerTestBase<TAnalyzer>:
using CommentSense.TestHelpers;
using NUnit.Framework;
namespace CommentSense.Analyzers.Tests;
public class MyAnalyzerTests : CommentSenseAnalyzerTestBase<CommentSenseAnalyzer>
{
[Test]
public async Task TestName()
{
const string testCode = """
// Your test code here
""";
await VerifyCSenseAsync(testCode);
}
}
Diagnostic Markers
Use {|CSENSE001:identifier|} syntax to mark expected diagnostic locations:
[Test]
public async Task PublicClassWithoutDocumentationReportsDiagnostic()
{
const string testCode = """
public class {|CSENSE001:MyClass|}
{
}
""";
await VerifyCSenseAsync(testCode);
}
Multiple Diagnostics
Mark multiple expected diagnostics in the same code:
[Test]
public async Task MultipleMissingDocumentationIssues()
{
const string testCode = """
public class {|CSENSE001:MyClass|}
{
public void {|CSENSE001:MyMethod|}() { }
public int {|CSENSE001:MyField|};
}
""";
await VerifyCSenseAsync(testCode);
}
Numbered Markers
Use numbered markers ({|#0:|}) for precise diagnostic assertions:
[Test]
public async Task ConstructorWithFriendlyName()
{
const string testCode = """
/// <summary>My class</summary>
public class MyClass
{
public {|#0:MyClass|}(int x) { }
}
""";
var expected = new DiagnosticResult(CommentSenseRules.MissingDocumentationRule)
.WithLocation(0)
.WithArguments("MyClass(int)");
await VerifyCSenseAsync(testCode, expectedDiagnostics: [expected]);
}
Configuration Options
Test analyzer behavior with different configuration options:
[Test]
public async Task TestWithConfiguration()
{
const string testCode = """...
""";
var config = new Dictionary<string, string>
{
["comment_sense.visibility_level"] = "public",
["comment_sense.exclude_constants"] = "true"
};
await VerifyCSenseAsync(testCode, configOptions: config);
}
No Diagnostic Expected
Test that no diagnostic is reported:
[Test]
public async Task DocumentedClassProducesNoDiagnostic()
{
const string testCode = """
/// <summary>This is documented.</summary>
public class MyClass { }
""";
await VerifyCSenseAsync(testCode, expectDiagnostic: false);
}
Writing Code Fix Tests
Code fix tests verify that code fixes correctly transform source code.
Test Structure
Code fix tests inherit from CommentSenseCodeFixTestBase<TAnalyzer, TCodeFix>:
using CommentSense.TestHelpers;
using NUnit.Framework;
namespace CommentSense.CodeFixes.Tests;
public class MyCodeFixTests : CommentSenseCodeFixTestBase<CommentSenseAnalyzer, MyCodeFixProvider>
{
[Test]
public async Task TestCodeFix()
{
const string testCode = """
// Code before fix
""";
const string fixedCode = """
// Code after fix
""";
await VerifyCSenseCodeFixAsync(testCode, fixedCode);
}
}
Example Code Fix Test
[Test]
public async Task AddsMissingSummaryTag()
{
const string testCode = """
public class {|CSENSE001:MyClass|}
{
}
""";
const string fixedCode = """
/// <summary>
///
/// </summary>
public class MyClass
{
}
""";
await VerifyCSenseCodeFixAsync(testCode, fixedCode);
}
Testing Fix All
Verify that code fixes work correctly with “Fix All” in document, project, or solution scope:
[Test]
public async Task FixAllInDocument()
{
const string testCode = """
public class {|CSENSE001:Class1|} { }
public class {|CSENSE001:Class2|} { }
""";
const string fixedCode = """
/// <summary>
///
/// </summary>
public class Class1 { }
/// <summary>
///
/// </summary>
public class Class2 { }
""";
await VerifyCSenseCodeFixAsync(testCode, fixedCode);
}
Test Helpers
The CommentSense.TestHelpers project provides:
- CommentSenseAnalyzerTestBase - Base class for analyzer tests
- CommentSenseCodeFixTestBase - Base class for code fix tests
- NUnitVerifier - NUnit integration for Roslyn testing framework
Best Practices
Test both positive and negative cases
Write tests for when diagnostics should be reported AND when they shouldn’t.
Use raw string literals
Use C# raw string literals ("""... """) for multi-line test code for better readability.
Test edge cases
Include tests for edge cases, special characters, and unusual code patterns.
Test configuration options
Verify that all configuration options work as documented.
Keep tests focused
Each test should verify one specific behavior or scenario.
All new features and bug fixes must include corresponding tests. This ensures the codebase remains stable and regressions are caught early.
Common Test Scenarios
Testing Visibility Levels
[Test]
public async Task RespectsVisibilityConfiguration()
{
const string testCode = """
public class OuterClass
{
internal class InternalClass { }
}
""";
var config = new Dictionary<string, string>
{
["comment_sense.visibility_level"] = "internal"
};
// Should report diagnostic with 'internal' visibility level
await VerifyCSenseAsync(testCode, configOptions: config);
}
Testing Inherited Documentation
[Test]
public async Task AllowsImplicitInheritDoc()
{
const string testCode = """
/// <summary>Base class</summary>
public class BaseClass
{
/// <summary>Base method</summary>
public virtual void Method() { }
}
/// <summary>Derived class</summary>
public class DerivedClass : BaseClass
{
public override void Method() { } // Should not report diagnostic
}
""";
await VerifyCSenseAsync(testCode, expectDiagnostic: false);
}
Testing Low Quality Detection
[Test]
public async Task DetectsLowQualityDocumentation()
{
const string testCode = """
/// <summary>TODO</summary>
public class {|CSENSE016:MyClass|} { }
""";
await VerifyCSenseAsync(testCode);
}