Skip to content

Commit 2e2d70c

Browse files
authored
feat: add nunit boolean assertions (#291)
1 parent 0522cc9 commit 2e2d70c

File tree

6 files changed

+181
-11
lines changed

6 files changed

+181
-11
lines changed

src/FluentAssertions.Analyzers.TestUtils/GenerateCode.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,5 +233,26 @@ public static string GenericIListExpressionBodyAssertion(string assertion) => Ge
233233
.AppendLine(" }")
234234
.AppendLine("}")
235235
.ToString();
236+
237+
public static string Nunit3Assertion(string methodArguments, string assertion) => new StringBuilder()
238+
.AppendLine("using System;")
239+
.AppendLine("using System.Collections.Generic;")
240+
.AppendLine("using System.Collections.Immutable;")
241+
.AppendLine("using System.Text.RegularExpressions;")
242+
.AppendLine("using FluentAssertions;")
243+
.AppendLine("using FluentAssertions.Extensions;")
244+
.AppendLine("using NUnit.Framework;")
245+
.AppendLine("using System.Threading.Tasks;")
246+
.AppendLine("namespace TestNamespace")
247+
.AppendLine("{")
248+
.AppendLine(" class TestClass")
249+
.AppendLine(" {")
250+
.AppendLine($" void TestMethod({methodArguments})")
251+
.AppendLine(" {")
252+
.AppendLine($" {assertion}")
253+
.AppendLine(" }")
254+
.AppendLine(" }")
255+
.AppendLine("}")
256+
.ToString();
236257
}
237258
}

src/FluentAssertions.Analyzers.TestUtils/PackageReference.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class PackageReference
1010

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

1416
public static PackageReference FluentAssertions(string version) => new("FluentAssertions", version, "lib/netstandard2.0/");
1517
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using FluentAssertions.Analyzers.TestUtils;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
5+
namespace FluentAssertions.Analyzers.Tests.Tips;
6+
7+
[TestClass]
8+
public class NunitTests
9+
{
10+
11+
[DataTestMethod]
12+
[AssertionDiagnostic("Assert.True(actual{0});")]
13+
[AssertionDiagnostic("Assert.True(bool.Parse(\"true\"){0});")]
14+
[AssertionDiagnostic("Assert.IsTrue(actual{0});")]
15+
[AssertionDiagnostic("Assert.IsTrue(bool.Parse(\"true\"){0});")]
16+
[Implemented]
17+
public void AssertTrue_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("bool actual", assertion);
18+
19+
[DataTestMethod]
20+
[AssertionCodeFix(
21+
oldAssertion: "Assert.True(actual{0});",
22+
newAssertion: "actual.Should().BeTrue({0});")]
23+
[AssertionCodeFix(
24+
oldAssertion: "Assert.True(bool.Parse(\"true\"){0});",
25+
newAssertion: "bool.Parse(\"true\").Should().BeTrue({0});")]
26+
[AssertionCodeFix(
27+
oldAssertion: "Assert.True(!actual{0});",
28+
newAssertion: "(!actual).Should().BeTrue({0});")]
29+
[AssertionCodeFix(
30+
oldAssertion: "Assert.True(actual == false{0});",
31+
newAssertion: "(actual == false).Should().BeTrue({0});")]
32+
[AssertionCodeFix(
33+
oldAssertion: "Assert.IsTrue(actual{0});",
34+
newAssertion: "actual.Should().BeTrue({0});")]
35+
[AssertionCodeFix(
36+
oldAssertion: "Assert.IsTrue(bool.Parse(\"true\"){0});",
37+
newAssertion: "bool.Parse(\"true\").Should().BeTrue({0});")]
38+
[AssertionCodeFix(
39+
oldAssertion: "Assert.IsTrue(!actual{0});",
40+
newAssertion: "(!actual).Should().BeTrue({0});")]
41+
[AssertionCodeFix(
42+
oldAssertion: "Assert.IsTrue(actual == false{0});",
43+
newAssertion: "(actual == false).Should().BeTrue({0});")]
44+
[Implemented]
45+
public void AssertTrue_TestCodeFix(string oldAssertion, string newAssertion)
46+
=> VerifyCSharpFix("bool actual", oldAssertion, newAssertion);
47+
48+
[DataTestMethod]
49+
[AssertionDiagnostic("Assert.False(actual{0});")]
50+
[AssertionDiagnostic("Assert.False(bool.Parse(\"false\"){0});")]
51+
[AssertionDiagnostic("Assert.IsFalse(actual{0});")]
52+
[AssertionDiagnostic("Assert.IsFalse(bool.Parse(\"false\"){0});")]
53+
[Implemented]
54+
public void AssertFalse_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("bool actual", assertion);
55+
56+
[DataTestMethod]
57+
[AssertionCodeFix(
58+
oldAssertion: "Assert.False(actual{0});",
59+
newAssertion: "actual.Should().BeFalse({0});")]
60+
[AssertionCodeFix(
61+
oldAssertion: "Assert.False(bool.Parse(\"false\"){0});",
62+
newAssertion: "bool.Parse(\"false\").Should().BeFalse({0});")]
63+
[AssertionCodeFix(
64+
oldAssertion: "Assert.IsFalse(actual{0});",
65+
newAssertion: "actual.Should().BeFalse({0});")]
66+
[AssertionCodeFix(
67+
oldAssertion: "Assert.IsFalse(bool.Parse(\"false\"){0});",
68+
newAssertion: "bool.Parse(\"false\").Should().BeFalse({0});")]
69+
[Implemented]
70+
public void AssertFalse_TestCodeFix(string oldAssertion, string newAssertion)
71+
=> VerifyCSharpFix("bool actual", oldAssertion, newAssertion);
72+
73+
private void VerifyCSharpDiagnostic(string methodArguments, string assertion)
74+
{
75+
var source = GenerateCode.Nunit3Assertion(methodArguments, assertion);
76+
DiagnosticVerifier.VerifyDiagnostic(new DiagnosticVerifierArguments()
77+
.WithAllAnalyzers()
78+
.WithSources(source)
79+
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, PackageReference.Nunit_3_14_0)
80+
.WithExpectedDiagnostics(new DiagnosticResult
81+
{
82+
Id = AssertAnalyzer.NUnitRule.Id,
83+
Message = AssertAnalyzer.Message,
84+
Locations = new DiagnosticResultLocation[]
85+
{
86+
new("Test0.cs", 15, 13)
87+
},
88+
Severity = DiagnosticSeverity.Info
89+
})
90+
);
91+
}
92+
93+
private void VerifyCSharpFix(string methodArguments, string oldAssertion, string newAssertion)
94+
{
95+
var oldSource = GenerateCode.Nunit3Assertion(methodArguments, oldAssertion);
96+
var newSource = GenerateCode.Nunit3Assertion(methodArguments, newAssertion);
97+
98+
DiagnosticVerifier.VerifyFix(new CodeFixVerifierArguments()
99+
.WithDiagnosticAnalyzer<AssertAnalyzer>()
100+
.WithCodeFixProvider<NunitCodeFixProvider>()
101+
.WithSources(oldSource)
102+
.WithFixedSources(newSource)
103+
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, PackageReference.Nunit_3_14_0)
104+
);
105+
}
106+
}

