Skip to content

Commit c4de756

Browse files
authored
Features/exceptions (#40)
* working analyzer * ExceptionShouldThrowWithMessage_TestCodeFix not implemented yet * support And.Message.Should() * oops * exception code-fix * failing tests should not crash the build * better AppVeyor usage * fixed StringShouldNotBeNullOrEmpty_TestCodeFix * fixed CollectionShouldNotBeNullOrEmpty_TestCodeFix * fixed DictionaryShouldContainPair_TestCodeFix * completed exception cases * cleanup
1 parent c595d34 commit c4de756

14 files changed

+534
-51
lines changed

build.cake

+11-5
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,18 @@ Task("Run-Unit-Tests")
6565
{
6666
Filter = "TestCategory=Completed",
6767
Configuration = configuration,
68-
ArgumentCustomization = builder => builder.Append($"--logger \"trx;LogFileName={testResults}\"")
68+
ArgumentCustomization = builder => builder.Append($"--logger \"trx;LogFileName={testResults}\"")
6969
});
70+
})
71+
.Finally(() => {
72+
if(AppVeyor.IsRunningOnAppVeyor)
73+
{
74+
AppVeyor.UploadTestResults(testResults, AppVeyorTestResultsType.MSTest);
75+
}
7076
});
7177

7278
Task("Pack")
79+
.IsDependentOn("Run-Unit-Tests")
7380
.Does(() =>
7481
{
7582
var nuGetPackSettings = new NuGetPackSettings
@@ -81,6 +88,7 @@ Task("Pack")
8188
});
8289

