Skip to content

Commit

Permalink
feat: add nunit boolean assertions (#291)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meir017 authored Jan 14, 2024
1 parent 0522cc9 commit 2e2d70c
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 11 deletions.
21 changes: 21 additions & 0 deletions src/FluentAssertions.Analyzers.TestUtils/GenerateCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,5 +233,26 @@ public static string GenericIListExpressionBodyAssertion(string assertion) => Ge
.AppendLine(" }")
.AppendLine("}")
.ToString();

public static string Nunit3Assertion(string methodArguments, string assertion) => new StringBuilder()
.AppendLine("using System;")
.AppendLine("using System.Collections.Generic;")
.AppendLine("using System.Collections.Immutable;")
.AppendLine("using System.Text.RegularExpressions;")
.AppendLine("using FluentAssertions;")
.AppendLine("using FluentAssertions.Extensions;")
.AppendLine("using NUnit.Framework;")
.AppendLine("using System.Threading.Tasks;")
.AppendLine("namespace TestNamespace")
.AppendLine("{")
.AppendLine(" class TestClass")
.AppendLine(" {")
.AppendLine($" void TestMethod({methodArguments})")
.AppendLine(" {")
.AppendLine($" {assertion}")
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}")
.ToString();
}
}
2 changes: 2 additions & 0 deletions src/FluentAssertions.Analyzers.TestUtils/PackageReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class PackageReference

public static PackageReference MSTestTestFramework_3_1_1 { get; } = new("MSTest.TestFramework", "3.1.1", "lib/netstandard2.0/");
public static PackageReference XunitAssert_2_5_1 { get; } = new("xunit.assert", "2.5.1", "lib/netstandard1.1/");
public static PackageReference Nunit_3_14_0 { get; } = new("NUnit", "3.14.0", "lib/netstandard2.0/");
public static PackageReference Nunit_4_0_1 { get; } = new("NUnit", "4.0.1", "lib/net6.0/");

public static PackageReference FluentAssertions(string version) => new("FluentAssertions", version, "lib/netstandard2.0/");
}
106 changes: 106 additions & 0 deletions src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using FluentAssertions.Analyzers.TestUtils;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FluentAssertions.Analyzers.Tests.Tips;

[TestClass]
public class NunitTests
{

[DataTestMethod]
[AssertionDiagnostic("Assert.True(actual{0});")]
[AssertionDiagnostic("Assert.True(bool.Parse(\"true\"){0});")]
[AssertionDiagnostic("Assert.IsTrue(actual{0});")]
[AssertionDiagnostic("Assert.IsTrue(bool.Parse(\"true\"){0});")]
[Implemented]
public void AssertTrue_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("bool actual", assertion);

[DataTestMethod]
[AssertionCodeFix(
oldAssertion: "Assert.True(actual{0});",
newAssertion: "actual.Should().BeTrue({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.True(bool.Parse(\"true\"){0});",
newAssertion: "bool.Parse(\"true\").Should().BeTrue({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.True(!actual{0});",
newAssertion: "(!actual).Should().BeTrue({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.True(actual == false{0});",
newAssertion: "(actual == false).Should().BeTrue({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.IsTrue(actual{0});",
newAssertion: "actual.Should().BeTrue({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.IsTrue(bool.Parse(\"true\"){0});",
newAssertion: "bool.Parse(\"true\").Should().BeTrue({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.IsTrue(!actual{0});",
newAssertion: "(!actual).Should().BeTrue({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.IsTrue(actual == false{0});",
newAssertion: "(actual == false).Should().BeTrue({0});")]
[Implemented]
public void AssertTrue_TestCodeFix(string oldAssertion, string newAssertion)
=> VerifyCSharpFix("bool actual", oldAssertion, newAssertion);

[DataTestMethod]
[AssertionDiagnostic("Assert.False(actual{0});")]
[AssertionDiagnostic("Assert.False(bool.Parse(\"false\"){0});")]
[AssertionDiagnostic("Assert.IsFalse(actual{0});")]
[AssertionDiagnostic("Assert.IsFalse(bool.Parse(\"false\"){0});")]
[Implemented]
public void AssertFalse_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("bool actual", assertion);

[DataTestMethod]
[AssertionCodeFix(
oldAssertion: "Assert.False(actual{0});",
newAssertion: "actual.Should().BeFalse({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.False(bool.Parse(\"false\"){0});",
newAssertion: "bool.Parse(\"false\").Should().BeFalse({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.IsFalse(actual{0});",
newAssertion: "actual.Should().BeFalse({0});")]
[AssertionCodeFix(
oldAssertion: "Assert.IsFalse(bool.Parse(\"false\"){0});",
newAssertion: "bool.Parse(\"false\").Should().BeFalse({0});")]
[Implemented]
public void AssertFalse_TestCodeFix(string oldAssertion, string newAssertion)
=> VerifyCSharpFix("bool actual", oldAssertion, newAssertion);

private void VerifyCSharpDiagnostic(string methodArguments, string assertion)
{
var source = GenerateCode.Nunit3Assertion(methodArguments, assertion);
DiagnosticVerifier.VerifyDiagnostic(new DiagnosticVerifierArguments()
.WithAllAnalyzers()
.WithSources(source)
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, PackageReference.Nunit_3_14_0)
.WithExpectedDiagnostics(new DiagnosticResult
{
Id = AssertAnalyzer.NUnitRule.Id,
Message = AssertAnalyzer.Message,
Locations = new DiagnosticResultLocation[]
{
new("Test0.cs", 15, 13)
},
Severity = DiagnosticSeverity.Info
})
);
}

