Skip to content

Commit 4b678fc

Browse files
committed
added skeleton for dictionaries
1 parent 9d7ba5d commit 4b678fc

9 files changed

+425
-2
lines changed

src/FluentAssertions.BestPractices.Tests/GenerateCode.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace FluentAssertions.BestPractices.Tests
55
{
66
public static class GenerateCode
7-
{
7+
{
88
public static string NamespaceName => "TestNamespace";
99
public static string ClassName => "TestClass";
1010
public static string MethodName => "TestMethod";
@@ -18,7 +18,7 @@ public static class GenerateCode
1818

1919
public static string ComplexClassName => "TestComplexClass";
2020
public static string ComplexClassBooleanpropertyName => "BooleanProperty";
21-
21+
2222
public static string EnumerableAssertion(string assertion) => new StringBuilder()
2323
.AppendLine("using System.Collections.Generic;")
2424
.AppendLine("using System.Linq;")
@@ -46,6 +46,33 @@ public static class GenerateCode
4646
.AppendLine("}")
4747
.ToString();
4848

49+
public static string DictionaryAssertion(string assertion) => new StringBuilder()
50+
.AppendLine("using System.Collections.Generic;")
51+
.AppendLine("using System.Linq;")
52+
.AppendLine("using System;")
53+
.AppendLine("using FluentAssertions;")
54+
.AppendLine($"namespace {NamespaceName}")
55+
.AppendLine("{")
56+
.AppendLine($" public class {ClassName}")
57+
.AppendLine(" {")
58+
.AppendLine($" public void {MethodName}({nameof(IDictionary)}<string, {ComplexClassName}> {ActualVariableName}, {nameof(IDictionary)}<string, {ComplexClassName}> {ExpectedVariableName}, {nameof(IList)}<{ComplexClassName}> {UnexpectedVariableName}, {ComplexClassName} {ExpectedItemVariableName}, {ComplexClassName} {UnexpectedItemVariableName}, int {CountVariable})")
59+
.AppendLine(" {")
60+
.AppendLine($" {assertion}")
61+
.AppendLine(" }")
62+
.AppendLine(" }")
63+
.AppendLine($" public class {ComplexClassName}")
64+
.AppendLine(" {")
65+
.AppendLine($" public bool {ComplexClassBooleanpropertyName} {{ get; set; }}")
66+
.AppendLine(" }")
67+
.AppendLine(" class Program")
68+
.AppendLine(" {")
69+
.AppendLine($" public static void Main()")
70+
.AppendLine(" {")
71+
.AppendLine(" }")
72+
.AppendLine(" }")
73+
.AppendLine("}")
74+
.ToString();
75+
4976
public static string NumericAssertion(string assertion) => new StringBuilder()
5077
.AppendLine($"namespace {NamespaceName}")
5178
.AppendLine("{")
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace FluentAssertions.BestPractices.Tests
10+
{
11+
[TestClass]
12+
public class DictionaryTests
13+
{
14+
[TestMethod]
15+
[AssertionDiagnostic("actual.ContainsKey(expected).Should().BeTrue();")]
16+
[NotImplemented]
17+
public void DictionaryShouldContainKey_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<DictionaryShouldContainKeyAnalyzer>(assertion);
18+
19+
[TestMethod]
20+
[AssertionCodeFix(
21+
oldAssertion: "actual.ContainsKey(expected).Should().BeTrue();",
22+
newAssertion: "actual.Should().ContainKey(expected);")]
23+
[NotImplemented]
24+
public void DictionaryShouldContainKey_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<DictionaryShouldContainKeyCodeFix, DictionaryShouldContainKeyAnalyzer>(oldAssertion, newAssertion);
25+
26+
[TestMethod]
27+
[AssertionDiagnostic("actual.ContainsKey(expected).Should().BeFalse();")]
28+
[NotImplemented]
29+
public void DictionaryShouldNotContainKey_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<DictionaryShouldNotContainKeyAnalyzer>(assertion);
30+
31+
[TestMethod]
32+
[AssertionCodeFix(
33+
oldAssertion: "actual.ContainsKey(expected).Should().BeFalse();",
34+
newAssertion: "actual.Should().NotContainKey(expected);")]
35+
[NotImplemented]
36+
public void DictionaryShouldNotContainKey_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<DictionaryShouldNotContainKeyCodeFix, DictionaryShouldNotContainKeyAnalyzer>(oldAssertion, newAssertion);
37+
38+
[TestMethod]
39+
[AssertionDiagnostic("actual.ContainsValue(expected).Should().BeTrue();")]
40+
[NotImplemented]
41+
public void DictionaryShouldContainValue_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<DictionaryShouldContainValueAnalyzer>(assertion);
42+
43+
[TestMethod]
44+
[AssertionCodeFix(
45+
oldAssertion: "actual.ContainsValue(expected).Should().BeTrue();",
46+
newAssertion: "actual.Should().ContainValue(expected);")]
47+
[NotImplemented]
48+
public void DictionaryShouldContainValue_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<DictionaryShouldContainValueCodeFix, DictionaryShouldContainValueAnalyzer>(oldAssertion, newAssertion);
49+
50+
[TestMethod]
51+
[AssertionDiagnostic("actual.ContainsValue(expected).Should().BeFalse();")]
52+
[NotImplemented]
53+
public void DictionaryShouldNotContainValue_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<DictionaryShouldNotContainValueAnalyzer>(assertion);
54+
55+
[TestMethod]
56+
[AssertionCodeFix(
57+
oldAssertion: "actual.ContainsValue(expected).Should().BeFalse();",
58+
newAssertion: "actual.Should().NotContainValue(expected);")]
59+
[NotImplemented]
60+
public void DictionaryShouldNotContainValue_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<DictionaryShouldNotContainValueCodeFix, DictionaryShouldNotContainValueAnalyzer>(oldAssertion, newAssertion);
61+
62+
[TestMethod]
63+
[AssertionDiagnostic("actual.Should().ContainKey(expectedKey).And.ContainValue(expectedValue);")]
64+
[NotImplemented]
65+
public void DictionaryShouldContainKeyAndValue_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<DictionaryShouldContainKeyAndValueAnalyzer>(assertion);
66+
67+
[TestMethod]
68+
[AssertionCodeFix(
69+
oldAssertion: "actual.Should().ContainKey(expectedKey).And.ContainValue(expectedValue);",
70+
newAssertion: "actual.Should().Contain(expectedKey, expectedValue);")]
71+
[NotImplemented]
72+
public void DictionaryShouldContainKeyAndValue_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<DictionaryShouldContainKeyAndValueCodeFix, DictionaryShouldContainKeyAndValueAnalyzer>(oldAssertion, newAssertion);
73+
74+
[TestMethod]
75+
[AssertionDiagnostic("actual.Should().ContainKey(expected.Key).And.ContainValue(expected.Value);")]
76+
[NotImplemented]
77+
public void DictionaryShouldContainPair_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<DictionaryShouldContainPairAnalyzer>(assertion);
78+
79+
[TestMethod]
80+
[AssertionCodeFix(
81+
oldAssertion: "actual.Should().ContainKey(expected.Key).And.ContainValue(expected.Value);",
82+
newAssertion: "actual.Should().Contain(expected);")]
83+
[NotImplemented]
84+
public void DictionaryShouldContainPair_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<DictionaryShouldContainPairCodeFix, DictionaryShouldContainPairAnalyzer>(oldAssertion, newAssertion);
85+
86+
private void VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string sourceAssersion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
87+
{
88+
var source = GenerateCode.DictionaryAssertion(sourceAssersion);
89+
90+
var type = typeof(TDiagnosticAnalyzer);
91+
var diagnosticId = (string)type.GetField("DiagnosticId").GetValue(null);
92+
var message = (string)type.GetField("Message").GetValue(null);
93+
94+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source, new DiagnosticResult
95+
{
96+
Id = diagnosticId,
97+
Message = string.Format(message, GenerateCode.ActualVariableName),
98+
Locations = new DiagnosticResultLocation[]
99+
{
100+
new DiagnosticResultLocation("Test0.cs", 11,13)
101+
},
102+
Severity = DiagnosticSeverity.Info
103+
});
104+
}
105+
106+
private void VerifyCSharpFix<TCodeFixProvider, TDiagnosticAnalyzer>(string oldSourceAssertion, string newSourceAssertion)
107+
where TCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, new()
108+
where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
109+
{
110+
var oldSource = GenerateCode.DictionaryAssertion(oldSourceAssertion);
111+
var newSource = GenerateCode.DictionaryAssertion(newSourceAssertion);
112+
113+
DiagnosticVerifier.VerifyCSharpFix<TCodeFixProvider, TDiagnosticAnalyzer>(oldSource, newSource);
114+
}
115+
}
116+
}

src/FluentAssertions.BestPractices/Constants.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ public static class Collections
4747
public const string CollectionShouldOnlyHaveUniqueItemsByComparer = nameof(CollectionShouldOnlyHaveUniqueItemsByComparer);
4848
public const string CollectionShouldHaveElementAt0Null = nameof(CollectionShouldHaveElementAt0Null);
4949
}
50+
public static class Dictionaries
51+
{
52+
public const string DictionaryShouldContainKey = nameof(DictionaryShouldContainKey);
53+
public const string DictionaryShouldContainValue = nameof(DictionaryShouldContainValue);
54+
public const string DictionaryShouldContainKeyAndValue = nameof(DictionaryShouldContainKeyAndValue);
55+
public const string DictionaryShouldContainPair = nameof(DictionaryShouldContainPair);
56+
public const string DictionaryShouldNotContainKey = nameof(DictionaryShouldNotContainKey);
57+
public const string DictionaryShouldNotContainValue = nameof(DictionaryShouldNotContainValue);
58+
59+
}
5060
}
5161
}
5262
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CodeFixes;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Composition;
9+
10+
namespace FluentAssertions.BestPractices
11+
{
12+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
13+
public class DictionaryShouldContainKeyAnalyzer : FluentAssertionsAnalyzer
14+
{
15+
public const string DiagnosticId = Constants.Tips.Dictionaries.DictionaryShouldContainKey;
16+
public const string Category = Constants.Tips.Category;
17+
18+
public const string Message = "Use {0} .Should() followed by ### instead.";
19+
20+
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
21+
protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors
22+
{
23+
get
24+
{
25+
yield return (new DictionaryShouldContainKeySyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("###", 0));
26+
}
27+
}
28+
29+
private class DictionaryShouldContainKeySyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
30+
{
31+
public DictionaryShouldContainKeySyntaxVisitor() : base("###", "Should")
32+
{
33+
}
34+
}
35+
}
36+
37+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DictionaryShouldContainKeyCodeFix)), Shared]
38+
public class DictionaryShouldContainKeyCodeFix : FluentAssertionsCodeFixProvider
39+
{
40+
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(DictionaryShouldContainKeyAnalyzer.DiagnosticId);
41+
42+
protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties)
43+
=> SyntaxFactory.ParseStatement($"{properties.VariableName}.Should().###;");
44+
}
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CodeFixes;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Composition;
9+
10+
namespace FluentAssertions.BestPractices
11+
{
12+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
13+
public class DictionaryShouldContainKeyAndValueAnalyzer : FluentAssertionsAnalyzer
14+
{
15+
public const string DiagnosticId = Constants.Tips.Dictionaries.DictionaryShouldContainKeyAndValue;
16+
public const string Category = Constants.Tips.Category;
17+
18+
public const string Message = "Use {0} .Should() followed by ### instead.";
19+
20+
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
21+
protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors
22+
{
23+
get
24+
{
25+
yield return (new DictionaryShouldContainKeyAndValueSyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("###", 0));
26+
}
27+
}
28+
29+
private class DictionaryShouldContainKeyAndValueSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
30+
{
31+
public DictionaryShouldContainKeyAndValueSyntaxVisitor() : base("###", "Should")
32+
{
33+
}
34+
}
35+
}
36+
37+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DictionaryShouldContainKeyAndValueCodeFix)), Shared]
38+
public class DictionaryShouldContainKeyAndValueCodeFix : FluentAssertionsCodeFixProvider
39+
{
40+
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(DictionaryShouldContainKeyAndValueAnalyzer.DiagnosticId);
41+
42+
protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties)
43+
=> SyntaxFactory.ParseStatement($"{properties.VariableName}.Should().###;");
44+
}
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CodeFixes;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Composition;
9+
10+
namespace FluentAssertions.BestPractices
11+
{
12+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
13+
public class DictionaryShouldContainPairAnalyzer : FluentAssertionsAnalyzer
14+
{
15+
public const string DiagnosticId = Constants.Tips.Dictionaries.DictionaryShouldContainPair;
16+
public const string Category = Constants.Tips.Category;
17+
18+
public const string Message = "Use {0} .Should() followed by ### instead.";
19+
20+
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
21+
protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors
22+
{
23+
get
24+
{
25+
yield return (new DictionaryShouldContainPairSyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("###", 0));
26+
}
27+
}
28+
29+
private class DictionaryShouldContainPairSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
30+
{
31+
public DictionaryShouldContainPairSyntaxVisitor() : base("###", "Should")
32+
{
33+
}
34+
}
35+
}
36+
37+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DictionaryShouldContainPairCodeFix)), Shared]
38+
public class DictionaryShouldContainPairCodeFix : FluentAssertionsCodeFixProvider
39+
{
40+
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(DictionaryShouldContainPairAnalyzer.DiagnosticId);
41+
42+
protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties)
43+
=> SyntaxFactory.ParseStatement($"{properties.VariableName}.Should().###;");
44+
}
45+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CodeFixes;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Composition;
9+
10+
namespace FluentAssertions.BestPractices
11+
{
12+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
13+
public class DictionaryShouldContainValueAnalyzer : FluentAssertionsAnalyzer
14+
{
15+
public const string DiagnosticId = Constants.Tips.Dictionaries.DictionaryShouldContainValue;
16+
public const string Category = Constants.Tips.Category;
17+
18+
public const string Message = "Use {0} .Should() followed by ### instead.";
19+
20+
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
21+
protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors
22+
{
23+
get
24+
{
25+
yield return (new DictionaryShouldContainValueSyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("###", 0));
26+
}
27+
}
28+
29+
private class DictionaryShouldContainValueSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
30+
{
31+
public DictionaryShouldContainValueSyntaxVisitor() : base("###", "Should")
32+
{
33+
}
34+
}
35+
}
36+
37+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DictionaryShouldContainValueCodeFix)), Shared]
38+
public class DictionaryShouldContainValueCodeFix : FluentAssertionsCodeFixProvider
39+
{
40+
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(DictionaryShouldContainValueAnalyzer.DiagnosticId);
41+
42+
protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties)
43+
=> SyntaxFactory.ParseStatement($"{properties.VariableName}.Should().###;");
44+
}
45+
}

0 commit comments

Comments
 (0)