/
ObjectAssertions.cs
193 lines (179 loc) · 9.73 KB
/
ObjectAssertions.cs
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using FluentAssertions.Common;
using FluentAssertions.Equivalency;
using FluentAssertions.Execution;
namespace FluentAssertions.Primitives
{
/// <summary>
/// Contains a number of methods to assert that an <see cref="object"/> is in the expected state.
/// </summary>
public class ObjectAssertions : ReferenceTypeAssertions<object, ObjectAssertions>
{
public ObjectAssertions(object value)
{
Subject = value;
}
/// <summary>
/// Asserts that an object equals another object using its <see cref="object.Equals(object)" /> implementation.
/// </summary>
/// <param name="expected">The expected value</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public AndConstraint<ObjectAssertions> Be(object expected, string because = "", params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(Subject.IsSameOrEqualTo(expected))
.FailWith("Expected {context:object} to be {0}{reason}, but found {1}.", expected,
Subject);
return new AndConstraint<ObjectAssertions>(this);
}
/// <summary>
/// Asserts that an object does not equal another object using its <see cref="object.Equals(object)" /> method.
/// </summary>
/// <param name="unexpected">The unexpected value</param>
/// <param name="because">
/// A formatted phrase explaining why the assertion should be satisfied. If the phrase does not
/// start with the word <i>because</i>, it is prepended to the message.
/// </param>
/// <param name="becauseArgs">
/// Zero or more values to use for filling in any <see cref="string.Format(string,object[])" /> compatible placeholders.
/// </param>
public AndConstraint<ObjectAssertions> NotBe(object unexpected, string because = "", params object[] becauseArgs)
{
Execute.Assertion
.ForCondition(!Subject.IsSameOrEqualTo(unexpected))
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect {context:object} to be equal to {0}{reason}.", unexpected);
return new AndConstraint<ObjectAssertions>(this);
}
/// <summary>
/// Asserts that an object is equivalent to another object.
/// </summary>
/// <remarks>
/// Objects are equivalent when both object graphs have equally named properties with the same value,
/// irrespective of the type of those objects. Two properties are also equal if one type can be converted to another and the result is equal.
/// The type of a collection property is ignored as long as the collection implements <see cref="IEnumerable{T}"/> and all
/// items in the collection are structurally equal.
/// Notice that actual behavior is determined by the global defaults managed by <see cref="AssertionOptions"/>.
/// </remarks>
/// <param name="because">
/// An optional formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the
/// assertion is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public void BeEquivalentTo<TExpectation>(TExpectation expectation, string because = "",
params object[] becauseArgs)
{
BeEquivalentTo(expectation, config => config, because, becauseArgs);
}
/// <summary>
/// Asserts that an object is equivalent to another object.
/// </summary>
/// <remarks>
/// Objects are equivalent when both object graphs have equally named properties with the same value,
/// irrespective of the type of those objects. Two properties are also equal if one type can be converted to another and the result is equal.
/// The type of a collection property is ignored as long as the collection implements <see cref="IEnumerable{T}"/> and all
/// items in the collection are structurally equal.
/// </remarks>
/// <param name="config">
/// A reference to the <see cref="EquivalencyAssertionOptions{TSubject}"/> configuration object that can be used
/// to influence the way the object graphs are compared. You can also provide an alternative instance of the
/// <see cref="EquivalencyAssertionOptions{TSubject}"/> class. The global defaults are determined by the
/// <see cref="AssertionOptions"/> class.
/// </param>
/// <param name="because">
/// An optional formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the
/// assertion is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public void BeEquivalentTo<TExpectation>(TExpectation expectation,
Func<EquivalencyAssertionOptions<TExpectation>, EquivalencyAssertionOptions<TExpectation>> config, string because = "",
params object[] becauseArgs)
{
IEquivalencyAssertionOptions options = config(AssertionOptions.CloneDefaults<TExpectation>());
var context = new EquivalencyValidationContext
{
Subject = Subject,
Expectation = expectation,
CompileTimeType = typeof(TExpectation),
Because = because,
BecauseArgs = becauseArgs,
Tracer = options.TraceWriter
};
var equivalencyValidator = new EquivalencyValidator(options);
equivalencyValidator.AssertEquality(context);
}
/// <summary>
/// Asserts that an object is an enum and has a specified flag
/// </summary>
/// <param name="expectedFlag">The expected flag.</param>
/// <param name="because">
/// A formatted phrase explaining why the assertion should be satisfied. If the phrase does not
/// start with the word <i>because</i>, it is prepended to the message.
/// </param>
/// <param name="becauseArgs">
/// Zero or more values to use for filling in any <see cref="string.Format(string,object[])" /> compatible placeholders.
/// </param>
public AndConstraint<ObjectAssertions> HaveFlag(Enum expectedFlag, string because = "",
params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(!(Subject is null))
.FailWith("Expected type to be {0}{reason}, but found <null>.", expectedFlag.GetType())
.Then
.ForCondition(Subject.GetType() == expectedFlag.GetType())
.FailWith("Expected the enum to be of type {0} type but found {1}{reason}.", expectedFlag.GetType(), Subject.GetType())
.Then
.Given(() => Subject as Enum)
.ForCondition(@enum => @enum.HasFlag(expectedFlag))
.FailWith("The enum was expected to have flag {0} but found {1}{reason}.", _ => expectedFlag, @enum => @enum);
return new AndConstraint<ObjectAssertions>(this);
}
/// <summary>
/// Asserts that an object is an enum and does not have a specified flag
/// </summary>
/// <param name="unexpectedFlag">The unexpected flag.</param>
/// <param name="because">
/// A formatted phrase explaining why the assertion should be satisfied. If the phrase does not
/// start with the word <i>because</i>, it is prepended to the message.
/// </param>
/// <param name="becauseArgs">
/// Zero or more values to use for filling in any <see cref="string.Format(string,object[])" /> compatible placeholders.
/// </param>
public AndConstraint<ObjectAssertions> NotHaveFlag(Enum unexpectedFlag, string because = "",
params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(!(Subject is null))
.FailWith("Expected type to be {0}{reason}, but found <null>.", unexpectedFlag.GetType())
.Then
.ForCondition(Subject.GetType() == unexpectedFlag.GetType())
.FailWith("Expected the enum to be of type {0} type but found {1}{reason}.", unexpectedFlag.GetType(), Subject.GetType())
.Then
.Given(() => Subject as Enum)
.ForCondition(@enum => !@enum.HasFlag(unexpectedFlag))
.FailWith("Did not expect the enum to have flag {0}{reason}.", unexpectedFlag);
return new AndConstraint<ObjectAssertions>(this);
}
/// <summary>
/// Returns the type of the subject the assertion applies on.
/// </summary>
protected override string Identifier => "object";
}
}