From b4d93f3915210c6cfdd31c1cd2cb6e27e304d0d5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Dec 2025 13:41:23 +0000
Subject: [PATCH 1/9] Initial plan
From 1b5514fe98974ed74e380ee9fc282978cdbb9c5e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Dec 2025 14:00:35 +0000
Subject: [PATCH 2/9] feat: Add bUnit.Analyzers project with BUNIT0002 analyzer
Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
---
Directory.Build.props | 2 +-
bunit.slnx | 2 +
.../AnalyzerReleases.Unshipped.md | 15 +++
.../Analyzers/PreferGenericFindAnalyzer.cs | 102 ++++++++++++++++++
src/bunit.analyzers/DiagnosticDescriptors.cs | 37 +++++++
src/bunit.analyzers/README.md | 61 +++++++++++
src/bunit.analyzers/bunit.analyzers.csproj | 61 +++++++++++
tests/Directory.Build.props | 4 +-
tests/bunit.analyzers.tests/AnalyzerTests.cs | 13 +++
.../bunit.analyzers.tests.csproj | 28 +++++
10 files changed, 322 insertions(+), 3 deletions(-)
create mode 100644 src/bunit.analyzers/AnalyzerReleases.Unshipped.md
create mode 100644 src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs
create mode 100644 src/bunit.analyzers/DiagnosticDescriptors.cs
create mode 100644 src/bunit.analyzers/README.md
create mode 100644 src/bunit.analyzers/bunit.analyzers.csproj
create mode 100644 tests/bunit.analyzers.tests/AnalyzerTests.cs
create mode 100644 tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
diff --git a/Directory.Build.props b/Directory.Build.props
index 432358349..7d2bbe961 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -37,7 +37,7 @@
true
-
+
diff --git a/bunit.slnx b/bunit.slnx
index 8c453b444..9096e574c 100644
--- a/bunit.slnx
+++ b/bunit.slnx
@@ -22,6 +22,7 @@
+
@@ -35,6 +36,7 @@
+
diff --git a/src/bunit.analyzers/AnalyzerReleases.Unshipped.md b/src/bunit.analyzers/AnalyzerReleases.Unshipped.md
new file mode 100644
index 000000000..191ed37a6
--- /dev/null
+++ b/src/bunit.analyzers/AnalyzerReleases.Unshipped.md
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://raw.githubusercontent.com/dotnet/roslyn/main/src/Compilers/Core/Portable/DiagnosticAnalyzers/AnalyzerReleases.Unshipped.schema.json",
+ "document": [
+ {
+ "id": "BUNIT0001",
+ "isEnabledByDefault": true,
+ "severity": "warning"
+ },
+ {
+ "id": "BUNIT0002",
+ "isEnabledByDefault": true,
+ "severity": "info"
+ }
+ ]
+}
diff --git a/src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs b/src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs
new file mode 100644
index 000000000..445129445
--- /dev/null
+++ b/src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs
@@ -0,0 +1,102 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Bunit.Analyzers;
+
+///
+/// Analyzer that detects cast expressions from Find() and suggests using Find{T}() instead.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public class PreferGenericFindAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.PreferGenericFind);
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ if (context is null)
+ {
+ throw new System.ArgumentNullException(nameof(context));
+ }
+
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterSyntaxNodeAction(AnalyzeCastExpression, SyntaxKind.CastExpression);
+ }
+
+ private static void AnalyzeCastExpression(SyntaxNodeAnalysisContext context)
+ {
+ var castExpression = (CastExpressionSyntax)context.Node;
+
+ // Check if the cast is on a Find() invocation
+ if (castExpression.Expression is not InvocationExpressionSyntax invocation)
+ {
+ return;
+ }
+
+ // Check if it's a member access expression (e.g., cut.Find(...))
+ if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess)
+ {
+ return;
+ }
+
+ // Check if the method name is "Find"
+ if (memberAccess.Name.Identifier.ValueText != "Find")
+ {
+ return;
+ }
+
+ // Get the method symbol to verify it's the bUnit Find method
+ var methodSymbol = context.SemanticModel.GetSymbolInfo(memberAccess, context.CancellationToken).Symbol as IMethodSymbol;
+ if (methodSymbol is null)
+ {
+ return;
+ }
+
+ // Check if the method is from IRenderedFragment or related types
+ var containingType = methodSymbol.ContainingType;
+ if (containingType is null || !IsRenderedFragmentType(containingType))
+ {
+ return;
+ }
+
+ // Get the selector argument if present
+ var selector = invocation.ArgumentList.Arguments.FirstOrDefault()?.Expression.ToString() ?? "selector";
+
+ // Get the cast type
+ var castType = castExpression.Type.ToString();
+
+ var diagnostic = Diagnostic.Create(
+ DiagnosticDescriptors.PreferGenericFind,
+ castExpression.GetLocation(),
+ castType,
+ selector);
+
+ context.ReportDiagnostic(diagnostic);
+ }
+
+ private static bool IsRenderedFragmentType(INamedTypeSymbol type)
+ {
+ // Check if the type or any of its interfaces is IRenderedFragment
+ var typeName = type.Name;
+ if (typeName is "IRenderedFragment" or "IRenderedComponent" or "RenderedFragment" or "RenderedComponent")
+ {
+ return true;
+ }
+
+ foreach (var @interface in type.AllInterfaces)
+ {
+ if (@interface.Name is "IRenderedFragment" or "IRenderedComponent")
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/bunit.analyzers/DiagnosticDescriptors.cs b/src/bunit.analyzers/DiagnosticDescriptors.cs
new file mode 100644
index 000000000..b894a40bc
--- /dev/null
+++ b/src/bunit.analyzers/DiagnosticDescriptors.cs
@@ -0,0 +1,37 @@
+using Microsoft.CodeAnalysis;
+
+namespace Bunit.Analyzers;
+
+///
+/// Diagnostic descriptors for bUnit analyzers.
+///
+internal static class DiagnosticDescriptors
+{
+ private const string Category = "Usage";
+
+ ///
+ /// BUNIT0001: Razor test files should inherit from BunitContext when using local variables in component parameters.
+ ///
+ public static readonly DiagnosticDescriptor MissingInheritsInRazorFile = new(
+ id: "BUNIT0001",
+ title: "Razor test files should inherit from BunitContext",
+ messageFormat: "Razor test file should inherit from BunitContext using @inherits BunitContext to avoid render handle errors",
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "When writing tests in Razor files that use variables or event callbacks from the test code, the file must inherit from BunitContext. Otherwise, you may encounter the error: The render handle is not yet assigned.",
+ helpLinkUri: "https://bunit.dev/docs/analyzers/bunit0001.html");
+
+ ///
+ /// BUNIT0002: Prefer Find{T} over casting.
+ ///
+ public static readonly DiagnosticDescriptor PreferGenericFind = new(
+ id: "BUNIT0002",
+ title: "Prefer Find over casting",
+ messageFormat: "Use Find<{0}>(\"{1}\") instead of casting",
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Info,
+ isEnabledByDefault: true,
+ description: "When finding elements with a specific type, use Find(selector) instead of casting the result of Find(selector).",
+ helpLinkUri: "https://bunit.dev/docs/analyzers/bunit0002.html");
+}
diff --git a/src/bunit.analyzers/README.md b/src/bunit.analyzers/README.md
new file mode 100644
index 000000000..126035d95
--- /dev/null
+++ b/src/bunit.analyzers/README.md
@@ -0,0 +1,61 @@
+# bUnit Analyzers
+
+This package contains Roslyn analyzers for bUnit that help identify common mistakes and anti-patterns when writing bUnit tests.
+
+## Analyzers
+
+### BUNIT0001: Razor test files should inherit from BunitContext
+
+When writing tests in `.razor` files that use variables or event callbacks from the test code, the file must inherit from `BunitContext` using `@inherits BunitContext`. Otherwise, you may encounter the error "The render handle is not yet assigned."
+
+**Bad:**
+```razor
+@code
+{
+ [Fact]
+ public void Test()
+ {
+ using var ctx = new BunitContext();
+ Action onClickHandler = _ => { Assert.True(true); };
+ var cut = ctx.Render(@);
+ }
+}
+```
+
+**Good:**
+```razor
+@inherits BunitContext
+@code
+{
+ [Fact]
+ public void Test()
+ {
+ Action onClickHandler = _ => { Assert.True(true); };
+ var cut = Render(@);
+ }
+}
+```
+
+### BUNIT0002: Prefer Find over casting
+
+When finding elements with a specific type, use `Find(selector)` instead of casting the result of `Find(selector)`.
+
+**Bad:**
+```csharp
+IHtmlAnchorElement elem = (IHtmlAnchorElement)cut.Find("a");
+```
+
+**Good:**
+```csharp
+var elem = cut.Find("a");
+```
+
+## Installation
+
+Install the package via NuGet:
+
+```bash
+dotnet add package bunit.analyzers
+```
+
+The analyzers will automatically run during compilation and provide warnings and code fixes in your IDE.
diff --git a/src/bunit.analyzers/bunit.analyzers.csproj b/src/bunit.analyzers/bunit.analyzers.csproj
new file mode 100644
index 000000000..273b89d27
--- /dev/null
+++ b/src/bunit.analyzers/bunit.analyzers.csproj
@@ -0,0 +1,61 @@
+
+
+
+ netstandard2.0
+ 12.0
+ true
+ Bunit
+ disable
+
+
+
+ true
+
+ true
+
+ snupkg
+
+ true
+
+ embedded
+ true
+
+ false
+ false
+
+
+
+ bunit.analyzers
+ bUnit.analyzers
+
+ bUnit.analyzers is an extension to bUnit that provides Roslyn analyzers to help identify common mistakes and anti-patterns when writing bUnit tests.
+
+
+
+
+
+ true
+ \
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
index cf491ca78..822c247fe 100644
--- a/tests/Directory.Build.props
+++ b/tests/Directory.Build.props
@@ -23,7 +23,7 @@
NU1903
-
+
@@ -35,7 +35,7 @@
-
+
diff --git a/tests/bunit.analyzers.tests/AnalyzerTests.cs b/tests/bunit.analyzers.tests/AnalyzerTests.cs
new file mode 100644
index 000000000..93b4bf4ff
--- /dev/null
+++ b/tests/bunit.analyzers.tests/AnalyzerTests.cs
@@ -0,0 +1,13 @@
+using Xunit;
+
+namespace Bunit.Analyzers.Tests;
+
+public class AnalyzerTests
+{
+ [Fact]
+ public void AnalyzerPackage_Exists()
+ {
+ // Placeholder test to ensure the test project builds
+ Assert.True(true);
+ }
+}
diff --git a/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj b/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
new file mode 100644
index 000000000..1cab5e225
--- /dev/null
+++ b/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net8.0;net9.0;net10.0
+ Bunit
+ Bunit.Analyzers.Tests
+ disable
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From bf9848e10b92fb36227bc35bdaec263cad085320 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Dec 2025 14:04:01 +0000
Subject: [PATCH 3/9] docs: Update README with bUnit.analyzers package
information
Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
---
README.md | 1 +
src/bunit.analyzers/README.md | 38 +++++++++++++++++++++--------------
2 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index 98ab4b82f..94f87afcc 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ bUnit is available on NuGet in various incarnations. Most should just pick the [
| Name | Description | NuGet Download Link |
| -------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| [bUnit](https://www.nuget.org/packages/bunit/) | Adds support for testing Blazor components. | [](https://www.nuget.org/packages/bunit/) |
+| [bUnit.analyzers](https://www.nuget.org/packages/bunit.analyzers/) | Roslyn analyzers to help identify common mistakes and anti-patterns. | [](https://www.nuget.org/packages/bunit.analyzers/) |
| [bUnit.template](https://www.nuget.org/packages/bunit.template/) | Template, which currently creates xUnit-based bUnit test projects only. | [](https://www.nuget.org/packages/bunit.template/) |
| [bUnit.generators](https://www.nuget.org/packages/bunit.generators/) | Source code generators to minimize code setup in various situations. | [](https://www.nuget.org/packages/bunit.generators/) |
| [bUnit.web.query](https://www.nuget.org/packages/bunit.web.query/) | bUnit implementation of testing-library.com's query APIs. | [](https://www.nuget.org/packages/bunit.web.query/) |
diff --git a/src/bunit.analyzers/README.md b/src/bunit.analyzers/README.md
index 126035d95..5416f40db 100644
--- a/src/bunit.analyzers/README.md
+++ b/src/bunit.analyzers/README.md
@@ -4,8 +4,26 @@ This package contains Roslyn analyzers for bUnit that help identify common mista
## Analyzers
+### BUNIT0002: Prefer Find over casting
+
+**Status**: ✅ Implemented
+
+When finding elements with a specific type, use `Find(selector)` instead of casting the result of `Find(selector)`.
+
+**Bad:**
+```csharp
+IHtmlAnchorElement elem = (IHtmlAnchorElement)cut.Find("a");
+```
+
+**Good:**
+```csharp
+var elem = cut.Find("a");
+```
+
### BUNIT0001: Razor test files should inherit from BunitContext
+**Status**: 🚧 Planned
+
When writing tests in `.razor` files that use variables or event callbacks from the test code, the file must inherit from `BunitContext` using `@inherits BunitContext`. Otherwise, you may encounter the error "The render handle is not yet assigned."
**Bad:**
@@ -36,20 +54,6 @@ When writing tests in `.razor` files that use variables or event callbacks from
}
```
-### BUNIT0002: Prefer Find over casting
-
-When finding elements with a specific type, use `Find(selector)` instead of casting the result of `Find(selector)`.
-
-**Bad:**
-```csharp
-IHtmlAnchorElement elem = (IHtmlAnchorElement)cut.Find("a");
-```
-
-**Good:**
-```csharp
-var elem = cut.Find("a");
-```
-
## Installation
Install the package via NuGet:
@@ -58,4 +62,8 @@ Install the package via NuGet:
dotnet add package bunit.analyzers
```
-The analyzers will automatically run during compilation and provide warnings and code fixes in your IDE.
+The analyzers will automatically run during compilation and provide warnings in your IDE.
+
+## Contributing
+
+We welcome contributions! If you have ideas for additional analyzers that could help bUnit users, please open an issue or submit a pull request.
From 088c56233f75a988ae87a625af62129fd77d0d22 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Dec 2025 14:07:35 +0000
Subject: [PATCH 4/9] fix: Add missing System.Linq using statement in
PreferGenericFindAnalyzer
Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
---
src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs b/src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs
index 445129445..980675e4e 100644
--- a/src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs
+++ b/src/bunit.analyzers/Analyzers/PreferGenericFindAnalyzer.cs
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
+using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
From a9d7ed0ae3cfbb83c6ab0fc5cc857824cff980df Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Dec 2025 14:51:17 +0000
Subject: [PATCH 5/9] test: Add comprehensive unit tests for BUNIT0002 analyzer
Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
---
Directory.Packages.props | 1 +
docs/site/docs/extensions/bunit-analyzers.md | 132 +++++++++++
docs/site/docs/extensions/index.md | 1 +
src/bunit.analyzers/DiagnosticDescriptors.cs | 2 +-
tests/bunit.analyzers.tests/AnalyzerTests.cs | 13 --
.../PreferGenericFindAnalyzerTests.cs | 217 ++++++++++++++++++
.../bunit.analyzers.tests.csproj | 3 +
7 files changed, 355 insertions(+), 14 deletions(-)
create mode 100644 docs/site/docs/extensions/bunit-analyzers.md
delete mode 100644 tests/bunit.analyzers.tests/AnalyzerTests.cs
create mode 100644 tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
diff --git a/Directory.Packages.props b/Directory.Packages.props
index bf8561613..8aaa9b571 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -102,5 +102,6 @@
+
diff --git a/docs/site/docs/extensions/bunit-analyzers.md b/docs/site/docs/extensions/bunit-analyzers.md
new file mode 100644
index 000000000..b5777c560
--- /dev/null
+++ b/docs/site/docs/extensions/bunit-analyzers.md
@@ -0,0 +1,132 @@
+---
+uid: bunit-analyzers
+title: bUnit Analyzers
+---
+
+# bUnit Analyzers
+
+The `bunit.analyzers` package contains a set of Roslyn analyzers that help identify common mistakes and anti-patterns when writing bUnit tests. The analyzers are designed to provide early feedback during development, catching issues before tests are run. To use the analyzers, install the `bunit.analyzers` NuGet package in your test project.
+
+This page describes the available analyzers and their usage.
+
+## Installation
+
+Install the package via NuGet:
+
+```bash
+dotnet add package bunit.analyzers
+```
+
+The analyzers will automatically run during compilation and provide warnings or suggestions in your IDE and build output.
+
+## Available Analyzers
+
+### BUNIT0002: Prefer Find<T> over casting
+
+**Severity**: Info
+**Category**: Usage
+
+This analyzer detects when you cast the result of `Find(selector)` and suggests using the generic `Find(selector)` method instead. Using the generic method is more concise, type-safe, and expresses intent more clearly.
+
+#### Examples
+
+**❌ Incorrect** - triggers BUNIT0002:
+```csharp
+using AngleSharp.Dom;
+
+var cut = RenderComponent();
+IHtmlAnchorElement link = (IHtmlAnchorElement)cut.Find("a");
+```
+
+**✅ Correct**:
+```csharp
+using AngleSharp.Dom;
+
+var cut = RenderComponent();
+var link = cut.Find("a");
+```
+
+#### When to Use
+
+Use `Find()` whenever you need a specific element type:
+- When working with AngleSharp element interfaces (`IHtmlAnchorElement`, `IHtmlButtonElement`, etc.)
+- When you need to access type-specific properties or methods
+- When you want clearer, more maintainable test code
+
+### BUNIT0001: Razor test files should inherit from BunitContext
+
+**Status**: 🚧 Planned for future release
+
+This analyzer will detect when Razor test files (`.razor` files) use variables or event callbacks from the test code without inheriting from `BunitContext`. Without the proper inheritance, you may encounter the error "The render handle is not yet assigned."
+
+#### Planned Examples
+
+**❌ Incorrect** - will trigger BUNIT0001 in the future:
+```razor
+@code
+{
+ [Fact]
+ public void Test()
+ {
+ using var ctx = new BunitContext();
+
+ Action onClickHandler = _ => { Assert.True(true); };
+
+ var cut = ctx.Render(@);
+ cut.Find("button").Click();
+ }
+}
+```
+
+**✅ Correct**:
+```razor
+@inherits BunitContext
+@code
+{
+ [Fact]
+ public async Task Test()
+ {
+ var wasInvoked = false;
+
+ Action onClick = _ => { wasInvoked = true; };
+
+ var cut = Render(@);
+ var button = cut.Find("button");
+ await button.ClickAsync(new MouseEventArgs());
+
+ cut.WaitForAssertion(() => Assert.True(wasInvoked));
+ }
+}
+```
+
+## Configuration
+
+The analyzers can be configured in your project's `.editorconfig` file or using ruleset files. For example, to change the severity of BUNIT0002:
+
+```ini
+# .editorconfig
+[*.cs]
+# Change BUNIT0002 from Info to Warning
+dotnet_diagnostic.BUNIT0002.severity = warning
+
+# Or disable it entirely
+dotnet_diagnostic.BUNIT0002.severity = none
+```
+
+## Contributing
+
+We welcome contributions! If you have ideas for additional analyzers that could help bUnit users, please:
+
+1. Check existing [issues](https://github.com/bunit-dev/bUnit/issues) for similar suggestions
+2. Open a new issue describing the problem the analyzer would solve
+3. Submit a pull request with your implementation
+
+Common areas for improvement include:
+- Detecting incorrect usage of `WaitFor` methods
+- Identifying missing or incorrect component parameter bindings
+- Catching improper service injection patterns
+- Finding opportunities to use helper methods
+
+## Feedback
+
+If you encounter issues with the analyzers or have suggestions for improvements, please [open an issue](https://github.com/bunit-dev/bUnit/issues/new) on GitHub.
diff --git a/docs/site/docs/extensions/index.md b/docs/site/docs/extensions/index.md
index c274a432a..c548eb598 100644
--- a/docs/site/docs/extensions/index.md
+++ b/docs/site/docs/extensions/index.md
@@ -7,4 +7,5 @@ title: Extensions for bUnit
This section covers the various extensions available for bUnit. These extensions are not part of the core bUnit package, but are instead available as separate NuGet packages. The extensions are listed below, and each has its own documentation page.
+ * **[bunit.analyzers](xref:bunit-analyzers)** - A set of Roslyn analyzers that help identify common mistakes and anti-patterns when writing bUnit tests
* **[bunit.generators](xref:bunit-generators)** - A set of source generators that can be used to generate code like stubs for Blazor components
\ No newline at end of file
diff --git a/src/bunit.analyzers/DiagnosticDescriptors.cs b/src/bunit.analyzers/DiagnosticDescriptors.cs
index b894a40bc..787e8d361 100644
--- a/src/bunit.analyzers/DiagnosticDescriptors.cs
+++ b/src/bunit.analyzers/DiagnosticDescriptors.cs
@@ -5,7 +5,7 @@ namespace Bunit.Analyzers;
///
/// Diagnostic descriptors for bUnit analyzers.
///
-internal static class DiagnosticDescriptors
+public static class DiagnosticDescriptors
{
private const string Category = "Usage";
diff --git a/tests/bunit.analyzers.tests/AnalyzerTests.cs b/tests/bunit.analyzers.tests/AnalyzerTests.cs
deleted file mode 100644
index 93b4bf4ff..000000000
--- a/tests/bunit.analyzers.tests/AnalyzerTests.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Xunit;
-
-namespace Bunit.Analyzers.Tests;
-
-public class AnalyzerTests
-{
- [Fact]
- public void AnalyzerPackage_Exists()
- {
- // Placeholder test to ensure the test project builds
- Assert.True(true);
- }
-}
diff --git a/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
new file mode 100644
index 000000000..427221395
--- /dev/null
+++ b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
@@ -0,0 +1,217 @@
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using Xunit;
+using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier;
+
+namespace Bunit.Analyzers.Tests;
+
+public class PreferGenericFindAnalyzerTests
+{
+ [Fact]
+ public async Task NoDiagnostic_WhenUsingGenericFind()
+ {
+ const string code = @"
+namespace TestNamespace
+{
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var cut = new TestHelper();
+ var elem = cut.Find(""a"");
+ }
+ }
+
+ public class TestHelper
+ {
+ public T Find(string selector) => default(T);
+ }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [Fact]
+ public async Task NoDiagnostic_WhenCastingNonFindMethod()
+ {
+ const string code = @"
+namespace TestNamespace
+{
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var obj = new TestHelper();
+ var elem = (string)obj.GetSomething();
+ }
+ }
+
+ public class TestHelper
+ {
+ public object GetSomething() => null;
+ }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [Fact]
+ public async Task NoDiagnostic_WhenFindIsNotFromRenderedFragment()
+ {
+ const string code = @"
+namespace TestNamespace
+{
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var helper = new UnrelatedHelper();
+ var result = (string)helper.Find(""test"");
+ }
+ }
+
+ public class UnrelatedHelper
+ {
+ public object Find(string selector) => null;
+ }
+}";
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [Fact]
+ public async Task Diagnostic_WhenCastingFindResultFromIRenderedFragment()
+ {
+ const string code = @"
+namespace TestNamespace
+{
+ public interface IMyElement { }
+
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var cut = new MockRenderedFragment();
+ IMyElement elem = {|#0:(IMyElement)cut.Find(""a"")|};
+ }
+ }
+
+ public interface IRenderedFragment
+ {
+ object Find(string selector);
+ }
+
+ public class MockRenderedFragment : IRenderedFragment
+ {
+ public object Find(string selector) => null;
+ }
+}";
+
+ var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+ .WithLocation(0)
+ .WithArguments("IMyElement", "\"a\"");
+
+ await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ }
+
+ [Fact]
+ public async Task Diagnostic_WhenCastingFindResultFromRenderedComponent()
+ {
+ const string code = @"
+namespace TestNamespace
+{
+ public interface IMyElement { }
+
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var cut = new MockRenderedComponent();
+ var elem = {|#0:(IMyElement)cut.Find(""div"")|};
+ }
+ }
+
+ public interface IRenderedComponent
+ {
+ object Find(string selector);
+ }
+
+ public class MockRenderedComponent : IRenderedComponent
+ {
+ public object Find(string selector) => null;
+ }
+}";
+
+ var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+ .WithLocation(0)
+ .WithArguments("IMyElement", "\"div\"");
+
+ await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ }
+
+ [Fact]
+ public async Task Diagnostic_WhenCastingFindResultFromRenderedFragmentType()
+ {
+ const string code = @"
+namespace TestNamespace
+{
+ public interface IMyElement { }
+
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var cut = new RenderedFragment();
+ var button = {|#0:(IMyElement)cut.Find(""button"")|};
+ }
+ }
+
+ public class RenderedFragment
+ {
+ public object Find(string selector) => null;
+ }
+}";
+
+ var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+ .WithLocation(0)
+ .WithArguments("IMyElement", "\"button\"");
+
+ await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ }
+
+ [Fact]
+ public async Task Diagnostic_WithComplexSelector()
+ {
+ const string code = @"
+namespace TestNamespace
+{
+ public interface IMyElement { }
+
+ public class TestClass
+ {
+ public void TestMethod()
+ {
+ var cut = new MockRenderedFragment();
+ var link = {|#0:(IMyElement)cut.Find(""a.nav-link[href='/home']"")|};
+ }
+ }
+
+ public interface IRenderedFragment
+ {
+ object Find(string selector);
+ }
+
+ public class MockRenderedFragment : IRenderedFragment
+ {
+ public object Find(string selector) => null;
+ }
+}";
+
+ var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+ .WithLocation(0)
+ .WithArguments("IMyElement", "\"a.nav-link[href='/home']\"");
+
+ await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ }
+}
diff --git a/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj b/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
index 1cab5e225..6db6f19b3 100644
--- a/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
+++ b/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
@@ -9,9 +9,12 @@
false
true
false
+ $(NoWarn);NU1701
+
+
From 0873523120ab62f2427e8cd8a5b10a2fc8fde7ec Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Dec 2025 14:53:39 +0000
Subject: [PATCH 6/9] docs: Add comment explaining public DiagnosticDescriptors
class
Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
---
src/bunit.analyzers/DiagnosticDescriptors.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/bunit.analyzers/DiagnosticDescriptors.cs b/src/bunit.analyzers/DiagnosticDescriptors.cs
index 787e8d361..5207f2d55 100644
--- a/src/bunit.analyzers/DiagnosticDescriptors.cs
+++ b/src/bunit.analyzers/DiagnosticDescriptors.cs
@@ -4,6 +4,7 @@ namespace Bunit.Analyzers;
///
/// Diagnostic descriptors for bUnit analyzers.
+/// Public to allow testing frameworks to reference diagnostic IDs and descriptors.
///
public static class DiagnosticDescriptors
{
From c24c14d7f0655252bac8e39b60f2f98e0232c45c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 21 Dec 2025 20:43:06 +0000
Subject: [PATCH 7/9] docs: Fix documentation style per review feedback
- Remove "you" form from documentation (use passive voice)
- Remove emojis from code examples
- Change RenderComponent to Render in examples
- Update test method names: RenderedFragment -> RenderedComponent
Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
---
docs/site/docs/extensions/bunit-analyzers.md | 26 +++++++++----------
.../PreferGenericFindAnalyzerTests.cs | 22 ++++++++--------
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/docs/site/docs/extensions/bunit-analyzers.md b/docs/site/docs/extensions/bunit-analyzers.md
index b5777c560..c9b58739f 100644
--- a/docs/site/docs/extensions/bunit-analyzers.md
+++ b/docs/site/docs/extensions/bunit-analyzers.md
@@ -26,42 +26,42 @@ The analyzers will automatically run during compilation and provide warnings or
**Severity**: Info
**Category**: Usage
-This analyzer detects when you cast the result of `Find(selector)` and suggests using the generic `Find(selector)` method instead. Using the generic method is more concise, type-safe, and expresses intent more clearly.
+This analyzer detects when a cast is applied to the result of `Find(selector)` and suggests using the generic `Find(selector)` method instead. Using the generic method is more concise, type-safe, and expresses intent more clearly.
#### Examples
-**❌ Incorrect** - triggers BUNIT0002:
+**Incorrect** - triggers BUNIT0002:
```csharp
using AngleSharp.Dom;
-var cut = RenderComponent();
+var cut = Render();
IHtmlAnchorElement link = (IHtmlAnchorElement)cut.Find("a");
```
-**✅ Correct**:
+**Correct**:
```csharp
using AngleSharp.Dom;
-var cut = RenderComponent();
+var cut = Render();
var link = cut.Find("a");
```
#### When to Use
-Use `Find()` whenever you need a specific element type:
+`Find()` should be used whenever a specific element type is needed:
- When working with AngleSharp element interfaces (`IHtmlAnchorElement`, `IHtmlButtonElement`, etc.)
-- When you need to access type-specific properties or methods
-- When you want clearer, more maintainable test code
+- When type-specific properties or methods need to be accessed
+- When clearer, more maintainable test code is desired
### BUNIT0001: Razor test files should inherit from BunitContext
-**Status**: 🚧 Planned for future release
+**Status**: Planned for future release
-This analyzer will detect when Razor test files (`.razor` files) use variables or event callbacks from the test code without inheriting from `BunitContext`. Without the proper inheritance, you may encounter the error "The render handle is not yet assigned."
+This analyzer will detect when Razor test files (`.razor` files) use variables or event callbacks from the test code without inheriting from `BunitContext`. Without the proper inheritance, the error "The render handle is not yet assigned" may be encountered.
#### Planned Examples
-**❌ Incorrect** - will trigger BUNIT0001 in the future:
+**Incorrect** - will trigger BUNIT0001 in the future:
```razor
@code
{
@@ -78,7 +78,7 @@ This analyzer will detect when Razor test files (`.razor` files) use variables o
}
```
-**✅ Correct**:
+**Correct**:
```razor
@inherits BunitContext
@code
@@ -101,7 +101,7 @@ This analyzer will detect when Razor test files (`.razor` files) use variables o
## Configuration
-The analyzers can be configured in your project's `.editorconfig` file or using ruleset files. For example, to change the severity of BUNIT0002:
+The analyzers can be configured in a project's `.editorconfig` file or using ruleset files. For example, to change the severity of BUNIT0002:
```ini
# .editorconfig
diff --git a/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
index 427221395..fde18cd44 100644
--- a/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
+++ b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
@@ -57,7 +57,7 @@ public class TestHelper
}
[Fact]
- public async Task NoDiagnostic_WhenFindIsNotFromRenderedFragment()
+ public async Task NoDiagnostic_WhenFindIsNotFromRenderedComponent()
{
const string code = @"
namespace TestNamespace
@@ -81,7 +81,7 @@ public class UnrelatedHelper
}
[Fact]
- public async Task Diagnostic_WhenCastingFindResultFromIRenderedFragment()
+ public async Task Diagnostic_WhenCastingFindResultFromIRenderedComponent()
{
const string code = @"
namespace TestNamespace
@@ -92,17 +92,17 @@ public class TestClass
{
public void TestMethod()
{
- var cut = new MockRenderedFragment();
+ var cut = new MockRenderedComponent();
IMyElement elem = {|#0:(IMyElement)cut.Find(""a"")|};
}
}
- public interface IRenderedFragment
+ public interface IRenderedComponent
{
object Find(string selector);
}
- public class MockRenderedFragment : IRenderedFragment
+ public class MockRenderedComponent : IRenderedComponent
{
public object Find(string selector) => null;
}
@@ -151,7 +151,7 @@ public class MockRenderedComponent : IRenderedComponent
}
[Fact]
- public async Task Diagnostic_WhenCastingFindResultFromRenderedFragmentType()
+ public async Task Diagnostic_WhenCastingFindResultFromRenderedComponentType()
{
const string code = @"
namespace TestNamespace
@@ -162,12 +162,12 @@ public class TestClass
{
public void TestMethod()
{
- var cut = new RenderedFragment();
+ var cut = new RenderedComponent();
var button = {|#0:(IMyElement)cut.Find(""button"")|};
}
}
- public class RenderedFragment
+ public class RenderedComponent
{
public object Find(string selector) => null;
}
@@ -192,17 +192,17 @@ public class TestClass
{
public void TestMethod()
{
- var cut = new MockRenderedFragment();
+ var cut = new MockRenderedComponent();
var link = {|#0:(IMyElement)cut.Find(""a.nav-link[href='/home']"")|};
}
}
- public interface IRenderedFragment
+ public interface IRenderedComponent
{
object Find(string selector);
}
- public class MockRenderedFragment : IRenderedFragment
+ public class MockRenderedComponent : IRenderedComponent
{
public object Find(string selector) => null;
}
From ac2d44a24641d5f7f8668e1271c7cba384dfd559 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 22 Dec 2025 18:44:20 +0000
Subject: [PATCH 8/9] fix: Replace obsolete AnalyzerVerifier with modern
CSharpAnalyzerTest
- Fix CS0618 warning by replacing obsolete Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier
- Use CSharpAnalyzerTest with DefaultVerifier instead
- Update all test code strings to use file-scoped namespaces
- Remove obsolete using alias pattern
Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
---
.../PreferGenericFindAnalyzerTests.cs | 246 +++++++++---------
1 file changed, 129 insertions(+), 117 deletions(-)
diff --git a/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
index fde18cd44..828f5d8d0 100644
--- a/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
+++ b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
@@ -1,8 +1,8 @@
using System.Threading.Tasks;
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
using Xunit;
-using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier;
namespace Bunit.Analyzers.Tests;
@@ -12,206 +12,218 @@ public class PreferGenericFindAnalyzerTests
public async Task NoDiagnostic_WhenUsingGenericFind()
{
const string code = @"
-namespace TestNamespace
+namespace TestNamespace;
+
+public class TestClass
{
- public class TestClass
+ public void TestMethod()
{
- public void TestMethod()
- {
- var cut = new TestHelper();
- var elem = cut.Find(""a"");
- }
+ var cut = new TestHelper();
+ var elem = cut.Find(""a"");
}
+}
- public class TestHelper
- {
- public T Find(string selector) => default(T);
- }
-}";
+public class TestHelper
+{
+ public T Find(string selector) => default(T);
+}
+";
- await VerifyCS.VerifyAnalyzerAsync(code);
+ await VerifyAnalyzerAsync(code);
}
[Fact]
public async Task NoDiagnostic_WhenCastingNonFindMethod()
{
const string code = @"
-namespace TestNamespace
+namespace TestNamespace;
+
+public class TestClass
{
- public class TestClass
+ public void TestMethod()
{
- public void TestMethod()
- {
- var obj = new TestHelper();
- var elem = (string)obj.GetSomething();
- }
+ var obj = new TestHelper();
+ var elem = (string)obj.GetSomething();
}
+}
- public class TestHelper
- {
- public object GetSomething() => null;
- }
-}";
+public class TestHelper
+{
+ public object GetSomething() => null;
+}
+";
- await VerifyCS.VerifyAnalyzerAsync(code);
+ await VerifyAnalyzerAsync(code);
}
[Fact]
public async Task NoDiagnostic_WhenFindIsNotFromRenderedComponent()
{
const string code = @"
-namespace TestNamespace
+namespace TestNamespace;
+
+public class TestClass
{
- public class TestClass
+ public void TestMethod()
{
- public void TestMethod()
- {
- var helper = new UnrelatedHelper();
- var result = (string)helper.Find(""test"");
- }
+ var helper = new UnrelatedHelper();
+ var result = (string)helper.Find(""test"");
}
+}
- public class UnrelatedHelper
- {
- public object Find(string selector) => null;
- }
-}";
+public class UnrelatedHelper
+{
+ public object Find(string selector) => null;
+}
+";
- await VerifyCS.VerifyAnalyzerAsync(code);
+ await VerifyAnalyzerAsync(code);
}
[Fact]
public async Task Diagnostic_WhenCastingFindResultFromIRenderedComponent()
{
const string code = @"
-namespace TestNamespace
-{
- public interface IMyElement { }
+namespace TestNamespace;
- public class TestClass
- {
- public void TestMethod()
- {
- var cut = new MockRenderedComponent();
- IMyElement elem = {|#0:(IMyElement)cut.Find(""a"")|};
- }
- }
+public interface IMyElement { }
- public interface IRenderedComponent
+public class TestClass
+{
+ public void TestMethod()
{
- object Find(string selector);
+ var cut = new MockRenderedComponent();
+ IMyElement elem = {|#0:(IMyElement)cut.Find(""a"")|};
}
+}
- public class MockRenderedComponent : IRenderedComponent
- {
- public object Find(string selector) => null;
- }
-}";
+public interface IRenderedComponent
+{
+ object Find(string selector);
+}
- var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+public class MockRenderedComponent : IRenderedComponent
+{
+ public object Find(string selector) => null;
+}
+";
+
+ var expected = new DiagnosticResult(DiagnosticDescriptors.PreferGenericFind)
.WithLocation(0)
.WithArguments("IMyElement", "\"a\"");
- await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ await VerifyAnalyzerAsync(code, expected);
}
[Fact]
public async Task Diagnostic_WhenCastingFindResultFromRenderedComponent()
{
const string code = @"
-namespace TestNamespace
-{
- public interface IMyElement { }
+namespace TestNamespace;
- public class TestClass
- {
- public void TestMethod()
- {
- var cut = new MockRenderedComponent();
- var elem = {|#0:(IMyElement)cut.Find(""div"")|};
- }
- }
+public interface IMyElement { }
- public interface IRenderedComponent
+public class TestClass
+{
+ public void TestMethod()
{
- object Find(string selector);
+ var cut = new MockRenderedComponent();
+ var elem = {|#0:(IMyElement)cut.Find(""div"")|};
}
+}
- public class MockRenderedComponent : IRenderedComponent
- {
- public object Find(string selector) => null;
- }
-}";
+public interface IRenderedComponent
+{
+ object Find(string selector);
+}
+
+public class MockRenderedComponent : IRenderedComponent
+{
+ public object Find(string selector) => null;
+}
+";
- var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+ var expected = new DiagnosticResult(DiagnosticDescriptors.PreferGenericFind)
.WithLocation(0)
.WithArguments("IMyElement", "\"div\"");
- await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ await VerifyAnalyzerAsync(code, expected);
}
[Fact]
public async Task Diagnostic_WhenCastingFindResultFromRenderedComponentType()
{
const string code = @"
-namespace TestNamespace
-{
- public interface IMyElement { }
+namespace TestNamespace;
- public class TestClass
- {
- public void TestMethod()
- {
- var cut = new RenderedComponent();
- var button = {|#0:(IMyElement)cut.Find(""button"")|};
- }
- }
+public interface IMyElement { }
- public class RenderedComponent
+public class TestClass
+{
+ public void TestMethod()
{
- public object Find(string selector) => null;
+ var cut = new RenderedComponent();
+ var button = {|#0:(IMyElement)cut.Find(""button"")|};
}
-}";
+}
+
+public class RenderedComponent
+{
+ public object Find(string selector) => null;
+}
+";
- var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+ var expected = new DiagnosticResult(DiagnosticDescriptors.PreferGenericFind)
.WithLocation(0)
.WithArguments("IMyElement", "\"button\"");
- await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ await VerifyAnalyzerAsync(code, expected);
}
[Fact]
public async Task Diagnostic_WithComplexSelector()
{
const string code = @"
-namespace TestNamespace
-{
- public interface IMyElement { }
+namespace TestNamespace;
- public class TestClass
- {
- public void TestMethod()
- {
- var cut = new MockRenderedComponent();
- var link = {|#0:(IMyElement)cut.Find(""a.nav-link[href='/home']"")|};
- }
- }
+public interface IMyElement { }
- public interface IRenderedComponent
+public class TestClass
+{
+ public void TestMethod()
{
- object Find(string selector);
+ var cut = new MockRenderedComponent();
+ var link = {|#0:(IMyElement)cut.Find(""a.nav-link[href='/home']"")|};
}
+}
- public class MockRenderedComponent : IRenderedComponent
- {
- public object Find(string selector) => null;
- }
-}";
+public interface IRenderedComponent
+{
+ object Find(string selector);
+}
- var expected = VerifyCS.Diagnostic(DiagnosticDescriptors.PreferGenericFind.Id)
+public class MockRenderedComponent : IRenderedComponent
+{
+ public object Find(string selector) => null;
+}
+";
+
+ var expected = new DiagnosticResult(DiagnosticDescriptors.PreferGenericFind)
.WithLocation(0)
.WithArguments("IMyElement", "\"a.nav-link[href='/home']\"");
- await VerifyCS.VerifyAnalyzerAsync(code, expected);
+ await VerifyAnalyzerAsync(code, expected);
+ }
+
+ private static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
+ {
+ var test = new CSharpAnalyzerTest
+ {
+ TestCode = source,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ return test.RunAsync();
}
}
From c4966073a9b2c23fd5fe1310de669f25c6106a79 Mon Sep 17 00:00:00 2001
From: Steven Giesel
Date: Fri, 26 Dec 2025 17:10:11 +0100
Subject: [PATCH 9/9] refactor: Use correct package
---
Directory.Packages.props | 2 +-
tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs | 1 -
tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj | 2 +-
3 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8aaa9b571..e5ee13825 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -102,6 +102,6 @@
-
+
diff --git a/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
index 828f5d8d0..af73e3af2 100644
--- a/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
+++ b/tests/bunit.analyzers.tests/PreferGenericFindAnalyzerTests.cs
@@ -1,7 +1,6 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
-using Microsoft.CodeAnalysis.Testing.Verifiers;
using Xunit;
namespace Bunit.Analyzers.Tests;
diff --git a/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj b/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
index 6db6f19b3..a04bf6dde 100644
--- a/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
+++ b/tests/bunit.analyzers.tests/bunit.analyzers.tests.csproj
@@ -14,7 +14,7 @@
-
+