diff --git a/Build/_build.csproj b/Build/_build.csproj
index 14a1ece568..b3394d14d5 100644
--- a/Build/_build.csproj
+++ b/Build/_build.csproj
@@ -8,10 +8,10 @@
..\
-
+
-
+
-
+
diff --git a/README.md b/README.md
index eb819e72ce..2d2bca3217 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[![](https://img.shields.io/nuget/dt/FluentAssertions.svg?label=nuget%20downloads)](https://www.nuget.org/packages/FluentAssertions)
[![](https://img.shields.io/librariesio/dependents/nuget/FluentAssertions.svg?label=dependent%20libraries)](https://libraries.io/nuget/FluentAssertions)
![](https://img.shields.io/badge/release%20strategy-githubflow-orange.svg)
-[![Coverage Status](https://coveralls.io/repos/github/fluentassertions/fluentassertions/badge.svg?branch=develop)](https://coveralls.io/github/fluentassertions/fluentassertions?branch=develop)
+[![Coverage Status](https://coveralls.io/repos/github/fluentassertions/fluentassertions/badge.svg?branch=master)](https://coveralls.io/github/fluentassertions/fluentassertions?branch=master)
# About this project
A very extensive set of extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style unit tests. Targets .NET Framework 4.7, as well as .NET Core 2.1, .NET Core 3.0, .NET 6, .NET Standard 2.0 and 2.1.
@@ -19,9 +19,7 @@ Install Visual Studio 2022 17.0+ or JetBrains Rider 2021.3 as well as the Build
# What are these Approval.Tests?
This is a special set of tests that use the [Verify](https://github.com/VerifyTests/Verify) project to verify whether you've introduced any breaking changes in the public API of the library.
-If you've verified the changes and decided they are valid, you can accept them using `AcceptApiChanges.ps1` or `AcceptApiChanges.sh`. Alternatively, you can use the [Verify Support](https://plugins.jetbrains.com/plugin/17240-verify-support) plug-in to compare the changes and accept them right from inside Rider. See also the [Contribution Guidelines.](CONTRIBUTING.md).
-
-`build.ps1`
+If you've verified the changes and decided they are valid, you can accept them using `AcceptApiChanges.ps1` or `AcceptApiChanges.sh`. Alternatively, you can use the [Verify Support](https://plugins.jetbrains.com/plugin/17240-verify-support) plug-in to compare the changes and accept them right from inside Rider. See also the [Contribution Guidelines](CONTRIBUTING.md).
# Powered By
and
diff --git a/Src/FluentAssertions/AssertionExtensions.cs b/Src/FluentAssertions/AssertionExtensions.cs
index aa0b65309a..8624b43efa 100644
--- a/Src/FluentAssertions/AssertionExtensions.cs
+++ b/Src/FluentAssertions/AssertionExtensions.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Reflection;
@@ -385,6 +386,36 @@ public static StringCollectionAssertions Should(this IEnumerable @this)
return new GenericDictionaryAssertions(actualValue);
}
+ ///
+ /// Returns an assertions object that provides methods for asserting the state of a .
+ ///
+ [Pure]
+ public static GenericCollectionAssertions Should(this DataTableCollection actualValue)
+ {
+ return new GenericCollectionAssertions(
+ ReadOnlyNonGenericCollectionWrapper.Create(actualValue));
+ }
+
+ ///
+ /// Returns an assertions object that provides methods for asserting the state of a .
+ ///
+ [Pure]
+ public static GenericCollectionAssertions Should(this DataColumnCollection actualValue)
+ {
+ return new GenericCollectionAssertions(
+ ReadOnlyNonGenericCollectionWrapper.Create(actualValue));
+ }
+
+ ///
+ /// Returns an assertions object that provides methods for asserting the state of a .
+ ///
+ [Pure]
+ public static GenericCollectionAssertions Should(this DataRowCollection actualValue)
+ {
+ return new GenericCollectionAssertions(
+ ReadOnlyNonGenericCollectionWrapper.Create(actualValue));
+ }
+
///
/// Returns a object that can be used to assert the
/// current .
@@ -1028,6 +1059,22 @@ public static void Should(this TypeSelectorAssertions _)
InvalidShouldCall();
}
+ ///
+ [Obsolete("You are asserting the 'AndConstraint' itself. Remove the 'Should()' method directly following 'And'", error: true)]
+ public static void Should(this DateTimeRangeAssertions _)
+ where TAssertions : DateTimeAssertions
+ {
+ InvalidShouldCall();
+ }
+
+ ///
+ [Obsolete("You are asserting the 'AndConstraint' itself. Remove the 'Should()' method directly following 'And'", error: true)]
+ public static void Should(this DateTimeOffsetRangeAssertions _)
+ where TAssertions : DateTimeOffsetAssertions
+ {
+ InvalidShouldCall();
+ }
+
[DoesNotReturn]
private static void InvalidShouldCall()
{
diff --git a/Src/FluentAssertions/Common/Guard.cs b/Src/FluentAssertions/Common/Guard.cs
index fdfb97520b..c6e2e4c15b 100644
--- a/Src/FluentAssertions/Common/Guard.cs
+++ b/Src/FluentAssertions/Common/Guard.cs
@@ -55,14 +55,6 @@ public static void ThrowIfArgumentContainsNull(IEnumerable values, string
}
}
- public static void ThrowIfArgumentContainsNull(IEnumerable values, string paramName, string message)
- {
- if (values.Any(t => t is null))
- {
- throw new ArgumentNullException(paramName, message);
- }
- }
-
public static void ThrowIfArgumentIsEmpty(IEnumerable values, string paramName, string message)
{
if (!values.Any())
diff --git a/Src/FluentAssertions/Common/MemberPath.cs b/Src/FluentAssertions/Common/MemberPath.cs
index 505f7f0ab6..5d62ddf12f 100644
--- a/Src/FluentAssertions/Common/MemberPath.cs
+++ b/Src/FluentAssertions/Common/MemberPath.cs
@@ -17,6 +17,8 @@ internal class MemberPath
private string[] segments;
+ private static readonly MemberPathSegmentEqualityComparer MemberPathSegmentEqualityComparer = new();
+
public MemberPath(IMember member, string parentPath)
: this(member.ReflectedType, member.DeclaringType, parentPath.Combine(member.Name))
{
@@ -53,7 +55,7 @@ public bool IsSameAs(MemberPath candidate)
{
string[] candidateSegments = candidate.Segments;
- return candidateSegments.SequenceEqual(Segments);
+ return candidateSegments.SequenceEqual(Segments, MemberPathSegmentEqualityComparer);
}
return false;
@@ -64,7 +66,7 @@ private bool IsParentOf(MemberPath candidate)
string[] candidateSegments = candidate.Segments;
return candidateSegments.Length > Segments.Length &&
- candidateSegments.Take(Segments.Length).SequenceEqual(Segments);
+ candidateSegments.Take(Segments.Length).SequenceEqual(Segments, MemberPathSegmentEqualityComparer);
}
private bool IsChildOf(MemberPath candidate)
@@ -72,7 +74,14 @@ private bool IsChildOf(MemberPath candidate)
string[] candidateSegments = candidate.Segments;
return candidateSegments.Length < Segments.Length
- && candidateSegments.SequenceEqual(Segments.Take(candidateSegments.Length));
+ && candidateSegments.SequenceEqual(Segments.Take(candidateSegments.Length),
+ MemberPathSegmentEqualityComparer);
+ }
+
+ public MemberPath AsParentCollectionOf(MemberPath nextPath)
+ {
+ var extendedDottedPath = dottedPath.Combine(nextPath.dottedPath, "[]");
+ return new MemberPath(declaringType, nextPath.reflectedType, extendedDottedPath);
}
///
@@ -86,7 +95,7 @@ public bool IsEquivalentTo(string path)
public bool HasSameParentAs(MemberPath path)
{
return Segments.Length == path.Segments.Length
- && GetParentSegments().SequenceEqual(path.GetParentSegments());
+ && GetParentSegments().SequenceEqual(path.GetParentSegments(), MemberPathSegmentEqualityComparer);
}
private IEnumerable GetParentSegments() => Segments.Take(Segments.Length - 1);
@@ -96,6 +105,11 @@ public bool HasSameParentAs(MemberPath path)
///
public bool GetContainsSpecificCollectionIndex() => dottedPath.ContainsSpecificCollectionIndex();
+ private string[] Segments =>
+ segments ??= dottedPath
+ .Replace("[]", "[*]", StringComparison.Ordinal)
+ .Split(new[] { '.', '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
+
///
/// Returns a copy of the current object as if it represented an un-indexed item in a collection.
///
@@ -104,8 +118,6 @@ public MemberPath WithCollectionAsRoot()
return new MemberPath(reflectedType, declaringType, "[]." + dottedPath);
}
- private string[] Segments => segments ??= dottedPath.Split(new[] { '.', '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
-
///
/// Returns the name of the member the current path points to without its parent path.
///
diff --git a/Src/FluentAssertions/Common/MemberPathSegmentEqualityComparer.cs b/Src/FluentAssertions/Common/MemberPathSegmentEqualityComparer.cs
new file mode 100644
index 0000000000..10c1a43215
--- /dev/null
+++ b/Src/FluentAssertions/Common/MemberPathSegmentEqualityComparer.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace FluentAssertions.Common
+{
+ ///
+ /// Compares two segments of a .
+ /// Sets the equal with any numeric index qualifier.
+ /// All other comparisons are default string equality.
+ ///
+ internal class MemberPathSegmentEqualityComparer : IEqualityComparer
+ {
+ private const string AnyIndexQualifier = "*";
+ private static readonly Regex IndexQualifierRegex = new(@"^\d+$");
+
+ ///
+ /// Compares two segments of a .
+ ///
+ /// Left part of the comparison.
+ /// Right part of the comparison.
+ /// True if segments are equal, false if not.
+ public bool Equals(string x, string y)
+ {
+ if (x == AnyIndexQualifier)
+ {
+ return IsIndexQualifier(y);
+ }
+
+ if (y == AnyIndexQualifier)
+ {
+ return IsIndexQualifier(x);
+ }
+
+ return x == y;
+ }
+
+ private static bool IsIndexQualifier(string segment)
+ => segment == AnyIndexQualifier || IndexQualifierRegex.IsMatch(segment);
+
+ public int GetHashCode(string obj)
+ {
+#if NETCOREAPP2_1_OR_GREATER
+ return obj.GetHashCode(System.StringComparison.Ordinal);
+#else
+ return obj.GetHashCode();
+#endif
+ }
+ }
+}
diff --git a/Src/FluentAssertions/Common/ReadOnlyNonGenericCollectionWrapper.cs b/Src/FluentAssertions/Common/ReadOnlyNonGenericCollectionWrapper.cs
new file mode 100644
index 0000000000..fc6fa22a81
--- /dev/null
+++ b/Src/FluentAssertions/Common/ReadOnlyNonGenericCollectionWrapper.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+namespace FluentAssertions.Common
+{
+ internal static class ReadOnlyNonGenericCollectionWrapper
+ {
+ public static ReadOnlyNonGenericCollectionWrapper Create(DataTableCollection collection)
+ {
+ return
+ (collection != null)
+ ? new ReadOnlyNonGenericCollectionWrapper(collection)
+ : null;
+ }
+
+ public static ReadOnlyNonGenericCollectionWrapper Create(DataColumnCollection collection)
+ {
+ return
+ (collection != null)
+ ? new ReadOnlyNonGenericCollectionWrapper(collection)
+ : null;
+ }
+
+ public static ReadOnlyNonGenericCollectionWrapper Create(DataRowCollection collection)
+ {
+ return
+ (collection != null)
+ ? new ReadOnlyNonGenericCollectionWrapper(collection)
+ : null;
+ }
+ }
+
+ internal class ReadOnlyNonGenericCollectionWrapper : ICollection
+ where TCollection : ICollection
+ {
+ public TCollection UnderlyingCollection { get; }
+
+ public ReadOnlyNonGenericCollectionWrapper(TCollection collection)
+ {
+ Guard.ThrowIfArgumentIsNull(collection, nameof(collection));
+
+ UnderlyingCollection = collection;
+ }
+
+ public int Count => UnderlyingCollection.Count;
+
+ public bool IsReadOnly => true;
+
+ public IEnumerator GetEnumerator() => UnderlyingCollection.Cast().GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => UnderlyingCollection.GetEnumerator();
+
+ public bool Contains(TItem item) => UnderlyingCollection.Cast().Contains(item);
+
+ public void CopyTo(TItem[] array, int arrayIndex) => UnderlyingCollection.CopyTo(array, arrayIndex);
+
+ /*
+
+ Mutation is not supported, but these methods must be implemented to satisfy ICollection:
+ * Add
+ * Clear
+ * Remove
+
+ */
+
+ public void Add(TItem item) => throw new NotSupportedException();
+
+ public void Clear() => throw new NotSupportedException();
+
+ public bool Remove(TItem item) => throw new NotSupportedException();
+ }
+}
diff --git a/Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs b/Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs
new file mode 100644
index 0000000000..8cc8362408
--- /dev/null
+++ b/Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Globalization;
+using System.Linq;
+
+using FluentAssertions.Collections;
+using FluentAssertions.Common;
+using FluentAssertions.Execution;
+
+namespace FluentAssertions
+{
+ public static class DataColumnCollectionAssertionExtensions
+ {
+ ///
+ /// Asserts that an object reference refers to the exact same object as another object reference.
+ ///
+ /// The expected object
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> BeSameAs(
+ this GenericCollectionAssertions assertion, DataColumnCollection expected, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ expected, nameof(expected), "Cannot verify same reference against a collection (use BeNull instead?).");
+
+ if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper wrapper)
+ {
+ var actualSubject = wrapper.UnderlyingCollection;
+
+ Execute.Assertion
+ .UsingLineBreaks
+ .ForCondition(ReferenceEquals(actualSubject, expected))
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Expected {context:column collection} to refer to {0}{reason}, but found {1} (different underlying object).",
+ expected, actualSubject);
+ }
+ else
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Invalid expectation: Expected {context:column collection} to refer to an instance of " +
+ "DataColumnCollection{reason}, but found {0}.",
+ assertion.Subject);
+ }
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Asserts that an object reference refers to a different object than another object reference refers to.
+ ///
+ /// The unexpected object
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> NotBeSameAs(
+ this GenericCollectionAssertions assertion, DataColumnCollection unexpected, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ unexpected, nameof(unexpected), "Cannot verify same reference against a collection (use NotBeNull instead?).");
+
+ if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper wrapper)
+ {
+ var actualSubject = wrapper.UnderlyingCollection;
+
+ Execute.Assertion
+ .UsingLineBreaks
+ .ForCondition(!ReferenceEquals(actualSubject, unexpected))
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Did not expect {context:column collection} to refer to {0}{reason}.", unexpected);
+ }
+ else
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Invalid expectation: Expected {context:column collection} to refer to a different instance of " +
+ "DataColumnCollection{reason}, but found {0}.",
+ assertion.Subject);
+ }
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Assert that the current collection has the same number of elements as .
+ ///
+ /// The other collection with the same expected number of elements
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> HaveSameCount(
+ this GenericCollectionAssertions assertion, DataColumnCollection otherCollection, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ otherCollection, nameof(otherCollection), "Cannot verify count against a collection.");
+
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Expected {context:collection} to have ")
+ .Given(() => assertion.Subject)
+ .ForCondition(subject => subject is not null)
+ .FailWith("the same count as {0}{reason}, but found .", otherCollection)
+ .Then
+ .Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
+ .ForCondition(count => count.actual == count.expected)
+ .FailWith("{0} column(s){reason}, but found {1}.", count => count.expected, count => count.actual)
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Assert that the current collection of s does not have the same number of columns as
+ /// .
+ ///
+ /// The other with the unexpected number of
+ /// elements
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> NotHaveSameCount(
+ this GenericCollectionAssertions assertion, DataColumnCollection otherCollection, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ otherCollection, nameof(otherCollection), "Cannot verify count against a collection.");
+
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Expected {context:collection} to not have ")
+ .Given(() => assertion.Subject)
+ .ForCondition(subject => subject is not null)
+ .FailWith("the same count as {0}{reason}, but found .", otherCollection)
+ .Then
+ .Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
+ .ForCondition(count => count.actual != count.expected)
+ .FailWith("{0} column(s){reason}, but found {1}.", count => count.expected, count => count.actual)
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint>(assertion);
+ }
+ }
+}
diff --git a/Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs b/Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs
new file mode 100644
index 0000000000..ea5e745ff3
--- /dev/null
+++ b/Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+using FluentAssertions.Collections;
+using FluentAssertions.Common;
+using FluentAssertions.Data;
+using FluentAssertions.Equivalency;
+using FluentAssertions.Execution;
+
+namespace FluentAssertions
+{
+ public static class DataRowCollectionAssertionExtensions
+ {
+ ///
+ /// Asserts that an object reference refers to the exact same object as another object reference.
+ ///
+ /// The expected object
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> BeSameAs(
+ this GenericCollectionAssertions assertion, DataRowCollection expected, string because = "",
+ params object[] becauseArgs)
+ {
+ if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper wrapper)
+ {
+ var actualSubject = wrapper.UnderlyingCollection;
+
+ Execute.Assertion
+ .UsingLineBreaks
+ .ForCondition(ReferenceEquals(actualSubject, expected))
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Expected {context:row collection} to refer to {0}{reason}, but found {1} (different underlying object).",
+ expected, actualSubject);
+ }
+ else
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Invalid expectation: Expected {context:column collection} to refer to an instance of " +
+ "DataRowCollection{reason}, but found {0}.",
+ assertion.Subject);
+ }
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Asserts that an object reference refers to a different object than another object reference refers to.
+ ///
+ /// The unexpected object
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> NotBeSameAs(
+ this GenericCollectionAssertions assertion, DataRowCollection unexpected, string because = "",
+ params object[] becauseArgs)
+ {
+ if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper wrapper)
+ {
+ var actualSubject = wrapper.UnderlyingCollection;
+
+ Execute.Assertion
+ .UsingLineBreaks
+ .ForCondition(!ReferenceEquals(actualSubject, unexpected))
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Did not expect {context:row collection} to refer to {0}{reason}.", unexpected);
+ }
+ else
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Invalid expectation: Expected {context:column collection} to refer to a different instance of " +
+ "DataRowCollection{reason}, but found {0}.",
+ assertion.Subject);
+ }
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Assert that the current collection of s has the same number of rows as
+ /// .
+ ///
+ /// The other collection with the same expected number of elements
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> HaveSameCount(
+ this GenericCollectionAssertions assertion, DataRowCollection otherCollection, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ otherCollection, nameof(otherCollection), "Cannot verify count against a collection.");
+
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Expected {context:collection} to have ")
+ .Given(() => assertion.Subject)
+ .ForCondition(subject => subject is not null)
+ .FailWith("the same count as {0}{reason}, but found .", otherCollection)
+ .Then
+ .Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
+ .ForCondition(count => count.actual == count.expected)
+ .FailWith("{0} row(s){reason}, but found {1}.", count => count.expected, count => count.actual)
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Assert that the current collection of s does not have the same number of rows as
+ /// .
+ ///
+ /// The other with the unexpected number of elements
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> NotHaveSameCount(
+ this GenericCollectionAssertions assertion, DataRowCollection otherCollection, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ otherCollection, nameof(otherCollection), "Cannot verify count against a collection.");
+
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Expected {context:collection} to not have ")
+ .Given(() => assertion.Subject)
+ .ForCondition(subject => subject is not null)
+ .FailWith("the same count as {0}{reason}, but found .", otherCollection)
+ .Then
+ .Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
+ .ForCondition(count => count.actual != count.expected)
+ .FailWith("{0} row(s){reason}, but found {1}.", count => count.expected, count => count.actual)
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint>(assertion);
+ }
+ }
+}
diff --git a/Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs b/Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs
new file mode 100644
index 0000000000..76cdf9ea64
--- /dev/null
+++ b/Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Data;
+using System.Linq;
+
+using FluentAssertions.Collections;
+using FluentAssertions.Common;
+using FluentAssertions.Execution;
+
+namespace FluentAssertions
+{
+ public static class DataTableCollectionAssertionExtensions
+ {
+ ///
+ /// Asserts that an object reference refers to the exact same object as another object reference.
+ ///
+ /// The expected object
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> BeSameAs(
+ this GenericCollectionAssertions assertion, DataTableCollection expected, string because = "",
+ params object[] becauseArgs)
+ {
+ if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper wrapper)
+ {
+ var actualSubject = wrapper.UnderlyingCollection;
+
+ Execute.Assertion
+ .UsingLineBreaks
+ .ForCondition(ReferenceEquals(actualSubject, expected))
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Expected {context:table collection} to refer to {0}{reason}, but found {1} (different underlying object).",
+ expected, actualSubject);
+ }
+ else
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Invalid expectation: Expected {context:column collection} to refer to an instance of " +
+ "DataTableCollection{reason}, but found {0}.",
+ assertion.Subject);
+ }
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Asserts that an object reference refers to a different object than another object reference refers to.
+ ///
+ /// The unexpected object
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> NotBeSameAs(
+ this GenericCollectionAssertions assertion, DataTableCollection unexpected, string because = "",
+ params object[] becauseArgs)
+ {
+ if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper wrapper)
+ {
+ var actualSubject = wrapper.UnderlyingCollection;
+
+ Execute.Assertion
+ .UsingLineBreaks
+ .ForCondition(!ReferenceEquals(actualSubject, unexpected))
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Did not expect {context:table collection} to refer to {0}{reason}.", unexpected);
+ }
+ else
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Invalid expectation: Expected {context:column collection} to refer to a different instance of " +
+ "DataTableCollection{reason}, but found {0}.",
+ assertion.Subject);
+ }
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Assert that the current collection of s has the same number of tables as
+ /// .
+ ///
+ /// The other with the same expected number of tables
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> HaveSameCount(
+ this GenericCollectionAssertions assertion, DataSet otherDataSet, string because = "",
+ params object[] becauseArgs)
+ {
+ return assertion.HaveSameCount(otherDataSet.Tables, because, becauseArgs);
+ }
+
+ ///
+ /// Assert that the current collection of s does not have the same number of tables as
+ /// .
+ ///
+ /// The other with the unexpected number of tables
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> NotHaveSameCount(
+ this GenericCollectionAssertions assertion, DataSet otherDataSet, string because = "",
+ params object[] becauseArgs)
+ {
+ return assertion.NotHaveSameCount(otherDataSet.Tables, because, becauseArgs);
+ }
+
+ ///
+ /// Assert that the current collection of s has the same number of tables as
+ /// .
+ ///
+ /// The other with the same expected number of tables
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> HaveSameCount(
+ this GenericCollectionAssertions assertion, DataTableCollection otherCollection, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ otherCollection, nameof(otherCollection), "Cannot verify count against a collection.");
+
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Expected {context:collection} to have ")
+ .Given(() => assertion.Subject)
+ .ForCondition(subject => subject is not null)
+ .FailWith("the same count as {0}{reason}, but found .", otherCollection)
+ .Then
+ .Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
+ .ForCondition(count => count.actual == count.expected)
+ .FailWith("{0} table(s){reason}, but found {1}.", count => count.expected, count => count.actual)
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint>(assertion);
+ }
+
+ ///
+ /// Assert that the current collection of s does not have the same number of tables as
+ /// .
+ ///
+ /// The other with the unexpected number of tables
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public static AndConstraint> NotHaveSameCount(
+ this GenericCollectionAssertions assertion, DataTableCollection otherCollection, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(
+ otherCollection, nameof(otherCollection), "Cannot verify count against a collection.");
+
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Expected {context:collection} to not have ")
+ .Given(() => assertion.Subject)
+ .ForCondition(subject => subject is not null)
+ .FailWith("the same count as {0}{reason}, but found .", otherCollection)
+ .Then
+ .Given((subject) => (actual: subject.Count(), expected: otherCollection.Count))
+ .ForCondition(count => count.actual != count.expected)
+ .FailWith("{0} table(s){reason}, but found {1}.", count => count.expected, count => count.actual)
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint>(assertion);
+ }
+ }
+}
diff --git a/Src/FluentAssertions/Equivalency/EquivalencyAssertionOptions.cs b/Src/FluentAssertions/Equivalency/EquivalencyAssertionOptions.cs
index 0b16cdf241..14fbc86abc 100644
--- a/Src/FluentAssertions/Equivalency/EquivalencyAssertionOptions.cs
+++ b/Src/FluentAssertions/Equivalency/EquivalencyAssertionOptions.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using FluentAssertions.Common;
using FluentAssertions.Equivalency.Execution;
@@ -40,6 +39,17 @@ public EquivalencyAssertionOptions Excluding(Expression
+ /// Selects a collection to define exclusions at.
+ /// Allows to navigate deeper by using .
+ ///
+ public NestedExclusionOptionBuilder For(Expression>> expression)
+ {
+ var selectionRule = new ExcludeMemberByPathSelectionRule(expression.GetMemberPath());
+ AddSelectionRule(selectionRule);
+ return new NestedExclusionOptionBuilder(this, selectionRule);
+ }
+
///
/// Includes the specified member in the equality check.
///
diff --git a/Src/FluentAssertions/Equivalency/Execution/CollectionMemberAssertionOptionsDecorator.cs b/Src/FluentAssertions/Equivalency/Execution/CollectionMemberAssertionOptionsDecorator.cs
index 86c06cf999..86ec59e34a 100644
--- a/Src/FluentAssertions/Equivalency/Execution/CollectionMemberAssertionOptionsDecorator.cs
+++ b/Src/FluentAssertions/Equivalency/Execution/CollectionMemberAssertionOptionsDecorator.cs
@@ -61,6 +61,10 @@ public IEnumerable UserEquivalencySteps
public MemberVisibility IncludedFields => inner.IncludedFields;
+ public bool IgnoreNonBrowsableOnSubject => inner.IgnoreNonBrowsableOnSubject;
+
+ public bool ExcludeNonBrowsableOnExpectation => inner.ExcludeNonBrowsableOnExpectation;
+
public bool CompareRecordsByValue => inner.CompareRecordsByValue;
public EqualityStrategy GetEqualityStrategy(Type type)
diff --git a/Src/FluentAssertions/Equivalency/Field.cs b/Src/FluentAssertions/Equivalency/Field.cs
index 04b06c32da..9c1720b9c6 100644
--- a/Src/FluentAssertions/Equivalency/Field.cs
+++ b/Src/FluentAssertions/Equivalency/Field.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Reflection;
using FluentAssertions.Common;
@@ -10,6 +11,7 @@ namespace FluentAssertions.Equivalency
public class Field : Node, IMember
{
private readonly FieldInfo fieldInfo;
+ private bool? isBrowsable;
public Field(FieldInfo fieldInfo, INode parent)
: this(fieldInfo.ReflectedType, fieldInfo, parent)
@@ -42,5 +44,18 @@ public object GetValue(object obj)
public CSharpAccessModifier GetterAccessibility => fieldInfo.GetCSharpAccessModifier();
public CSharpAccessModifier SetterAccessibility => fieldInfo.GetCSharpAccessModifier();
+
+ public bool IsBrowsable
+ {
+ get
+ {
+ if (isBrowsable == null)
+ {
+ isBrowsable = fieldInfo.GetCustomAttribute() is not { State: EditorBrowsableState.Never };
+ }
+
+ return isBrowsable.Value;
+ }
+ }
}
}
diff --git a/Src/FluentAssertions/Equivalency/IEquivalencyAssertionOptions.cs b/Src/FluentAssertions/Equivalency/IEquivalencyAssertionOptions.cs
index bcc5a61199..6c0ff7279e 100644
--- a/Src/FluentAssertions/Equivalency/IEquivalencyAssertionOptions.cs
+++ b/Src/FluentAssertions/Equivalency/IEquivalencyAssertionOptions.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+
using FluentAssertions.Equivalency.Tracing;
namespace FluentAssertions.Equivalency
@@ -71,6 +73,18 @@ public interface IEquivalencyAssertionOptions
///
MemberVisibility IncludedFields { get; }
+ ///
+ /// Gets a value indicating whether members on the subject marked with []
+ /// and should be treated as though they don't exist.
+ ///
+ bool IgnoreNonBrowsableOnSubject { get; }
+
+ ///
+ /// Gets a value indicating whether members on the expectation marked with []
+ /// and should be excluded.
+ ///
+ bool ExcludeNonBrowsableOnExpectation { get; }
+
///
/// Gets a value indicating whether records should be compared by value instead of their members
///
diff --git a/Src/FluentAssertions/Equivalency/IMember.cs b/Src/FluentAssertions/Equivalency/IMember.cs
index f14c39ab95..91f4acc169 100644
--- a/Src/FluentAssertions/Equivalency/IMember.cs
+++ b/Src/FluentAssertions/Equivalency/IMember.cs
@@ -1,4 +1,6 @@
using System;
+using System.ComponentModel;
+
using FluentAssertions.Common;
namespace FluentAssertions.Equivalency
@@ -32,5 +34,11 @@ public interface IMember : INode
/// Gets the access modifier for the setter of this member.
///
CSharpAccessModifier SetterAccessibility { get; }
+
+ ///
+ /// Gets a value indicating whether the member is browsable in the source code editor. This is controlled with
+ /// .
+ ///
+ bool IsBrowsable { get; }
}
}
diff --git a/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs b/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs
index d710ac0e98..b7b45812b7 100644
--- a/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs
+++ b/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs
@@ -40,7 +40,7 @@ public IMember Match(IMember expectedMember, object subject, INode parent, IEqui
{
path = path.WithCollectionAsRoot();
}
-
+
if (path.IsEquivalentTo(expectedMember.PathAndName))
{
var member = MemberFactory.Find(subject, subjectPath.MemberName, expectedMember.Type, parent);
diff --git a/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs b/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs
index e386b51d07..64ee48c4ea 100644
--- a/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs
+++ b/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs
@@ -36,6 +36,13 @@ public IMember Match(IMember expectedMember, object subject, INode parent, IEqui
$"Expectation has {expectedMember.Description} that the other object does not have.");
}
+ if (config.IgnoreNonBrowsableOnSubject && !subjectMember.IsBrowsable)
+ {
+ Execute.Assertion.FailWith(
+ $"Expectation has {expectedMember.Description} that is non-browsable in the other object, and non-browsable " +
+ $"members on the subject are ignored with the current configuration");
+ }
+
return subjectMember;
}
diff --git a/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs
index 963b092868..15b6692b64 100644
--- a/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs
+++ b/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs
@@ -13,8 +13,7 @@ internal class MultiDimensionalArrayEquivalencyStep : IEquivalencyStep
{
public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
{
- Array expectationAsArray = comparands.Expectation as Array;
- if (expectationAsArray is null || expectationAsArray?.Rank == 1)
+ if (comparands.Expectation is not Array expectationAsArray || expectationAsArray?.Rank == 1)
{
return EquivalencyResult.ContinueWithNext;
}
diff --git a/Src/FluentAssertions/Equivalency/NestedExclusionOptionBuilder.cs b/Src/FluentAssertions/Equivalency/NestedExclusionOptionBuilder.cs
new file mode 100644
index 0000000000..02821424f1
--- /dev/null
+++ b/Src/FluentAssertions/Equivalency/NestedExclusionOptionBuilder.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using FluentAssertions.Common;
+using FluentAssertions.Equivalency.Selection;
+
+namespace FluentAssertions.Equivalency
+{
+ public class NestedExclusionOptionBuilder
+ {
+ ///
+ /// The selected path starting at the first .
+ ///
+ private readonly ExcludeMemberByPathSelectionRule currentPathSelectionRule;
+
+ private readonly EquivalencyAssertionOptions capturedAssertionOptions;
+
+ internal NestedExclusionOptionBuilder(EquivalencyAssertionOptions capturedAssertionOptions,
+ ExcludeMemberByPathSelectionRule currentPathSelectionRule)
+ {
+ this.capturedAssertionOptions = capturedAssertionOptions;
+ this.currentPathSelectionRule = currentPathSelectionRule;
+ }
+
+ ///
+ /// Selects a nested property to exclude. This ends the chain.
+ ///
+ public EquivalencyAssertionOptions Exclude(Expression> expression)
+ {
+ var nextPath = expression.GetMemberPath();
+ currentPathSelectionRule.AppendPath(nextPath);
+ return capturedAssertionOptions;
+ }
+
+ ///
+ /// Adds the selected collection to the chain.
+ ///
+ public NestedExclusionOptionBuilder For(
+ Expression>> expression)
+ {
+ var nextPath = expression.GetMemberPath();
+ currentPathSelectionRule.AppendPath(nextPath);
+ return new NestedExclusionOptionBuilder(capturedAssertionOptions, currentPathSelectionRule);
+ }
+ }
+}
diff --git a/Src/FluentAssertions/Equivalency/Property.cs b/Src/FluentAssertions/Equivalency/Property.cs
index f53eeab7a0..de3741bb9a 100644
--- a/Src/FluentAssertions/Equivalency/Property.cs
+++ b/Src/FluentAssertions/Equivalency/Property.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Reflection;
using FluentAssertions.Common;
@@ -11,6 +12,7 @@ namespace FluentAssertions.Equivalency
public class Property : Node, IMember
{
private readonly PropertyInfo propertyInfo;
+ private bool? isBrowsable;
public Property(PropertyInfo propertyInfo, INode parent)
: this(propertyInfo.ReflectedType, propertyInfo, parent)
@@ -43,5 +45,18 @@ public object GetValue(object obj)
public CSharpAccessModifier GetterAccessibility => propertyInfo.GetGetMethod(nonPublic: true).GetCSharpAccessModifier();
public CSharpAccessModifier SetterAccessibility => propertyInfo.GetSetMethod(nonPublic: true).GetCSharpAccessModifier();
+
+ public bool IsBrowsable
+ {
+ get
+ {
+ if (isBrowsable == null)
+ {
+ isBrowsable = propertyInfo.GetCustomAttribute() is not { State: EditorBrowsableState.Never };
+ }
+
+ return isBrowsable.Value;
+ }
+ }
}
}
diff --git a/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs b/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs
index 3b4c263837..0d68882444 100644
--- a/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs
+++ b/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs
@@ -8,7 +8,7 @@ namespace FluentAssertions.Equivalency.Selection
///
internal class ExcludeMemberByPathSelectionRule : SelectMemberByPathSelectionRule
{
- private readonly MemberPath memberToExclude;
+ private MemberPath memberToExclude;
public ExcludeMemberByPathSelectionRule(MemberPath pathToExclude)
: base(pathToExclude.ToString())
@@ -23,6 +23,12 @@ public ExcludeMemberByPathSelectionRule(MemberPath pathToExclude)
memberToExclude.IsSameAs(new MemberPath(member, parentPath)));
}
+ public void AppendPath(MemberPath nextPath)
+ {
+ memberToExclude = memberToExclude.AsParentCollectionOf(nextPath);
+ SetSelectedPath(memberToExclude.ToString());
+ }
+
public override string ToString()
{
return "Exclude member " + memberToExclude;
diff --git a/Src/FluentAssertions/Equivalency/Selection/ExcludeNonBrowsableMembersRule.cs b/Src/FluentAssertions/Equivalency/Selection/ExcludeNonBrowsableMembersRule.cs
new file mode 100644
index 0000000000..c5ee5f3f83
--- /dev/null
+++ b/Src/FluentAssertions/Equivalency/Selection/ExcludeNonBrowsableMembersRule.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace FluentAssertions.Equivalency.Selection
+{
+ internal class ExcludeNonBrowsableMembersRule : IMemberSelectionRule
+ {
+ public bool IncludesMembers => false;
+
+ public IEnumerable SelectMembers(INode currentNode, IEnumerable selectedMembers, MemberSelectionContext context)
+ {
+ return selectedMembers.Where(member => member.IsBrowsable).ToList();
+ }
+ }
+}
diff --git a/Src/FluentAssertions/Equivalency/Selection/SelectMemberByPathSelectionRule.cs b/Src/FluentAssertions/Equivalency/Selection/SelectMemberByPathSelectionRule.cs
index 8a32b13ea3..753b59818a 100644
--- a/Src/FluentAssertions/Equivalency/Selection/SelectMemberByPathSelectionRule.cs
+++ b/Src/FluentAssertions/Equivalency/Selection/SelectMemberByPathSelectionRule.cs
@@ -7,7 +7,7 @@ namespace FluentAssertions.Equivalency.Selection
{
internal abstract class SelectMemberByPathSelectionRule : IMemberSelectionRule
{
- private readonly string selectedPath;
+ private string selectedPath;
protected SelectMemberByPathSelectionRule(string selectedPath)
{
@@ -16,6 +16,11 @@ protected SelectMemberByPathSelectionRule(string selectedPath)
public virtual bool IncludesMembers => false;
+ protected void SetSelectedPath(string path)
+ {
+ this.selectedPath = path;
+ }
+
public IEnumerable SelectMembers(INode currentNode, IEnumerable selectedMembers,
MemberSelectionContext context)
{
diff --git a/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs b/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs
index ac095d0df7..ca8d9dbe0a 100644
--- a/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs
+++ b/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
@@ -56,6 +57,8 @@ public abstract class SelfReferenceEquivalencyAssertionOptions : IEquival
private MemberVisibility includedProperties;
private MemberVisibility includedFields;
+ private bool ignoreNonBrowsableOnSubject;
+ private bool excludeNonBrowsableOnExpectation;
private bool compareRecordsByValue;
@@ -80,6 +83,8 @@ protected SelfReferenceEquivalencyAssertionOptions(IEquivalencyAssertionOptions
useRuntimeTyping = defaults.UseRuntimeTyping;
includedProperties = defaults.IncludedProperties;
includedFields = defaults.IncludedFields;
+ ignoreNonBrowsableOnSubject = defaults.IgnoreNonBrowsableOnSubject;
+ excludeNonBrowsableOnExpectation = defaults.ExcludeNonBrowsableOnExpectation;
compareRecordsByValue = defaults.CompareRecordsByValue;
ConversionSelector = defaults.ConversionSelector.Clone();
@@ -115,6 +120,11 @@ IEnumerable IEquivalencyAssertionOptions.SelectionRules
yield return new AllFieldsSelectionRule();
}
+ if (excludeNonBrowsableOnExpectation)
+ {
+ yield return new ExcludeNonBrowsableMembersRule();
+ }
+
foreach (IMemberSelectionRule rule in selectionRules)
{
yield return rule;
@@ -162,6 +172,10 @@ IEnumerable IEquivalencyAssertionOptions.SelectionRules
MemberVisibility IEquivalencyAssertionOptions.IncludedFields => includedFields;
+ bool IEquivalencyAssertionOptions.IgnoreNonBrowsableOnSubject => ignoreNonBrowsableOnSubject;
+
+ bool IEquivalencyAssertionOptions.ExcludeNonBrowsableOnExpectation => excludeNonBrowsableOnExpectation;
+
public bool CompareRecordsByValue => compareRecordsByValue;
EqualityStrategy IEquivalencyAssertionOptions.GetEqualityStrategy(Type requestedType)
@@ -312,6 +326,29 @@ public TSelf ExcludingProperties()
return (TSelf)this;
}
+ ///
+ /// Instructs the comparison to exclude non-browsable members in the expectation (members set to
+ /// ). It is not required that they be marked non-browsable in the subject. Use
+ /// to ignore non-browsable members in the subject.
+ ///
+ ///
+ public TSelf ExcludingNonBrowsableMembers()
+ {
+ excludeNonBrowsableOnExpectation = true;
+ return (TSelf)this;
+ }
+
+ ///
+ /// Instructs the comparison to treat non-browsable members in the subject as though they do not exist. If you need to
+ /// ignore non-browsable members in the expectation, use .
+ ///
+ ///
+ public TSelf IgnoringNonBrowsableMembersOnSubject()
+ {
+ ignoreNonBrowsableOnSubject = true;
+ return (TSelf)this;
+ }
+
///
/// Instructs the comparison to respect the expectation's runtime type.
///
@@ -698,6 +735,11 @@ public override string ToString()
.Append(useRuntimeTyping ? "runtime" : "declared")
.AppendLine(" types and members");
+ if (ignoreNonBrowsableOnSubject)
+ {
+ builder.AppendLine("- Do not consider members marked non-browsable on the subject");
+ }
+
if (isRecursive)
{
if (allowInfiniteRecursion)
@@ -737,6 +779,15 @@ public override string ToString()
builder.AppendLine($"- Compare {type} by its members");
}
+ if (excludeNonBrowsableOnExpectation)
+ {
+ builder.AppendLine("- Exclude non-browsable members");
+ }
+ else
+ {
+ builder.AppendLine("- Include non-browsable members");
+ }
+
foreach (IMemberSelectionRule rule in selectionRules)
{
builder.Append("- ").AppendLine(rule.ToString());
@@ -811,7 +862,7 @@ private void RemoveSelectionRule()
{
selectionRules.RemoveAll(selectionRule => selectionRule is T);
}
-
+
protected TSelf AddSelectionRule(IMemberSelectionRule selectionRule)
{
selectionRules.Add(selectionRule);
diff --git a/Src/FluentAssertions/Equivalency/Steps/DataSetEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/DataSetEquivalencyStep.cs
index 209b5c815f..3cdde14384 100644
--- a/Src/FluentAssertions/Equivalency/Steps/DataSetEquivalencyStep.cs
+++ b/Src/FluentAssertions/Equivalency/Steps/DataSetEquivalencyStep.cs
@@ -11,9 +11,8 @@ public class DataSetEquivalencyStep : EquivalencyStep
protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
{
var subject = comparands.Subject as DataSet;
- var expectation = comparands.Expectation as DataSet;
- if (expectation is null)
+ if (comparands.Expectation is not DataSet expectation)
{
if (subject is not null)
{
diff --git a/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs
index 845f8a2633..382b686d73 100644
--- a/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs
+++ b/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs
@@ -82,6 +82,11 @@ public class StructuralEqualityEquivalencyStep : IEquivalencyStep
where match is not null
select match;
+ if (config.IgnoreNonBrowsableOnSubject)
+ {
+ query = query.Where(member => member.IsBrowsable);
+ }
+
return query.FirstOrDefault();
}
diff --git a/Src/FluentAssertions/EventRaisingExtensions.cs b/Src/FluentAssertions/EventRaisingExtensions.cs
index f73e98a6ec..61bff30c47 100644
--- a/Src/FluentAssertions/EventRaisingExtensions.cs
+++ b/Src/FluentAssertions/EventRaisingExtensions.cs
@@ -53,8 +53,9 @@ public static IEventRecording WithSender(this IEventRecording eventRecording, ob
}
///
- /// Asserts that at least one occurrence of the events had at least one of the arguments matching a predicate. Returns
- /// only the events that matched that predicate.
+ /// Asserts that at least one occurence of the events had one or more arguments of the expected
+ /// type which matched the given predicate.
+ /// Returns only the events that matched both type and optionally a predicate.
///
public static IEventRecording WithArgs(this IEventRecording eventRecording, Expression> predicate)
{
@@ -62,40 +63,33 @@ public static IEventRecording WithArgs(this IEventRecording eventRecording, E
Func compiledPredicate = predicate.Compile();
- bool hasArgumentOfRightType = false;
- var eventsMatchingPredicate = new List();
+ var eventsWithMatchingPredicate = new List();
foreach (OccurredEvent @event in eventRecording)
{
var typedParameters = @event.Parameters.OfType().ToArray();
- if (typedParameters.Any())
- {
- hasArgumentOfRightType = true;
- }
if (typedParameters.Any(parameter => compiledPredicate(parameter)))
{
- eventsMatchingPredicate.Add(@event);
+ eventsWithMatchingPredicate.Add(@event);
}
}
- if (!hasArgumentOfRightType)
- {
- throw new ArgumentException("No argument of event " + eventRecording.EventName + " is of type <" + typeof(T) + ">.");
- }
+ bool foundMatchingEvent = eventsWithMatchingPredicate.Any();
- if (!eventsMatchingPredicate.Any())
- {
- Execute.Assertion
- .FailWith("Expected at least one event with arguments matching {0}, but found none.", predicate.Body);
- }
+ Execute.Assertion
+ .ForCondition(foundMatchingEvent)
+ .FailWith("Expected at least one event which arguments are of type <{0}> and matches {1}, but found none.",
+ typeof(T),
+ predicate.Body);
- return new FilteredEventRecording(eventRecording, eventsMatchingPredicate);
+ return new FilteredEventRecording(eventRecording, eventsWithMatchingPredicate);
}
///
- /// Asserts that at least one of the occurred events has arguments the match the predicates in the same order. Returns
- /// only the events that matched those predicates.
+ /// Asserts that at least one occurence of the events had one or more arguments of the expected
+ /// type which matched the predicates in the same order.
+ /// Returns only the events that matched both type and optionally predicates.
///
///
/// If a null is provided as predicate argument, the corresponding event parameter value is ignored.
@@ -104,16 +98,12 @@ public static IEventRecording WithArgs(this IEventRecording eventRecording, p
{
Func[] compiledPredicates = predicates.Select(p => p?.Compile()).ToArray();
- bool hasArgumentOfRightType = false;
- var eventsMatchingPredicate = new List();
+ var eventsWithMatchingPredicate = new List();
foreach (OccurredEvent @event in eventRecording)
{
var typedParameters = @event.Parameters.OfType().ToArray();
- if (typedParameters.Any())
- {
- hasArgumentOfRightType = true;
- }
+ bool hasArgumentOfRightType = typedParameters.Any();
if (predicates.Length > typedParameters.Length)
{
@@ -121,7 +111,7 @@ public static IEventRecording WithArgs(this IEventRecording eventRecording, p
$"Expected the event to have at least {predicates.Length} parameters of type {typeof(T)}, but only found {typedParameters.Length}.");
}
- bool isMatch = true;
+ bool isMatch = hasArgumentOfRightType;
for (int index = 0; index < predicates.Length && isMatch; index++)
{
isMatch = compiledPredicates[index]?.Invoke(typedParameters[index]) ?? true;
@@ -129,24 +119,21 @@ public static IEventRecording WithArgs(this IEventRecording eventRecording, p
if (isMatch)
{
- eventsMatchingPredicate.Add(@event);
+ eventsWithMatchingPredicate.Add(@event);
}
}
- if (!hasArgumentOfRightType)
- {
- throw new ArgumentException("No argument of event " + eventRecording.EventName + " is of type <" + typeof(T) + ">.");
- }
+ bool foundMatchingEvent = eventsWithMatchingPredicate.Any();
- if (!eventsMatchingPredicate.Any())
+ if (!foundMatchingEvent)
{
- Execute
- .Assertion
- .FailWith("Expected at least one event with arguments matching {0}, but found none.",
- string.Join(" | ", predicates.Where(p => p is not null).Select(p => p.Body.ToString())));
+ Execute.Assertion
+ .FailWith("Expected at least one event which arguments are of type <{0}> and matches {1}, but found none.",
+ typeof(T),
+ string.Join(" | ", predicates.Where(p => p is not null).Select(p => p.Body.ToString())));
}
- return new FilteredEventRecording(eventRecording, eventsMatchingPredicate);
+ return new FilteredEventRecording(eventRecording, eventsWithMatchingPredicate);
}
}
}
diff --git a/Src/FluentAssertions/Execution/AssertionScope.cs b/Src/FluentAssertions/Execution/AssertionScope.cs
index ab7251a453..4d04e733fc 100644
--- a/Src/FluentAssertions/Execution/AssertionScope.cs
+++ b/Src/FluentAssertions/Execution/AssertionScope.cs
@@ -205,7 +205,7 @@ public Continuation ClearExpectation()
{
expectation = null;
- // SMELL: Isn't this always going to return null? Or this method also called without FailWidth (which sets the success state to null)
+ // SMELL: Isn't this always going to return null? Or this method also called without FailWith (which sets the success state to null)
return new Continuation(this, Succeeded);
}
diff --git a/Src/FluentAssertions/FluentAssertions.csproj b/Src/FluentAssertions/FluentAssertions.csproj
index 9c82594817..c13a513dc3 100644
--- a/Src/FluentAssertions/FluentAssertions.csproj
+++ b/Src/FluentAssertions/FluentAssertions.csproj
@@ -54,7 +54,7 @@
-
+
diff --git a/Src/FluentAssertions/Numeric/NumericAssertions.cs b/Src/FluentAssertions/Numeric/NumericAssertions.cs
index 62d06e1d7d..9a6284149f 100644
--- a/Src/FluentAssertions/Numeric/NumericAssertions.cs
+++ b/Src/FluentAssertions/Numeric/NumericAssertions.cs
@@ -292,10 +292,10 @@ public AndConstraint BeLessThan(T expected, string because = "", pa
/// Where the range is continuous or incremental depends on the actual type of the value.
///
///
- /// The minimum valid value of the range.
+ /// The minimum valid value of the range (inclusive).
///
///
- /// The maximum valid value of the range.
+ /// The maximum valid value of the range (inclusive).
///
///
/// A formatted phrase as is supported by explaining why the assertion
diff --git a/Src/FluentAssertions/Primitives/EnumAssertions.cs b/Src/FluentAssertions/Primitives/EnumAssertions.cs
index 77a6ee052e..0bbd7aea8c 100644
--- a/Src/FluentAssertions/Primitives/EnumAssertions.cs
+++ b/Src/FluentAssertions/Primitives/EnumAssertions.cs
@@ -129,6 +129,58 @@ public AndConstraint Be(TEnum? expected, string because = "", param
return new AndConstraint((TAssertions)this);
}
+ ///
+ /// Asserts that the current value of is defined inside the enum.
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint BeDefined(string because = "", params object[] becauseArgs)
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Expected {context:the enum} to be defined in {0}{reason}, ", typeof(TEnum))
+ .ForCondition(Subject is not null)
+ .FailWith("but found .")
+ .Then
+ .ForCondition(Enum.IsDefined(typeof(TEnum), Subject))
+ .FailWith("but it is not.")
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint((TAssertions)this);
+ }
+
+ ///
+ /// Asserts that the current value of is not defined inside the enum.
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndConstraint NotBeDefined(string because = "", params object[] becauseArgs)
+ {
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .WithExpectation("Did not expect {context:the enum} to be defined in {0}{reason}, ", typeof(TEnum))
+ .ForCondition(Subject is not null)
+ .FailWith("but found .")
+ .Then
+ .ForCondition(!Enum.IsDefined(typeof(TEnum), Subject))
+ .FailWith("but it is.")
+ .Then
+ .ClearExpectation();
+
+ return new AndConstraint((TAssertions)this);
+ }
+
///
/// Asserts that the current is exactly equal to the value.
///
diff --git a/Src/FluentAssertions/Primitives/StringAssertions.cs b/Src/FluentAssertions/Primitives/StringAssertions.cs
index a55ad0236d..60ca4084e5 100644
--- a/Src/FluentAssertions/Primitives/StringAssertions.cs
+++ b/Src/FluentAssertions/Primitives/StringAssertions.cs
@@ -507,8 +507,9 @@ public AndConstraint NotMatch(string wildcardPattern, string becaus
.ForConstraint(occurrenceConstraint, actual)
.UsingLineBreaks
.BecauseOf(because, becauseArgs)
- .FailWith($"Expected {{context:string}} to match regex {{0}} {{expectedOccurrence}}{{reason}}, but found it {actual.Times()}.",
- regexStr);
+ .FailWith($"Expected {{context:string}} {{0}} to match regex {{1}} {{expectedOccurrence}}{{reason}}, " +
+ $"but found it {actual.Times()}.",
+ Subject, regexStr);
}
return new AndConstraint((TAssertions)this);
diff --git a/Src/FluentAssertions/Primitives/StringWildcardMatchingValidator.cs b/Src/FluentAssertions/Primitives/StringWildcardMatchingValidator.cs
index 1430be8126..40bc16c682 100644
--- a/Src/FluentAssertions/Primitives/StringWildcardMatchingValidator.cs
+++ b/Src/FluentAssertions/Primitives/StringWildcardMatchingValidator.cs
@@ -49,11 +49,6 @@ private static string ConvertWildcardToRegEx(string wildcardExpression)
private string CleanNewLines(string input)
{
- if (input is null)
- {
- return null;
- }
-
return IgnoreNewLineDifferences ? input.RemoveNewLines() : input;
}
diff --git a/Src/FluentAssertions/Xml/XDocumentAssertions.cs b/Src/FluentAssertions/Xml/XDocumentAssertions.cs
index dd41a59357..668e5cebfb 100644
--- a/Src/FluentAssertions/Xml/XDocumentAssertions.cs
+++ b/Src/FluentAssertions/Xml/XDocumentAssertions.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
using System.Xml;
using System.Xml.Linq;
@@ -132,7 +134,7 @@ public AndConstraint NotBeEquivalentTo(XDocument unexpected
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
- "Cannot assert the document has a root element if the expected name is *");
+ "Cannot assert the document has a root element if the expected name is .");
return HaveRoot(XNamespace.None + expected, because, becauseArgs);
}
@@ -157,7 +159,7 @@ public AndConstraint NotBeEquivalentTo(XDocument unexpected
}
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
- "Cannot assert the document has a root element if the expected name is *");
+ "Cannot assert the document has a root element if the expected name is .");
XElement root = Subject.Root;
@@ -176,7 +178,7 @@ public AndConstraint NotBeEquivalentTo(XDocument unexpected
/// child element with the specified name.
///
///
- /// The name of the expected child element of the current document's Root element.
+ /// The name of the expected child element of the current document's element.
///
///
/// A formatted phrase as is supported by explaining why the assertion
@@ -189,17 +191,43 @@ public AndConstraint NotBeEquivalentTo(XDocument unexpected
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
- "Cannot assert the document has an element if the expected name is *");
+ "Cannot assert the document has an element if the expected name is .");
return HaveElement(XNamespace.None + expected, because, becauseArgs);
}
+ ///
+ /// Asserts that the element of the current has the specified occurrence of
+ /// child elements with the specified name.
+ ///
+ ///
+ /// The name of the expected child element of the current document's element.
+ ///
+ ///
+ /// A constraint specifying the number of times the specified elements should appear.
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndWhichConstraint> HaveElement(string expected,
+ OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
+ "Cannot assert the document has an element if the expected name is .");
+
+ return HaveElement(XNamespace.None + expected, occurrenceConstraint, because, becauseArgs);
+ }
+
///
/// Asserts that the element of the current has a direct
/// child element with the specified name.
///
///
- /// The full name of the expected child element of the current document's Root element.
+ /// The full name of the expected child element of the current document's element.
///
///
/// A formatted phrase as is supported by explaining why the assertion
@@ -217,7 +245,7 @@ public AndConstraint NotBeEquivalentTo(XDocument unexpected
}
Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
- "Cannot assert the document has an element if the expected name is *");
+ "Cannot assert the document has an element if the expected name is .");
Execute.Assertion
.ForCondition(Subject.Root is not null)
@@ -237,6 +265,65 @@ public AndConstraint NotBeEquivalentTo(XDocument unexpected
return new AndWhichConstraint(this, xElement);
}
+ ///
+ /// Asserts that the element of the current has the specified occurrence of
+ /// child elements with the specified name.
+ ///
+ ///
+ /// The full name of the expected child element of the current document's element.
+ ///
+ ///
+ /// A constraint specifying the number of times the specified elements should appear.
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndWhichConstraint> HaveElement(XName expected,
+ OccurrenceConstraint occurrenceConstraint, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
+ "Cannot assert the document has an element count if the element name is .");
+
+ bool success = Execute.Assertion
+ .ForCondition(Subject is not null)
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Cannot assert the count if the document itself is .");
+
+ IEnumerable xElements = Enumerable.Empty();
+
+ if (success)
+ {
+ var root = Subject.Root;
+ success = Execute.Assertion
+ .ForCondition(root is not null)
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Expected {context:subject} to have root element containing a child {0}{reason}, but it has no root element.",
+ expected.ToString());
+
+ if (success)
+ {
+ xElements = root.Elements(expected);
+ int actual = xElements.Count();
+
+ Execute.Assertion
+ .ForConstraint(occurrenceConstraint, actual)
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ $"Expected {{context:subject}} to have a root element containing a child {{0}} " +
+ $"{{expectedOccurrence}}{{reason}}, but found it {actual.Times()}.",
+ expected.ToString());
+ }
+ }
+
+ return new AndWhichConstraint>(this, xElements);
+ }
+
///
/// Returns the type of the subject the assertion applies on.
///
diff --git a/Src/FluentAssertions/Xml/XElementAssertions.cs b/Src/FluentAssertions/Xml/XElementAssertions.cs
index c0f2a0d4cb..4e9b6e2876 100644
--- a/Src/FluentAssertions/Xml/XElementAssertions.cs
+++ b/Src/FluentAssertions/Xml/XElementAssertions.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
using System.Xml;
using System.Xml.Linq;
using FluentAssertions.Common;
@@ -288,6 +290,82 @@ public AndConstraint HaveValue(string expected, string becau
return new AndWhichConstraint(this, xElement);
}
+ ///
+ /// Asserts that the of the current has the specified occurrence of
+ /// child elements with the specified name.
+ ///
+ ///
+ /// The full name of the expected child element of the current element's .
+ ///
+ ///
+ /// A constraint specifying the number of times the specified elements should appear.
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndWhichConstraint> HaveElement(XName expected,
+ OccurrenceConstraint occurrenceConstraint, string because = "",
+ params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
+ "Cannot assert the element has an element count if the element name is .");
+
+ bool success = Execute.Assertion
+ .ForCondition(Subject is not null)
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ "Expected {context:subject} to have an element with count of {0}{reason}, but the element itself is .",
+ expected.ToString());
+
+ IEnumerable xElements = Enumerable.Empty();
+
+ if (success)
+ {
+ xElements = Subject.Elements(expected);
+ int actual = xElements.Count();
+
+ Execute.Assertion
+ .ForConstraint(occurrenceConstraint, actual)
+ .BecauseOf(because, becauseArgs)
+ .FailWith(
+ $"Expected {{context:subject}} to have an element {{0}} {{expectedOccurrence}}" +
+ $"{{reason}}, but found it {actual.Times()}.",
+ expected.ToString());
+ }
+
+ return new AndWhichConstraint>(this, xElements);
+ }
+
+ ///
+ /// Asserts that the of the current has the specified occurrence of
+ /// child elements with the specified name.
+ ///
+ ///
+ /// The name of the expected child element of the current element's .
+ ///
+ ///
+ /// A constraint specifying the number of times the specified elements should appear.
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public AndWhichConstraint> HaveElement(string expected,
+ OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs)
+ {
+ Guard.ThrowIfArgumentIsNull(expected, nameof(expected),
+ "Cannot assert the element has an element if the expected name is .");
+
+ return HaveElement(XNamespace.None + expected, occurrenceConstraint, because, becauseArgs);
+ }
+
///
/// Returns the type of the subject the assertion applies on.
///
diff --git a/Tests/Approval.Tests/Approval.Tests.csproj b/Tests/Approval.Tests/Approval.Tests.csproj
index 79005a9c42..650f89992a 100644
--- a/Tests/Approval.Tests/Approval.Tests.csproj
+++ b/Tests/Approval.Tests/Approval.Tests.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
index 6ff5858bab..8a2d1f8e14 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
@@ -58,6 +58,9 @@ namespace FluentAssertions
public static FluentAssertions.Specialized.ActionAssertions Should(this System.Action action) { }
public static FluentAssertions.Collections.StringCollectionAssertions Should(this System.Collections.Generic.IEnumerable @this) { }
public static FluentAssertions.Data.DataColumnAssertions Should(this System.Data.DataColumn actualValue) { }
+ public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Data.DataColumnCollection actualValue) { }
+ public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Data.DataRowCollection actualValue) { }
+ public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Data.DataTableCollection actualValue) { }
public static FluentAssertions.Primitives.DateTimeAssertions Should(this System.DateTime actualValue) { }
public static FluentAssertions.Primitives.NullableDateTimeAssertions Should(this System.DateTime? actualValue) { }
public static FluentAssertions.Primitives.DateTimeOffsetAssertions Should(this System.DateTimeOffset actualValue) { }
@@ -118,6 +121,14 @@ namespace FluentAssertions
where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { }
[System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
"ly following \'And\'", true)]
+ public static void Should(this FluentAssertions.Primitives.DateTimeOffsetRangeAssertions _)
+ where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { }
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
+ public static void Should(this FluentAssertions.Primitives.DateTimeRangeAssertions _)
+ where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { }
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.GuidAssertions _)
where TAssertions : FluentAssertions.Primitives.GuidAssertions { }
[System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
@@ -187,11 +198,25 @@ namespace FluentAssertions
{
public CustomAssertionAttribute() { }
}
+ public static class DataColumnCollectionAssertionExtensions
+ {
+ public static FluentAssertions.AndConstraint> BeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection expected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotBeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection unexpected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ }
public static class DataRowAssertionExtensions
{
public static FluentAssertions.Data.DataRowAssertions Should(this TDataRow actualValue)
where TDataRow : System.Data.DataRow { }
}
+ public static class DataRowCollectionAssertionExtensions
+ {
+ public static FluentAssertions.AndConstraint> BeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection expected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotBeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection unexpected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ }
public static class DataSetAssertionExtensions
{
public static FluentAssertions.Data.DataSetAssertions Should(this TDataSet actualValue)
@@ -202,6 +227,15 @@ namespace FluentAssertions
public static FluentAssertions.Data.DataTableAssertions Should(this TDataTable actualValue)
where TDataTable : System.Data.DataTable { }
}
+ public static class DataTableCollectionAssertionExtensions
+ {
+ public static FluentAssertions.AndConstraint> BeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection expected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataSet otherDataSet, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotBeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection unexpected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataSet otherDataSet, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ }
public static class EnumAssertionsExtensions
{
public static FluentAssertions.Primitives.EnumAssertions Should(this TEnum @enum)
@@ -757,6 +791,7 @@ namespace FluentAssertions.Equivalency
public EquivalencyAssertionOptions(FluentAssertions.Equivalency.IEquivalencyAssertionOptions defaults) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions> AsCollection() { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions Excluding(System.Linq.Expressions.Expression> expression) { }
+ public FluentAssertions.Equivalency.NestedExclusionOptionBuilder For(System.Linq.Expressions.Expression>> expression) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions Including(System.Linq.Expressions.Expression> expression) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions WithMapping(string expectationMemberPath, string subjectMemberPath) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions WithMapping(System.Linq.Expressions.Expression> expectationMemberPath, System.Linq.Expressions.Expression> subjectMemberPath) { }
@@ -803,6 +838,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; set; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
+ public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
@@ -823,6 +859,8 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.ConversionSelector ConversionSelector { get; }
FluentAssertions.Equivalency.CyclicReferenceHandling CyclicReferenceHandling { get; }
FluentAssertions.Equivalency.EnumEquivalencyHandling EnumEquivalencyHandling { get; }
+ bool ExcludeNonBrowsableOnExpectation { get; }
+ bool IgnoreNonBrowsableOnSubject { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedFields { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedProperties { get; }
bool IsRecursive { get; }
@@ -858,6 +896,7 @@ namespace FluentAssertions.Equivalency
{
System.Type DeclaringType { get; }
FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
+ bool IsBrowsable { get; }
System.Type ReflectedType { get; }
FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
object GetValue(object obj);
@@ -921,6 +960,11 @@ namespace FluentAssertions.Equivalency
Internal = 1,
Public = 2,
}
+ public class NestedExclusionOptionBuilder
+ {
+ public FluentAssertions.Equivalency.EquivalencyAssertionOptions Exclude(System.Linq.Expressions.Expression> expression) { }
+ public FluentAssertions.Equivalency.NestedExclusionOptionBuilder For(System.Linq.Expressions.Expression>> expression) { }
+ }
public class Node : FluentAssertions.Equivalency.INode
{
public Node() { }
@@ -961,6 +1005,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
+ public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
@@ -989,8 +1034,10 @@ namespace FluentAssertions.Equivalency
public TSelf ExcludingFields() { }
public TSelf ExcludingMissingMembers() { }
public TSelf ExcludingNestedObjects() { }
+ public TSelf ExcludingNonBrowsableMembers() { }
public TSelf ExcludingProperties() { }
public TSelf IgnoringCyclicReferences() { }
+ public TSelf IgnoringNonBrowsableMembersOnSubject() { }
public TSelf Including(System.Linq.Expressions.Expression> predicate) { }
public TSelf IncludingAllDeclaredProperties() { }
public TSelf IncludingAllRuntimeProperties() { }
@@ -1887,6 +1934,7 @@ namespace FluentAssertions.Primitives
public TEnum? Subject { get; }
public FluentAssertions.AndConstraint Be(TEnum expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint Be(TEnum? expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint BeDefined(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint BeOneOf(params TEnum[] validValues) { }
public FluentAssertions.AndConstraint BeOneOf(System.Collections.Generic.IEnumerable validValues, string because = "", params object[] becauseArgs) { }
public override bool Equals(object obj) { }
@@ -1899,6 +1947,7 @@ namespace FluentAssertions.Primitives
public FluentAssertions.AndConstraint Match(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBe(TEnum unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBe(TEnum? unexpected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndConstraint NotBeDefined(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotHaveFlag(TEnum unexpectedFlag, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotHaveSameNameAs(T unexpected, string because = "", params object[] becauseArgs)
where T : struct, System.Enum { }
@@ -2636,6 +2685,8 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndWhichConstraint> HaveElement(string expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndWhichConstraint> HaveElement(System.Xml.Linq.XName expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint HaveRoot(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint HaveRoot(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBe(System.Xml.Linq.XDocument unexpected, string because = "", params object[] becauseArgs) { }
@@ -2651,6 +2702,8 @@ namespace FluentAssertions.Xml
public FluentAssertions.AndConstraint HaveAttribute(System.Xml.Linq.XName expectedName, string expectedValue, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint HaveElement(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint HaveElement(System.Xml.Linq.XName expected, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndWhichConstraint> HaveElement(string expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
+ public FluentAssertions.AndWhichConstraint> HaveElement(System.Xml.Linq.XName expected, FluentAssertions.OccurrenceConstraint occurrenceConstraint, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint HaveValue(string expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBe(System.Xml.Linq.XElement unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeEquivalentTo(System.Xml.Linq.XElement unexpected, string because = "", params object[] becauseArgs) { }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
index cf6defd2c8..1b6faed215 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
@@ -58,6 +58,9 @@ namespace FluentAssertions
public static FluentAssertions.Specialized.ActionAssertions Should(this System.Action action) { }
public static FluentAssertions.Collections.StringCollectionAssertions Should(this System.Collections.Generic.IEnumerable @this) { }
public static FluentAssertions.Data.DataColumnAssertions Should(this System.Data.DataColumn actualValue) { }
+ public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Data.DataColumnCollection actualValue) { }
+ public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Data.DataRowCollection actualValue) { }
+ public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Data.DataTableCollection actualValue) { }
public static FluentAssertions.Primitives.DateOnlyAssertions Should(this System.DateOnly actualValue) { }
public static FluentAssertions.Primitives.NullableDateOnlyAssertions Should(this System.DateOnly? actualValue) { }
public static FluentAssertions.Primitives.DateTimeAssertions Should(this System.DateTime actualValue) { }
@@ -126,6 +129,14 @@ namespace FluentAssertions
where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { }
[System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
"ly following \'And\'", true)]
+ public static void Should(this FluentAssertions.Primitives.DateTimeOffsetRangeAssertions _)
+ where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { }
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
+ public static void Should(this FluentAssertions.Primitives.DateTimeRangeAssertions _)
+ where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { }
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.GuidAssertions _)
where TAssertions : FluentAssertions.Primitives.GuidAssertions { }
[System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
@@ -199,11 +210,25 @@ namespace FluentAssertions
{
public CustomAssertionAttribute() { }
}
+ public static class DataColumnCollectionAssertionExtensions
+ {
+ public static FluentAssertions.AndConstraint> BeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection expected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotBeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection unexpected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataColumnCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ }
public static class DataRowAssertionExtensions
{
public static FluentAssertions.Data.DataRowAssertions Should(this TDataRow actualValue)
where TDataRow : System.Data.DataRow { }
}
+ public static class DataRowCollectionAssertionExtensions
+ {
+ public static FluentAssertions.AndConstraint> BeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection expected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotBeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection unexpected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataRowCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ }
public static class DataSetAssertionExtensions
{
public static FluentAssertions.Data.DataSetAssertions Should(this TDataSet actualValue)
@@ -214,6 +239,15 @@ namespace FluentAssertions
public static FluentAssertions.Data.DataTableAssertions Should(this TDataTable actualValue)
where TDataTable : System.Data.DataTable { }
}
+ public static class DataTableCollectionAssertionExtensions
+ {
+ public static FluentAssertions.AndConstraint> BeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection expected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataSet otherDataSet, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> HaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotBeSameAs(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection unexpected, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataSet otherDataSet, string because = "", params object[] becauseArgs) { }
+ public static FluentAssertions.AndConstraint> NotHaveSameCount(this FluentAssertions.Collections.GenericCollectionAssertions assertion, System.Data.DataTableCollection otherCollection, string because = "", params object[] becauseArgs) { }
+ }
public static class EnumAssertionsExtensions
{
public static FluentAssertions.Primitives.EnumAssertions Should(this TEnum @enum)
@@ -769,6 +803,7 @@ namespace FluentAssertions.Equivalency
public EquivalencyAssertionOptions(FluentAssertions.Equivalency.IEquivalencyAssertionOptions defaults) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions> AsCollection() { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions Excluding(System.Linq.Expressions.Expression> expression) { }
+ public FluentAssertions.Equivalency.NestedExclusionOptionBuilder For(System.Linq.Expressions.Expression>> expression) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions Including(System.Linq.Expressions.Expression> expression) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions WithMapping(string expectationMemberPath, string subjectMemberPath) { }
public FluentAssertions.Equivalency.EquivalencyAssertionOptions WithMapping(System.Linq.Expressions.Expression> expectationMemberPath, System.Linq.Expressions.Expression> subjectMemberPath) { }
@@ -815,6 +850,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; set; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
+ public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
@@ -835,6 +871,8 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.ConversionSelector ConversionSelector { get; }
FluentAssertions.Equivalency.CyclicReferenceHandling CyclicReferenceHandling { get; }
FluentAssertions.Equivalency.EnumEquivalencyHandling EnumEquivalencyHandling { get; }
+ bool ExcludeNonBrowsableOnExpectation { get; }
+ bool IgnoreNonBrowsableOnSubject { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedFields { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedProperties { get; }
bool IsRecursive { get; }
@@ -870,6 +908,7 @@ namespace FluentAssertions.Equivalency
{
System.Type DeclaringType { get; }
FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
+ bool IsBrowsable { get; }
System.Type ReflectedType { get; }
FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
object GetValue(object obj);
@@ -933,6 +972,11 @@ namespace FluentAssertions.Equivalency
Internal = 1,
Public = 2,
}
+ public class NestedExclusionOptionBuilder
+ {
+ public FluentAssertions.Equivalency.EquivalencyAssertionOptions Exclude(System.Linq.Expressions.Expression> expression) { }
+ public FluentAssertions.Equivalency.NestedExclusionOptionBuilder For(System.Linq.Expressions.Expression>> expression) { }
+ }
public class Node : FluentAssertions.Equivalency.INode
{
public Node() { }
@@ -973,6 +1017,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
+ public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
@@ -1001,8 +1046,10 @@ namespace FluentAssertions.Equivalency
public TSelf ExcludingFields() { }
public TSelf ExcludingMissingMembers() { }
public TSelf ExcludingNestedObjects() { }
+ public TSelf ExcludingNonBrowsableMembers() { }
public TSelf ExcludingProperties() { }
public TSelf IgnoringCyclicReferences() { }
+ public TSelf IgnoringNonBrowsableMembersOnSubject() { }
public TSelf Including(System.Linq.Expressions.Expression> predicate) { }
public TSelf IncludingAllDeclaredProperties() { }
public TSelf IncludingAllRuntimeProperties() { }
@@ -1944,6 +1991,7 @@ namespace FluentAssertions.Primitives
public TEnum? Subject { get; }
public FluentAssertions.AndConstraint Be(TEnum expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint