Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: detect namespace-scoped TUnit.AspNetCore usings when applying co…
…de fix

Also guards against ignoring pre-existing top-level usings and adds tests
for both cases (top-level + namespace-scoped) so BatchFixer can't emit
duplicate directives.
  • Loading branch information
thomhurst committed Apr 17, 2026
commit 5aeb7cdeb491b0ddfe511363b8265a6f61717c1f
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ private static async Task<Document> ReplaceBaseTypeAsync(

private static CompilationUnitSyntax AddUsingIfMissing(CompilationUnitSyntax compilationUnit, string namespaceName)
{
if (compilationUnit.Usings.Any(u => u.Name?.ToString() == namespaceName))
if (ContainsUsing(compilationUnit.Usings, namespaceName) ||
compilationUnit.Members.Any(m => ContainsUsingInNamespace(m, namespaceName)))
{
return compilationUnit;
}
Expand All @@ -101,4 +102,27 @@ private static CompilationUnitSyntax AddUsingIfMissing(CompilationUnitSyntax com

return compilationUnit.AddUsings(newUsing);
}

private static bool ContainsUsingInNamespace(MemberDeclarationSyntax member, string namespaceName) => member switch
{
BaseNamespaceDeclarationSyntax ns =>
ContainsUsing(ns.Usings, namespaceName) ||
ns.Members.Any(m => ContainsUsingInNamespace(m, namespaceName)),
_ => false,
};

private static bool ContainsUsing(SyntaxList<UsingDirectiveSyntax> usings, string namespaceName)
{
foreach (var directive in usings)
{
if (directive.Alias is null &&
directive.StaticKeyword.IsKind(SyntaxKind.None) &&
directive.Name?.ToString() == namespaceName)
{
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,72 @@ namespace TUnit.AspNetCore.Analyzers.Tests;

public class UseTestWebApplicationFactoryCodeFixProviderTests
{
[Test]
public async Task Does_Not_Duplicate_Existing_Using()
{
var source = $$"""
using TUnit.AspNetCore;
{{WebApplicationFactoryStubs.Source}}

public class MyFactory : {|#0:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<Program>|}
{
}
""";

var fixedSource = $$"""
using TUnit.AspNetCore;
{{WebApplicationFactoryStubs.Source}}

public class MyFactory : TestWebApplicationFactory<Program>
{
}
""";

await Verifier.VerifyCodeFixAsync(
source,
fixedSource,
Verifier.Diagnostic(Rules.DirectWebApplicationFactoryInheritance)
.WithLocation(0)
.WithArguments("MyFactory"));
}

[Test]
public async Task Does_Not_Duplicate_Using_When_Imported_Inside_Namespace()
{
var source = $$"""
{{WebApplicationFactoryStubs.Source}}

namespace App
{
using TUnit.AspNetCore;

public class MyFactory : {|#0:Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<Program>|}
{
}
}
""";

var fixedSource = $$"""
{{WebApplicationFactoryStubs.Source}}

namespace App
{
using TUnit.AspNetCore;

public class MyFactory : TestWebApplicationFactory<Program>
{
}
}
""";

await Verifier.VerifyCodeFixAsync(
source,
fixedSource,
Verifier.Diagnostic(Rules.DirectWebApplicationFactoryInheritance)
.WithLocation(0)
.WithArguments("MyFactory"));
}

[Test]
public async Task Rewrites_Base_Type_To_TestWebApplicationFactory()
{
Expand Down
Loading