src/FluentAssertions.Analyzers/Tips/AssertAnalyzer.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,12 @@ public override void Initialize(AnalysisContext context)
8888
context.RegisterOperationAction(analyzerContext.AnalyzeMsTestThrow, OperationKind.Throw);
8989
}
9090

91-
// TODO: enable NUnit
92-
// if (analyzerContext.IsNUnitAvailable)
93-
// {
94-
// context.RegisterOperationAction(analyzerContext.AnalyzeNunitInvocation, OperationKind.Invocation);
95-
// context.RegisterOperationAction(analyzerContext.AnalyzeNunitDynamicInvocation, OperationKind.DynamicInvocation);
96-
// context.RegisterOperationAction(analyzerContext.AnalyzeNunitThrow, OperationKind.Throw);
97-
// }
91+
if (analyzerContext.IsNUnitAvailable)
92+
{
93+
context.RegisterOperationAction(analyzerContext.AnalyzeNunitInvocation, OperationKind.Invocation);
94+
context.RegisterOperationAction(analyzerContext.AnalyzeNunitDynamicInvocation, OperationKind.DynamicInvocation);
95+
context.RegisterOperationAction(analyzerContext.AnalyzeNunitThrow, OperationKind.Throw);
96+
}
9897
});
9998
}
10099

src/FluentAssertions.Analyzers/Tips/MsTestCodeFixProvider.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ public class MsTestCodeFixProvider : TestingFrameworkCodeFixProvider
1212
{
1313
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertAnalyzer.MSTestsRule.Id);
1414

15-
protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext testContext, Diagnostic diagnostic)
15+
protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic)
1616
{
1717
var assertType = invocation.TargetMethod.ContainingType;
1818
return assertType.Name switch
1919
{
20-
"Assert" => TryComputeFixForAssert(invocation, context, testContext),
21-
"StringAssert" => TryComputeFixForStringAssert(invocation, context, testContext),
22-
"CollectionAssert" => TryComputeFixForCollectionAssert(invocation, context, testContext),
20+
"Assert" => TryComputeFixForAssert(invocation, context, t),
21+
"StringAssert" => TryComputeFixForStringAssert(invocation, context, t),
22+
"CollectionAssert" => TryComputeFixForCollectionAssert(invocation, context, t),
2323
_ => null
2424
};
2525
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Collections.Immutable;
2+
using System.Composition;
3+
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CodeFixes;
5+
using Microsoft.CodeAnalysis.Operations;
6+
using CreateChangedDocument = System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Document>>;
7+
using System;
8+
9+
namespace FluentAssertions.Analyzers;
10+
11+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NunitCodeFixProvider)), Shared]
12+
public class NunitCodeFixProvider : TestingFrameworkCodeFixProvider
13+
{
14+
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertAnalyzer.NUnitRule.Id);
15+
protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t, Diagnostic diagnostic)
16+
{
17+
var assertType = invocation.TargetMethod.ContainingType;
18+
return assertType.Name switch
19+
{
20+
"Assert" => TryComputeFixForAssert(invocation, context, t),
21+
//"StringAssert" => TryComputeFixForStringAssert(invocation, context, testContext),
22+
//"CollectionAssert" => TryComputeFixForCollectionAssert(invocation, context, testContext),
23+
_ => null
24+
};
25+
}
26+
27+
private CreateChangedDocument TryComputeFixForAssert(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t)
28+
{
29+
switch (invocation.TargetMethod.Name)
30+
{
31+
case "True":
32+
case "IsTrue":
33+
return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeTrue", subjectIndex: 0, argumentsToRemove: []);
34+
35+
case "False":
36+
case "IsFalse":
37+
return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeFalse", subjectIndex: 0, argumentsToRemove: []);
38+
}
39+
40+
return null;
41+
}
42+
}

0 commit comments

Comments
 (0)