private void VerifyCSharpFix(string methodArguments, string oldAssertion, string newAssertion)
{
var oldSource = GenerateCode.Nunit3Assertion(methodArguments, oldAssertion);
var newSource = GenerateCode.Nunit3Assertion(methodArguments, newAssertion);

DiagnosticVerifier.VerifyFix(new CodeFixVerifierArguments()
.WithDiagnosticAnalyzer<AssertAnalyzer>()
.WithCodeFixProvider<NunitCodeFixProvider>()
.WithSources(oldSource)
.WithFixedSources(newSource)
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, PackageReference.Nunit_3_14_0)
);
}
}
13 changes: 6 additions & 7 deletions src/FluentAssertions.Analyzers/Tips/AssertAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,12 @@ public override void Initialize(AnalysisContext context)
context.RegisterOperationAction(analyzerContext.AnalyzeMsTestThrow, OperationKind.Throw);
}

// TODO: enable NUnit
// if (analyzerContext.IsNUnitAvailable)
// {
// context.RegisterOperationAction(analyzerContext.AnalyzeNunitInvocation, OperationKind.Invocation);
// context.RegisterOperationAction(analyzerContext.AnalyzeNunitDynamicInvocation, OperationKind.DynamicInvocation);
// context.RegisterOperationAction(analyzerContext.AnalyzeNunitThrow, OperationKind.Throw);
// }
if (analyzerContext.IsNUnitAvailable)
{
context.RegisterOperationAction(analyzerContext.AnalyzeNunitInvocation, OperationKind.Invocation);
context.RegisterOperationAction(analyzerContext.AnalyzeNunitDynamicInvocation, OperationKind.DynamicInvocation);
context.RegisterOperationAction(analyzerContext.AnalyzeNunitThrow, OperationKind.Throw);
}
});
}

Expand Down
8 changes: 4 additions & 4 deletions src/FluentAssertions.Analyzers/Tips/MsTestCodeFixProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ public class MsTestCodeFixProvider : TestingFrameworkCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertAnalyzer.MSTestsRule.Id);

protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext testContext, Diagnostic diagnostic)
protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic)
{
var assertType = invocation.TargetMethod.ContainingType;
return assertType.Name switch
{
"Assert" => TryComputeFixForAssert(invocation, context, testContext),
"StringAssert" => TryComputeFixForStringAssert(invocation, context, testContext),
"CollectionAssert" => TryComputeFixForCollectionAssert(invocation, context, testContext),
"Assert" => TryComputeFixForAssert(invocation, context, t),
"StringAssert" => TryComputeFixForStringAssert(invocation, context, t),
"CollectionAssert" => TryComputeFixForCollectionAssert(invocation, context, t),
_ => null
};
}
Expand Down
42 changes: 42 additions & 0 deletions src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Operations;
using CreateChangedDocument = System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Document>>;
using System;

namespace FluentAssertions.Analyzers;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NunitCodeFixProvider)), Shared]
public class NunitCodeFixProvider : TestingFrameworkCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertAnalyzer.NUnitRule.Id);
protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic)
{
var assertType = invocation.TargetMethod.ContainingType;
return assertType.Name switch
{
"Assert" => TryComputeFixForAssert(invocation, context, t),
//"StringAssert" => TryComputeFixForStringAssert(invocation, context, testContext),
//"CollectionAssert" => TryComputeFixForCollectionAssert(invocation, context, testContext),
_ => null
};
}

private CreateChangedDocument TryComputeFixForAssert(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t)
{
switch (invocation.TargetMethod.Name)
{
case "True":
case "IsTrue":
return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeTrue", subjectIndex: 0, argumentsToRemove: []);

case "False":
case "IsFalse":
return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeFalse", subjectIndex: 0, argumentsToRemove: []);
}

return null;
}
}

0 comments on commit 2e2d70c

Please sign in to comment.