Skip to content

Commit 6396e08

Browse files
authored
docs: add mstest docs (#323)
* docs: add mstest docs
1 parent 6479db1 commit 6396e08

File tree

6 files changed

+258
-41
lines changed

6 files changed

+258
-41
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dotnet add package FluentAssertions.Analyzers
3131
## Docs
3232

3333
- [FluentAssertions Analyzer Docs](docs/FluentAssertionsAnalyzer.md)
34+
- [MsTest Analyzer Docs](docs/MsTestAnalyzer.md)
3435

3536
## Getting Started
3637

docs/MsTestAnalyzer.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!--
2+
This is a generated file, please edit src\FluentAssertions.Analyzers.FluentAssertionAnalyzerDocsGenerator\DocsGenerator.cs to change the contents
3+
-->
4+
5+
# MsTest Analyzer Docs
6+
7+
- [BooleanAssertIsTrue](#scenario-booleanassertistrue) - `flag.Should().BeTrue();`
8+
- [BooleanAssertIsFalse](#scenario-booleanassertisfalse) - `flag.Should().BeFalse();`
9+
10+
11+
## Scenarios
12+
13+
### scenario: BooleanAssertIsTrue
14+
15+
```cs
16+
// arrange
17+
var flag = true;
18+
19+
// old assertion:
20+
Assert.IsTrue(flag);
21+
22+
// new assertion:
23+
flag.Should().BeTrue();
24+
```
25+
26+
#### Failure messages
27+
28+
```cs
29+
var flag = false;
30+
31+
// old assertion:
32+
Assert.IsTrue(flag); // fail message: Assert.IsTrue failed.
33+
34+
// new assertion:
35+
flag.Should().BeTrue(); // fail message: Expected flag to be true, but found False.
36+
```
37+
38+
### scenario: BooleanAssertIsFalse
39+
40+
```cs
41+
// arrange
42+
var flag = false;
43+
44+
// old assertion:
45+
Assert.IsFalse(flag);
46+
47+
// new assertion:
48+
flag.Should().BeFalse();
49+
```
50+
51+
#### Failure messages
52+
53+
```cs
54+
var flag = true;
55+
56+
// old assertion:
57+
Assert.IsFalse(flag); // fail message: Assert.IsFalse failed.
58+
59+
// new assertion:
60+
flag.Should().BeFalse(); // fail message: Expected flag to be false, but found True.
61+
```
62+
63+

scripts/run-docs-tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Pop-Location
1111
if ($FormatAndExecuteTestsAgain) {
1212
Push-Location src
1313
Push-Location FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs
14-
dotnet format analyzers --diagnostics FAA0001 --severity info --verbosity normal
14+
dotnet format analyzers --diagnostics FAA0001 FAA0003 --severity info --verbosity normal
1515
Pop-Location
1616
Pop-Location
1717

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using FluentAssertions;
5+
using FluentAssertions.Execution;
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
8+
namespace FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs;
9+
10+
[TestClass]
11+
public class MsTestAnalyzerTests
12+
{
13+
[TestMethod]
14+
public void BooleanAssertIsTrue()
15+
{
16+
// arrange
17+
var flag = true;
18+
19+
// old assertion:
20+
Assert.IsTrue(flag);
21+
22+
// new assertion:
23+
flag.Should().BeTrue();
24+
}
25+
26+
[TestMethod, ExpectedException(typeof(AssertFailedException))]
27+
public void BooleanAssertIsTrue_Failure_OldAssertion()
28+
{
29+
// arrange
30+
var flag = false;
31+
32+
// old assertion:
33+
Assert.IsTrue(flag);
34+
}
35+
36+
[TestMethod, ExpectedException(typeof(AssertFailedException))]
37+
public void BooleanAssertIsTrue_Failure_NewAssertion()
38+
{
39+
// arrange
40+
var flag = false;
41+
42+
// new assertion:
43+
flag.Should().BeTrue();
44+
}
45+
46+
[TestMethod]
47+
public void BooleanAssertIsFalse()
48+
{
49+
// arrange
50+
var flag = false;
51+
52+
// old assertion:
53+
Assert.IsFalse(flag);
54+
55+
// new assertion:
56+
flag.Should().BeFalse();
57+
}
58+
59+
[TestMethod, ExpectedException(typeof(AssertFailedException))]
60+
public void BooleanAssertIsFalse_Failure_OldAssertion()
61+
{
62+
// arrange
63+
var flag = true;
64+
65+
// old assertion:
66+
Assert.IsFalse(flag);
67+
}
68+
69+
[TestMethod, ExpectedException(typeof(AssertFailedException))]
70+
public void BooleanAssertIsFalse_Failure_NewAssertion()
71+
{
72+
// arrange
73+
var flag = true;
74+
75+
// new assertion:
76+
flag.Should().BeFalse();
77+
}
78+
79+
}

src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocsGenerator/DocsGenerator.cs

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,41 @@ public async Task Execute()
2222
var compilation = await FluentAssertionAnalyzerDocsUtils.GetFluentAssertionAnalyzerDocsCompilation();
2323
var compilationWithAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create(analyzer));
2424

25-
var docs = new StringBuilder();
26-
var toc = new StringBuilder();
27-
var scenarios = new StringBuilder();
25+
var testAssembly = typeof(FluentAssertionAnalyzerDocs.FluentAssertionsAnalyzerTests).Assembly;
2826

29-
docs.AppendLine("<!--");
30-
docs.AppendLine("This is a generated file, please edit src\\FluentAssertions.Analyzers.FluentAssertionAnalyzerDocsGenerator\\DocsGenerator.cs to change the contents");
31-
docs.AppendLine("-->");
32-
docs.AppendLine();
27+
foreach (var tree in compilationWithAnalyzers.Compilation.SyntaxTrees.Where(t => t.FilePath.EndsWith("Tests.cs")))
28+
{
29+
Console.WriteLine($"File: {Path.GetFileName(tree.FilePath)}");
3330

34-
docs.AppendLine("# FluentAssertions Analyzer Docs");
35-
docs.AppendLine();
31+
var docsName = Path.GetFileNameWithoutExtension(tree.FilePath).Replace("Tests", ".md");
3632

37-
scenarios.AppendLine("## Scenarios");
38-
scenarios.AppendLine();
33+
var docs = new StringBuilder();
34+
var toc = new StringBuilder();
35+
var scenarios = new StringBuilder();
3936

40-
var testAssembly = typeof(FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs.FluentAssertionsAnalyzerTests).Assembly;
37+
docs.AppendLine("<!--");
38+
docs.AppendLine("This is a generated file, please edit src\\FluentAssertions.Analyzers.FluentAssertionAnalyzerDocsGenerator\\DocsGenerator.cs to change the contents");
39+
docs.AppendLine("-->");
40+
docs.AppendLine();
4141

42-
foreach (var tree in compilationWithAnalyzers.Compilation.SyntaxTrees.Where(t => t.FilePath.EndsWith("Tests.cs")))
43-
{
44-
Console.WriteLine($"File: {Path.GetFileName(tree.FilePath)}");
42+
var subject = Path.GetFileNameWithoutExtension(tree.FilePath).Replace("AnalyzerTests", string.Empty);
43+
docs.AppendLine($"# {subject} Analyzer Docs");
44+
docs.AppendLine();
4545

46+
scenarios.AppendLine("## Scenarios");
47+
scenarios.AppendLine();
4648

4749
var root = await tree.GetRootAsync();
4850
var classDef = root.DescendantNodes().OfType<ClassDeclarationSyntax>().First();
4951
var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
52+
var methodsMap = methods.ToDictionary(m => m.Identifier.Text);
5053

5154
var classType = testAssembly.GetType($"FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs.{classDef.Identifier.Text}");
5255
var classInstance = Activator.CreateInstance(classType);
5356

5457
foreach (var method in methods.Where(m => m.AttributeLists.Any(list => list.Attributes.Count is 1 && list.Attributes[0].Name.ToString() is "TestMethod")))
5558
{
59+
// success scenario:
5660
{
5761
scenarios.AppendLine($"### scenario: {method.Identifier}");
5862
scenarios.AppendLine();
@@ -67,8 +71,10 @@ public async Task Execute()
6771

6872
toc.AppendLine($"- [{method.Identifier}](#scenario-{method.Identifier.Text.ToLower()}) - `{newAssertion}`");
6973
}
74+
75+
// FluentAssertion failures scenario:
76+
if (methodsMap.TryGetValue($"{method.Identifier.Text}_Failure", out var testWithFailure))
7077
{
71-
var testWithFailure = methods.FirstOrDefault(m => m.Identifier.Text == $"{method.Identifier.Text}_Failure");
7278
var testMethodWithFailure = classType.GetMethod(testWithFailure.Identifier.Text);
7379

7480
var exceptionMessageLines = GetMethodExceptionMessageLines(classInstance, testMethodWithFailure);
@@ -88,8 +94,6 @@ public async Task Execute()
8894
var arrange = bodyLines.TakeWhile(x => !string.IsNullOrEmpty(x))
8995
.Select(l => l.Length > paddingToRemove ? l.Substring(paddingToRemove) : l).Aggregate((a, b) => $"{a}{Environment.NewLine}{b}");
9096

91-
var methodBody = $"```cs{Environment.NewLine}{arrange}{Environment.NewLine}```";
92-
9397
scenarios.AppendLine($"#### Failure messages");
9498
scenarios.AppendLine();
9599
scenarios.AppendLine("```cs");
@@ -106,6 +110,43 @@ public async Task Execute()
106110
scenarios.AppendLine("```");
107111
scenarios.AppendLine();
108112
}
113+
114+
// Testing Libraries failures scenarios:
115+
if (methodsMap.TryGetValue($"{method.Identifier.Text}_Failure_OldAssertion", out var testWithFailureOldAssertion)
116+
&& methodsMap.TryGetValue($"{method.Identifier.Text}_Failure_NewAssertion", out var testWithFailureNewAssertion))
117+
{
118+
var testMethodWithFailureOldAssertion = classType.GetMethod(testWithFailureOldAssertion.Identifier.Text);
119+
var testMethodWithFailureNewAssertion = classType.GetMethod(testWithFailureNewAssertion.Identifier.Text);
120+
121+
var exceptionMessageLinesOldAssertion = GetMethodExceptionMessage(classInstance, testMethodWithFailureOldAssertion);
122+
var exceptionMessageLinesNewAssertion = GetMethodExceptionMessage(classInstance, testMethodWithFailureNewAssertion);
123+
124+
var oldAssertionComment = testWithFailureOldAssertion.DescendantTrivia().First(x => x.IsKind(SyntaxKind.SingleLineCommentTrivia) && x.ToString().Equals("// old assertion:"));
125+
var newAssertionComment = testWithFailureNewAssertion.DescendantTrivia().First(x => x.IsKind(SyntaxKind.SingleLineCommentTrivia) && x.ToString().Equals("// new assertion:"));
126+
127+
var bodyLines = testWithFailureNewAssertion.Body.ToFullString().Split(Environment.NewLine)[2..^2];
128+
var paddingToRemove = bodyLines[0].IndexOf(bodyLines[0].TrimStart());
129+
130+
var oldAssertion = testWithFailureOldAssertion.Body.Statements.OfType<ExpressionStatementSyntax>().Single(x => x.Span.CompareTo(oldAssertionComment.Span) > 0).ToString().TrimStart() + " \t// fail message: " + exceptionMessageLinesOldAssertion;
131+
var newAssertion = testWithFailureNewAssertion.Body.Statements.OfType<ExpressionStatementSyntax>().Single(x => x.Span.CompareTo(newAssertionComment.Span) > 0).ToString().TrimStart() + " \t// fail message: " + exceptionMessageLinesNewAssertion;
132+
133+
var arrange = bodyLines.TakeWhile(x => !string.IsNullOrEmpty(x))
134+
.Select(l => l.Length > paddingToRemove ? l.Substring(paddingToRemove) : l).Aggregate((a, b) => $"{a}{Environment.NewLine}{b}");
135+
136+
scenarios.AppendLine($"#### Failure messages");
137+
scenarios.AppendLine();
138+
scenarios.AppendLine("```cs");
139+
scenarios.AppendLine(arrange);
140+
scenarios.AppendLine();
141+
scenarios.AppendLine($"// old assertion:");
142+
scenarios.AppendLine(oldAssertion);
143+
scenarios.AppendLine();
144+
scenarios.AppendLine($"// new assertion:");
145+
scenarios.AppendLine(newAssertion);
146+
scenarios.AppendLine("```");
147+
scenarios.AppendLine();
148+
}
149+
109150
}
110151

111152
var diagnostics = await compilationWithAnalyzers.GetAllDiagnosticsAsync();
@@ -114,26 +155,28 @@ public async Task Execute()
114155
Console.WriteLine($"source: {root.FindNode(diagnostic.Location.SourceSpan)}");
115156
Console.WriteLine($" diagnostic: {diagnostic}");
116157
}
117-
}
118158

119-
docs.AppendLine(toc.ToString());
120-
docs.AppendLine();
121-
docs.AppendLine(scenarios.ToString());
159+
docs.AppendLine(toc.ToString());
160+
docs.AppendLine();
161+
docs.AppendLine(scenarios.ToString());
122162

123-
var docsPath = Path.Combine(Environment.CurrentDirectory, "..", "..", "docs", "FluentAssertionsAnalyzer.md");
124-
Directory.CreateDirectory(Path.GetDirectoryName(docsPath));
125-
await File.WriteAllTextAsync(docsPath, docs.ToString());
163+
var docsPath = Path.Combine(Environment.CurrentDirectory, "..", "..", "docs", docsName);
164+
Directory.CreateDirectory(Path.GetDirectoryName(docsPath));
165+
await File.WriteAllTextAsync(docsPath, docs.ToString());
166+
}
126167
}
127168

128-
private string[] GetMethodExceptionMessageLines(object instnace, MethodInfo method)
169+
private string[] GetMethodExceptionMessageLines(object instance, MethodInfo method)
170+
=> GetMethodExceptionMessage(instance, method).Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
171+
private string GetMethodExceptionMessage(object instance, MethodInfo method)
129172
{
130173
try
131174
{
132-
method.Invoke(instnace, null);
175+
method.Invoke(instance, null);
133176
}
134177
catch (Exception ex) when (ex.InnerException is AssertFailedException exception)
135178
{
136-
return exception.Message.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
179+
return exception.Message;
137180
}
138181

139182
throw new InvalidOperationException("Method did not throw an exception");

0 commit comments

Comments
 (0)