Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Be, NotBe and BeOneOf for object comparisons with custom comparer #2243

Merged
merged 1 commit into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
186 changes: 184 additions & 2 deletions Src/FluentAssertions/Primitives/ObjectAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,97 @@ 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)
jnyrup marked this conversation as resolved.
Show resolved Hide resolved
{
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);
jnyrup marked this conversation as resolved.
Show resolved Hide resolved
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 +123,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 +146,36 @@ 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 +196,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 +409,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