8390
Task("Publish-NuGet")
91+
.IsDependentOn("Pack")
8492
.WithCriteria(AppVeyor.IsRunningOnAppVeyor)
8593
.WithCriteria(HasEnvironmentVariable("NUGET_API_KEY"))
8694
.WithCriteria(HasEnvironmentVariable("NUGET_API_URL"))
@@ -106,10 +114,8 @@ Task("AppVeyor")
106114
.WithCriteria(AppVeyor.IsRunningOnAppVeyor)
107115
.Does(() =>
108116
{
109-
AppVeyor.UploadTestResults(testResults, AppVeyorTestResultsType.MSTest);
110-
111-
var nugetPackage = MakeAbsolute(buildDir + File($"./FluentAssertions.Analyzers.{version}.nupkg"));
112-
AppVeyor.UploadArtifact(nugetPackage, new AppVeyorUploadArtifactsSettings()
117+
var nupkgFile = File($"{buildDir}/FluentAssertions.Analyzers.{version}.nupkg");
118+
AppVeyor.UploadArtifact(nupkgFile, new AppVeyorUploadArtifactsSettings()
113119
.SetArtifactType(AppVeyorUploadArtifactType.NuGetPackage));
114120
});
115121

src/FluentAssertions.Analyzers.Tests/GenerateCode.cs

+16
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,22 @@ public static string EnumerableExpressionBodyAssertion(string assertion) => Enum
106106
.AppendLine("}")
107107
.ToString();
108108

109+
public static string ExceptionAssertion(string assertion) => new StringBuilder()
110+
.AppendLine("using System;")
111+
.AppendLine("using FluentAssertions;using FluentAssertions.Extensions;")
112+
.AppendLine("namespace TestNamespace")
113+
.AppendLine("{")
114+
.AppendLine(" class TestClass")
115+
.AppendLine(" {")
116+
.AppendLine(" void TestMethod(Action action, string expectedMessage)")
117+
.AppendLine(" {")
118+
.AppendLine($" {assertion}")
119+
.AppendLine(" }")
120+
.AppendLine(" }")
121+
.AppendMainMethod()
122+
.AppendLine("}")
123+
.ToString();
124+
109125
private static StringBuilder AppendMainMethod(this StringBuilder builder) => builder
110126
.AppendLine(" class Program")
111127
.AppendLine(" {")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
using FluentAssertions.Analyzers.Tips.Exceptions;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
5+
namespace FluentAssertions.Analyzers.Tests.Tips
6+
{
7+
[TestClass]
8+
public class ExceptionsTests
9+
{
10+
[AssertionDataTestMethod]
11+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().Contain(expectedMessage{0});")]
12+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().Contain(expectedMessage{0});")]
13+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().Contain(\"a constant string\"{0});")]
14+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().Contain(\"a constant string\"{0});")]
15+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().Be(expectedMessage{0});")]
16+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().Be(expectedMessage{0});")]
17+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().Be(\"a constant string\"{0});")]
18+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().Be(\"a constant string\"{0});")]
19+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().StartWith(expectedMessage{0});")]
20+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().StartWith(expectedMessage{0});")]
21+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().StartWith(\"a constant string\"{0});")]
22+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().StartWith(\"a constant string\"{0});")]
23+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().EndWith(expectedMessage{0});")]
24+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().EndWith(expectedMessage{0});")]
25+
[AssertionDiagnostic("action.Should().Throw<Exception>().Which.Message.Should().EndWith(\"a constant string\"{0});")]
26+
[AssertionDiagnostic("action.Should().Throw<Exception>().And.Message.Should().EndWith(\"a constant string\"{0});")]
27+
[Implemented]
28+
public void ExceptionShouldThrowWithMessage_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<ExceptionShouldThrowWithMessageAnalyzer>(assertion);
29+
30+
[AssertionDataTestMethod]
31+
[AssertionCodeFix(
32+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().Contain(expectedMessage{0});",
33+
newAssertion: "action.Should().Throw<Exception>().WithMessage($\"*{{expectedMessage}}*\"{0});")]
34+
[AssertionCodeFix(
35+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().Contain(expectedMessage{0});",
36+
newAssertion: "action.Should().Throw<Exception>().WithMessage($\"*{{expectedMessage}}*\"{0});")]
37+
[AssertionCodeFix(
38+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().Contain(\"a constant string\"{0});",
39+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"*a constant string*\"{0});")]
40+
[AssertionCodeFix(
41+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().Contain(\"a constant string\"{0});",
42+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"*a constant string*\"{0});")]
43+
[AssertionCodeFix(
44+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().Be(expectedMessage{0});",
45+
newAssertion: "action.Should().Throw<Exception>().WithMessage(expectedMessage{0});")]
46+
[AssertionCodeFix(
47+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().Be(expectedMessage{0});",
48+
newAssertion: "action.Should().Throw<Exception>().WithMessage(expectedMessage{0});")]
49+
[AssertionCodeFix(
50+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().Be(\"a constant string\"{0});",
51+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"a constant string\"{0});")]
52+
[AssertionCodeFix(
53+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().Be(\"a constant string\"{0});",
54+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"a constant string\"{0});")]
55+
[AssertionCodeFix(
56+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().StartWith(expectedMessage{0});",
57+
newAssertion: "action.Should().Throw<Exception>().WithMessage($\"{{expectedMessage}}*\"{0});")]
58+
[AssertionCodeFix(
59+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().StartWith(expectedMessage{0});",
60+
newAssertion: "action.Should().Throw<Exception>().WithMessage($\"{{expectedMessage}}*\"{0});")]
61+
[AssertionCodeFix(
62+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().StartWith(\"a constant string\"{0});",
63+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"a constant string*\"{0});")]
64+
[AssertionCodeFix(
65+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().StartWith(\"a constant string\"{0});",
66+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"a constant string*\"{0});")]
67+
[AssertionCodeFix(
68+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().EndWith(expectedMessage{0});",
69+
newAssertion: "action.Should().Throw<Exception>().WithMessage($\"*{{expectedMessage}}\"{0});")]
70+
[AssertionCodeFix(
71+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().EndWith(expectedMessage{0});",
72+
newAssertion: "action.Should().Throw<Exception>().WithMessage($\"*{{expectedMessage}}\"{0});")]
73+
[AssertionCodeFix(
74+
oldAssertion: "action.Should().Throw<Exception>().Which.Message.Should().EndWith(\"a constant string\"{0});",
75+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"*a constant string\"{0});")]
76+
[AssertionCodeFix(
77+
oldAssertion: "action.Should().Throw<Exception>().And.Message.Should().EndWith(\"a constant string\"{0});",
78+
newAssertion: "action.Should().Throw<Exception>().WithMessage(\"*a constant string\"{0});")]
79+
[Implemented]
80+
public void ExceptionShouldThrowWithMessage_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<ExceptionShouldThrowWithMessageCodeFix, ExceptionShouldThrowWithMessageAnalyzer>(oldAssertion, newAssertion);
81+
82+
[AssertionDataTestMethod]
83+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().Contain(expectedMessage{0});")]
84+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().Contain(expectedMessage{0});")]
85+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().Contain(\"a constant string\"{0});")]
86+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().Contain(\"a constant string\"{0});")]
87+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().Be(expectedMessage{0});")]
88+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().Be(expectedMessage{0});")]
89+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().Be(\"a constant string\"{0});")]
90+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().Be(\"a constant string\"{0});")]
91+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().StartWith(expectedMessage{0});")]
92+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().StartWith(expectedMessage{0});")]
93+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().StartWith(\"a constant string\"{0});")]
94+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().StartWith(\"a constant string\"{0});")]
95+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().EndWith(expectedMessage{0});")]
96+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().EndWith(expectedMessage{0});")]
97+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().Which.Message.Should().EndWith(\"a constant string\"{0});")]
98+
[AssertionDiagnostic("action.Should().ThrowExactly<Exception>().And.Message.Should().EndWith(\"a constant string\"{0});")]
99+
[Implemented]
100+
public void ExceptionShouldThrowExactlyWithMessage_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<ExceptionShouldThrowWithMessageAnalyzer>(assertion);
101+
102+
[AssertionDataTestMethod]
103+
[AssertionCodeFix(
104+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().Contain(expectedMessage{0});",
105+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage($\"*{{expectedMessage}}*\"{0});")]
106+
[AssertionCodeFix(
107+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().Contain(expectedMessage{0});",
108+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage($\"*{{expectedMessage}}*\"{0});")]
109+
[AssertionCodeFix(
110+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().Contain(\"a constant string\"{0});",
111+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"*a constant string*\"{0});")]
112+
[AssertionCodeFix(
113+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().Contain(\"a constant string\"{0});",
114+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"*a constant string*\"{0});")]
115+
[AssertionCodeFix(
116+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().Be(expectedMessage{0});",
117+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(expectedMessage{0});")]
118+
[AssertionCodeFix(
119+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().Be(expectedMessage{0});",
120+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(expectedMessage{0});")]
121+
[AssertionCodeFix(
122+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().Be(\"a constant string\"{0});",
123+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"a constant string\"{0});")]
124+
[AssertionCodeFix(
125+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().Be(\"a constant string\"{0});",
126+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"a constant string\"{0});")]
127+
[AssertionCodeFix(
128+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().StartWith(expectedMessage{0});",
129+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage($\"{{expectedMessage}}*\"{0});")]
130+
[AssertionCodeFix(
131+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().StartWith(expectedMessage{0});",
132+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage($\"{{expectedMessage}}*\"{0});")]
133+
[AssertionCodeFix(
134+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().StartWith(\"a constant string\"{0});",
135+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"a constant string*\"{0});")]
136+
[AssertionCodeFix(
137+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().StartWith(\"a constant string\"{0});",
138+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"a constant string*\"{0});")]
139+
[AssertionCodeFix(
140+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().EndWith(expectedMessage{0});",
141+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage($\"*{{expectedMessage}}\"{0});")]
142+
[AssertionCodeFix(
143+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().EndWith(expectedMessage{0});",
144+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage($\"*{{expectedMessage}}\"{0});")]
145+
[AssertionCodeFix(
146+
oldAssertion: "action.Should().ThrowExactly<Exception>().Which.Message.Should().EndWith(\"a constant string\"{0});",
147+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"*a constant string\"{0});")]
148+
[AssertionCodeFix(
149+
oldAssertion: "action.Should().ThrowExactly<Exception>().And.Message.Should().EndWith(\"a constant string\"{0});",
150+
newAssertion: "action.Should().ThrowExactly<Exception>().WithMessage(\"*a constant string\"{0});")]
151+
[Implemented]
152+
public void ExceptionShouldThrowExactlyWithMessage_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<ExceptionShouldThrowWithMessageCodeFix, ExceptionShouldThrowWithMessageAnalyzer>(oldAssertion, newAssertion);
153+
154+
private void VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string sourceAssersion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
155+
{
156+
System.Console.WriteLine(sourceAssersion);
157+
var source = GenerateCode.ExceptionAssertion(sourceAssersion);
158+
159+
var type = typeof(TDiagnosticAnalyzer);
160+
var diagnosticId = (string)type.GetField("DiagnosticId").GetValue(null);
161+
var message = (string)type.GetField("Message").GetValue(null);
162+
163+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source, new DiagnosticResult
164+
{
165+
Id = diagnosticId,
166+
Message = message,
167+
Locations = new DiagnosticResultLocation[]
168+
{
169+
new DiagnosticResultLocation("Test0.cs", 9,13)
170+
},
171+
Severity = DiagnosticSeverity.Info
172+
});
173+
}
174+
175+
private void VerifyCSharpFix<TCodeFixProvider, TDiagnosticAnalyzer>(string oldSourceAssertion, string newSourceAssertion)
176+
where TCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, new()
177+
where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
178+
{
179+
var oldSource = GenerateCode.ExceptionAssertion(oldSourceAssertion);
180+
var newSource = GenerateCode.ExceptionAssertion(newSourceAssertion);
181+
182+
DiagnosticVerifier.VerifyCSharpFix<TCodeFixProvider, TDiagnosticAnalyzer>(oldSource, newSource);
183+
}
184+
}
185+
}

src/FluentAssertions.Analyzers/Constants.cs

+5
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ public static class Numeric
8383
public const string NumericShouldBeInRange = nameof(NumericShouldBeInRange);
8484
public const string NumericShouldBeApproximately = nameof(NumericShouldBeApproximately);
8585
}
86+
87+
public static class Exceptions
88+
{
89+
public const string ExceptionShouldThrowWithMessage = nameof(ExceptionShouldThrowWithMessage);
90+
}
8691
}
8792

8893
public static class CodeSmell

src/FluentAssertions.Analyzers/Tips/Collections/CollectionShouldBeEmpty.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression
7171

7272
private class HaveCountNodeReplacement : NodeReplacement
7373
{
74-
public override bool IsValidNode(MemberAccessExpressionSyntax node) => node.Name.Identifier.Text == "HaveCount";
74+
public override bool IsValidNode(LinkedListNode<MemberAccessExpressionSyntax> listNode) => listNode.Value.Name.Identifier.Text == "HaveCount";
7575
public override SyntaxNode ComputeOld(LinkedListNode<MemberAccessExpressionSyntax> listNode) => listNode.Value.Parent;
7676
public override SyntaxNode ComputeNew(LinkedListNode<MemberAccessExpressionSyntax> listNode)
7777
{

0 commit comments

Comments
 (0)