Skip to content

Commit

Permalink
Added Be, NotBe and BeOneOf for object comparisons with custom …
Browse files Browse the repository at this point in the history
…comparer
  • Loading branch information
xmarshal authored and jnyrup committed Jul 29, 2023
1 parent cd28678 commit 7a019e0
Show file tree
Hide file tree
Showing 10 changed files with 547 additions and 2 deletions.
184 changes: 182 additions & 2 deletions Src/FluentAssertions/Primitives/ObjectAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,96 @@ public ObjectAssertions(object value)
: base(value)
{
}

/// <summary>
/// Asserts that a value equals <paramref name="expected"/> using the provided <paramref name="comparer"/>.
/// </summary>
/// <param name="expected">The expected value</param>
/// <param name="comparer">
/// An equality comparer to compare values.
/// </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 <paramref name="because" />.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <see langword="null"/>.</exception>
public AndConstraint<ObjectAssertions> Be<TExpectation>(TExpectation expected, IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(comparer);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(Subject is TExpectation subject && comparer.Equals(subject, expected))
.WithDefaultIdentifier(Identifier)
.FailWith("Expected {context} to be {0}{reason}, but found {1}.", expected,
Subject);

return new AndConstraint<ObjectAssertions>(this);
}

/// <summary>
/// Asserts that a value does not equal <paramref name="unexpected"/> using the provided <paramref name="comparer"/>.
/// </summary>
/// <param name="unexpected">The unexpected value</param>
/// <param name="comparer">
/// An equality comparer to compare values.
/// </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 <paramref name="because"/>.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <see langword="null"/>.</exception>
public AndConstraint<ObjectAssertions> NotBe<TExpectation>(TExpectation unexpected, IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(comparer);

Execute.Assertion
.ForCondition(Subject is not TExpectation subject || !comparer.Equals(subject, unexpected))
.BecauseOf(because, becauseArgs)
.WithDefaultIdentifier(Identifier)
.FailWith("Did not expect {context} to be equal to {0}{reason}.", unexpected);

return new AndConstraint<ObjectAssertions>(this);
}

/// <summary>
/// Asserts that a value is one of the specified <paramref name="validValues"/> using the provided <paramref name="comparer"/>.
/// </summary>
/// <param name="validValues">
/// The values that are valid.
/// </param>
/// <param name="comparer">
/// An equality comparer to compare values.
/// </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 <paramref name="because" />.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="validValues"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <see langword="null"/>.</exception>
public AndConstraint<ObjectAssertions> BeOneOf<TExpectation>(IEnumerable<TExpectation> validValues,
IEqualityComparer<TExpectation> comparer,
string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(validValues);
Guard.ThrowIfArgumentIsNull(comparer);

Execute.Assertion
.ForCondition(Subject is TExpectation subject && validValues.Contains(subject, comparer))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:object} to be one of {0}{reason}, but found {1}.", validValues, Subject);

return new AndConstraint<ObjectAssertions>(this);
}
}

#pragma warning disable CS0659, S1206 // Ignore not overriding Object.GetHashCode()
Expand All @@ -32,7 +122,7 @@ public ObjectAssertions(TSubject value)
}

/// <summary>
/// Asserts that a <typeparamref name="TSubject"/> equals another <typeparamref name="TSubject"/> using its <see cref="object.Equals(object)" /> implementation.
/// Asserts that a value equals <paramref name="expected"/> using its <see cref="object.Equals(object)" /> implementation.
/// </summary>
/// <param name="expected">The expected value</param>
/// <param name="because">
Expand All @@ -55,7 +145,35 @@ public AndConstraint<TAssertions> Be(TSubject expected, string because = "", par
}

