-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathUnitTestAssertions.cs
More file actions
136 lines (118 loc) · 6.41 KB
/
Copy pathUnitTestAssertions.cs
File metadata and controls
136 lines (118 loc) · 6.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#pragma warning disable IDE0130 // Namespace does not match folder structure; by design.
namespace AwesomeAssertions;
#pragma warning restore IDE0130 // Namespace does not match folder structure
/// <summary>
/// Provides custom <see cref="AwesomeAssertions"/> assertion classes for use in unit testing.
/// </summary>
public static class UnitTestAssertions
{
/// <summary>
/// Returns a <see cref="JsonNodeAssertions"/> for asserting on the <see cref="JsonNode"/> directly.
/// </summary>
/// <param name="node">The <see cref="JsonNode"/> subject.</param>
/// <returns>A <see cref="JsonNodeAssertions"/> for fluent JSON-specific chaining.</returns>
public static JsonNodeAssertions Should(this JsonNode node) => new(node, AssertionChain.GetOrCreate());
/// <summary>
/// Asserts that the string is valid JSON, enabling further JSON-specific assertions.
/// </summary>
/// <param name="assertions">The <see cref="StringAssertions"/>.</param>
/// <param name="because">The reason the assertion should be satisfied.</param>
/// <param name="becauseArgs">The <paramref name="because"/> format arguments.</param>
/// <returns>A <see cref="JsonNodeAssertions"/> for fluent JSON-specific chaining.</returns>
public static JsonNodeAssertions BeJson(this StringAssertions assertions, string because = "", params object[] becauseArgs)
{
var chain = assertions.CurrentAssertionChain;
chain.ForCondition(assertions.Subject is not null)
.BecauseOf(because, becauseArgs)
.WithDefaultIdentifier("JSON string")
.FailWith("Expected {context} to be valid JSON{reason}, but it was <null>.");
JsonNode? node = null;
string? parseError = null;
var isValid = true;
try
{
node = JsonNode.Parse(assertions.Subject!);
}
catch (JsonException ex)
{
isValid = false;
parseError = ex.Message;
}
chain.ForCondition(isValid)
.BecauseOf(because, becauseArgs)
.WithDefaultIdentifier("string")
.FailWith("Expected {context} to be valid JSON{reason}, but parsing failed: {0}.", parseError);
return new JsonNodeAssertions(node!, chain);
}
/// <summary>
/// Provides JSON-specific assertions for a <see cref="JsonNode"/> subject.
/// </summary>
public sealed class JsonNodeAssertions : ReferenceTypeAssertions<JsonNode, JsonNodeAssertions>
{
/// <summary>
/// Initializes a new instance of the <see cref="JsonNodeAssertions"/> class.
/// </summary>
/// <param name="subject">The <see cref="JsonNode"/> subject.</param>
/// <param name="assertionChain">The <see cref="AssertionChain"/>.</param>
internal JsonNodeAssertions(JsonNode subject, AssertionChain assertionChain) : base(subject, assertionChain) { }
/// <inheritdoc/>
protected override string Identifier => "JSON";
/// <summary>
/// Asserts that the JSON contains all the specified JSON paths.
/// </summary>
/// <param name="paths">The JSON paths to match.</param>
/// <param name="because">The reason the assertion should be satisfied.</param>
/// <param name="becauseArgs">The <paramref name="because"/> format arguments.</param>
/// <returns>The current <see cref="JsonNodeAssertions"/> to support fluent-style chaining.</returns>
/// <remarks>The original <see cref="JsonNode"/> is cloned before matching to ensure the assertion does not modify the original JSON.</remarks>
public JsonNodeAssertions ContainAll(IEnumerable<string> paths, string because = "", params object[] becauseArgs)
{
foreach (var path in paths)
{
var jn = JsonFilter.GetMatched(Subject.DeepClone(), path);
CurrentAssertionChain.ForCondition(jn is not null)
.BecauseOf(because, becauseArgs)
.WithDefaultIdentifier("string")
.FailWith("Expected {context} path to exist{reason}: {0}", path);
}
return this;
}
/// <summary>
/// Asserts that the JSON does not contain any of the specified JSON paths.
/// </summary>
/// <param name="paths">The JSON paths to match.</param>
/// <param name="because">The reason the assertion should be satisfied.</param>
/// <param name="becauseArgs">The <paramref name="because"/> format arguments.</param>
/// <returns>The current <see cref="JsonNodeAssertions"/> to support fluent-style chaining.</returns>
/// <remarks>The original <see cref="JsonNode"/> is cloned before matching to ensure the assertion does not modify the original JSON.</remarks>
public JsonNodeAssertions NotContainAny(IEnumerable<string> paths, string because = "", params object[] becauseArgs)
{
foreach (var path in paths)
{
var jn = JsonFilter.GetMatched(Subject.DeepClone(), path);
CurrentAssertionChain.ForCondition(jn is null)
.BecauseOf(because, becauseArgs)
.WithDefaultIdentifier("string")
.FailWith("Expected {context} path to not exist{reason}: {0}", path);
}
return this;
}
/// <summary>
/// Asserts that the JSON contains the specified JSON path, returning the matching <see cref="JsonNode"/> for further chaining if desired.
/// </summary>
/// <param name="path">The JSON path to match.</param>
/// <param name="because">The reason the assertion should be satisfied.</param>
/// <param name="becauseArgs">The <paramref name="because"/> format arguments.</param>
/// <returns>The <see cref="JsonNode"/> that matches the specified JSON path.</returns>
/// <remarks>The original <see cref="JsonNode"/> is cloned before matching to ensure the assertion does not modify the original JSON.</remarks>
public JsonNode HavePath(string path, string because = "", params object[] becauseArgs)
{
var jn = JsonFilter.GetMatched(Subject.DeepClone(), path);
CurrentAssertionChain.ForCondition(jn is not null)
.BecauseOf(because, becauseArgs)
.WithDefaultIdentifier("string")
.FailWith("Expected {context} path to exist{reason}");
return jn!;
}
}
}