/// <summary>
/// Asserts that a <typeparamref name="TSubject"/> does not equal another <typeparamref name="TSubject"/> using its <see cref="object.Equals(object)" /> method.
/// Asserts that a value equals <paramref name="expected"/> using the provided <paramref name="comparer"/>.
/// </summary>
/// <param name="expected">The expected value</param>
/// <param name="comparer">
/// An equality comparer to compare values.
/// </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 <paramref name="because" />.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <see langword="null"/>.</exception>
public AndConstraint<TAssertions> Be(TSubject expected, IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(comparer);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(comparer.Equals(Subject, expected))
.WithDefaultIdentifier(Identifier)
.FailWith("Expected {context} to be {0}{reason}, but found {1}.", expected,
Subject);

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a value does not equal <paramref name="unexpected"/> using its <see cref="object.Equals(object)" /> method.
/// </summary>
/// <param name="unexpected">The unexpected value</param>
/// <param name="because">
Expand All @@ -76,6 +194,34 @@ public AndConstraint<TAssertions> NotBe(TSubject unexpected, string because = ""
return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a value does not equal <paramref name="unexpected"/> using the provided <paramref name="comparer"/>.
/// </summary>
/// <param name="unexpected">The unexpected value</param>
/// <param name="comparer">
/// An equality comparer to compare values.
/// </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 <paramref name="because"/>.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <see langword="null"/>.</exception>
public AndConstraint<TAssertions> NotBe(TSubject unexpected, IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(comparer);

Execute.Assertion
.ForCondition(!comparer.Equals(Subject, unexpected))
.BecauseOf(because, becauseArgs)
.WithDefaultIdentifier(Identifier)
.FailWith("Did not expect {context} to be equal to {0}{reason}.", unexpected);

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that an object is equivalent to another object.
/// </summary>
Expand Down Expand Up @@ -261,6 +407,40 @@ public AndConstraint<TAssertions> BeOneOf(params TSubject[] validValues)
return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a value is one of the specified <paramref name="validValues"/>.
/// </summary>
/// <param name="validValues">
/// The values that are valid.
/// </param>
/// <param name="comparer">
/// An equality comparer to compare values.
/// </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 <paramref name="because" />.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="validValues"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <see langword="null"/>.</exception>
public AndConstraint<TAssertions> BeOneOf(IEnumerable<TSubject> validValues,
IEqualityComparer<TSubject> comparer,
string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(validValues);
Guard.ThrowIfArgumentIsNull(comparer);

Execute.Assertion
.ForCondition(validValues.Contains(Subject, comparer))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:object} to be one of {0}{reason}, but found {1}.", validValues, Subject);

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <inheritdoc/>
public override bool Equals(object obj) =>
throw new NotSupportedException("Equals is not part of Fluent Assertions. Did you mean Be() or BeSameAs() instead?");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2102,19 +2102,25 @@ namespace FluentAssertions.Primitives
public class ObjectAssertions : FluentAssertions.Primitives.ObjectAssertions<object, FluentAssertions.Primitives.ObjectAssertions>
{
public ObjectAssertions(object value) { }
public FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> Be<TExpectation>(TExpectation expected, System.Collections.Generic.IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeOneOf<TExpectation>(System.Collections.Generic.IEnumerable<TExpectation> validValues, System.Collections.Generic.IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> NotBe<TExpectation>(TExpectation unexpected, System.Collections.Generic.IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs) { }
}
public class ObjectAssertions<TSubject, TAssertions> : FluentAssertions.Primitives.ReferenceTypeAssertions<TSubject, TAssertions>
where TAssertions : FluentAssertions.Primitives.ObjectAssertions<TSubject, TAssertions>
{
public ObjectAssertions(TSubject value) { }
protected override string Identifier { get; }
public FluentAssertions.AndConstraint<TAssertions> Be(TSubject expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> Be(TSubject expected, System.Collections.Generic.IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeEquivalentTo<TExpectation>(TExpectation expectation, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeEquivalentTo<TExpectation>(TExpectation expectation, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeOneOf(params TSubject[] validValues) { }
public FluentAssertions.AndConstraint<TAssertions> BeOneOf(System.Collections.Generic.IEnumerable<TSubject> validValues, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeOneOf(System.Collections.Generic.IEnumerable<TSubject> validValues, System.Collections.Generic.IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs) { }
public override bool Equals(object obj) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(TSubject unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(TSubject unexpected, System.Collections.Generic.IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo<TExpectation>(TExpectation unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo<TExpectation>(TExpectation unexpected, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2186,19 +2186,25 @@ namespace FluentAssertions.Primitives
public class ObjectAssertions : FluentAssertions.Primitives.ObjectAssertions<object, FluentAssertions.Primitives.ObjectAssertions>
{
public ObjectAssertions(object value) { }
public FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> Be<TExpectation>(TExpectation expected, System.Collections.Generic.IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeOneOf<TExpectation>(System.Collections.Generic.IEnumerable<TExpectation> validValues, System.Collections.Generic.IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> NotBe<TExpectation>(TExpectation unexpected, System.Collections.Generic.IEqualityComparer<TExpectation> comparer, string because = "", params object[] becauseArgs) { }
}
public class ObjectAssertions<TSubject, TAssertions> : FluentAssertions.Primitives.ReferenceTypeAssertions<TSubject, TAssertions>
where TAssertions : FluentAssertions.Primitives.ObjectAssertions<TSubject, TAssertions>
{
public ObjectAssertions(TSubject value) { }
protected override string Identifier { get; }
public FluentAssertions.AndConstraint<TAssertions> Be(TSubject expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> Be(TSubject expected, System.Collections.Generic.IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeEquivalentTo<TExpectation>(TExpectation expectation, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeEquivalentTo<TExpectation>(TExpectation expectation, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeOneOf(params TSubject[] validValues) { }
public FluentAssertions.AndConstraint<TAssertions> BeOneOf(System.Collections.Generic.IEnumerable<TSubject> validValues, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> BeOneOf(System.Collections.Generic.IEnumerable<TSubject> validValues, System.Collections.Generic.IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs) { }
public override bool Equals(object obj) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(TSubject unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBe(TSubject unexpected, System.Collections.Generic.IEqualityComparer<TSubject> comparer, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo<TExpectation>(TExpectation unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> NotBeEquivalentTo<TExpectation>(TExpectation unexpected, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
}
Expand Down

0 comments on commit 7a019e0

Please sign in to comment.