diff --git a/Build/_build.csproj b/Build/_build.csproj
index 1c2cc869f9..31894d6f91 100644
--- a/Build/_build.csproj
+++ b/Build/_build.csproj
@@ -9,19 +9,10 @@
9.0.4
1
-
- OS_WINDOWS
-
-
- OS_LINUX
-
-
- OS_MAC
-
-
-
+
+
diff --git a/Directory.Build.props b/Directory.Build.props
index 9d396e38fb..2f23cead51 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -30,11 +30,11 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/LICENSE.md b/LICENSE
similarity index 100%
rename from LICENSE.md
rename to LICENSE
diff --git a/README.md b/README.md
index 0b0c3509a2..48a219198c 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Extension methods to fluently assert the outcome of .NET tests
Fluent Assertions provides a comprehensive set of extension methods that enable developers to express the expected outcomes of TDD (Test-Driven Development) and BDD (Behavior-Driven Development) unit tests in a natural, readable style. It is compatible with .NET Standard 2.0+, .NET Framework 4.7+, and .NET 6+.
Versions 8 and beyond are free for open-source projects and non-commercial use, but commercial use requires [a paid license](https://xceed.com/products/unit-testing/fluent-assertions/).
-Check out the [license page](LICENSE.md) for more information. Version 7
+Check out the [license page](LICENSE) for more information. Version 7
will remain fully open-source indefinitely and receive bugfixes and other important corrections.
diff --git a/Src/FluentAssertions/AndWhichConstraint.cs b/Src/FluentAssertions/AndWhichConstraint.cs
index 49df87657e..37d6183a47 100644
--- a/Src/FluentAssertions/AndWhichConstraint.cs
+++ b/Src/FluentAssertions/AndWhichConstraint.cs
@@ -47,12 +47,29 @@ public AndWhichConstraint(TParent parent, TSubject subject, AssertionChain asser
///
/// If contains more than one object, a clear exception is thrown.
///
+ // REFACTOR: In a next major version, we need to remove this overload and make the AssertionChain required
public AndWhichConstraint(TParent parent, IEnumerable subjects)
: base(parent)
{
getSubject = new Lazy(() => Single(subjects));
}
+ ///
+ /// Creates an object that allows continuing an assertion executed through and
+ /// which resulted in a potential collection of objects through on an
+ /// existing .
+ ///
+ ///
+ /// If contains more than one object, a clear exception is thrown.
+ ///
+ public AndWhichConstraint(TParent parent, IEnumerable subjects, AssertionChain assertionChain)
+ : base(parent)
+ {
+ getSubject = new Lazy(() => Single(subjects));
+
+ this.assertionChain = assertionChain;
+ }
+
///
/// Creates an object that allows continuing an assertion executed through and
/// which resulted in a potential collection of objects through on an
@@ -108,6 +125,12 @@ public TSubject Which
{
assertionChain.WithCallerPostfix(pathPostfix).ReuseOnce();
}
+ else
+ {
+ // Make sure the caller identification restarts with the code following the Which property.
+ assertionChain?.AdvanceToNextIdentifier();
+ assertionChain?.ReuseOnce();
+ }
return getSubject.Value;
}
diff --git a/Src/FluentAssertions/AssertionExtensions.cs b/Src/FluentAssertions/AssertionExtensions.cs
index c68660cb1d..56b207a30d 100644
--- a/Src/FluentAssertions/AssertionExtensions.cs
+++ b/Src/FluentAssertions/AssertionExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
@@ -301,7 +301,7 @@ public static BooleanAssertions Should(this bool actualValue)
/// current nullable .
///
[Pure]
- public static NullableBooleanAssertions Should(this bool? actualValue)
+ public static NullableBooleanAssertions Should([NotNull] this bool? actualValue)
{
return new NullableBooleanAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -321,7 +321,7 @@ public static GuidAssertions Should(this Guid actualValue)
/// current nullable .
///
[Pure]
- public static NullableGuidAssertions Should(this Guid? actualValue)
+ public static NullableGuidAssertions Should([NotNull] this Guid? actualValue)
{
return new NullableGuidAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -406,7 +406,7 @@ public static DateTimeOffsetAssertions Should(this DateTimeOffset actualValue)
/// current nullable .
///
[Pure]
- public static NullableDateTimeAssertions Should(this DateTime? actualValue)
+ public static NullableDateTimeAssertions Should([NotNull] this DateTime? actualValue)
{
return new NullableDateTimeAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -416,7 +416,7 @@ public static NullableDateTimeAssertions Should(this DateTime? actualValue)
/// current nullable .
///
[Pure]
- public static NullableDateTimeOffsetAssertions Should(this DateTimeOffset? actualValue)
+ public static NullableDateTimeOffsetAssertions Should([NotNull] this DateTimeOffset? actualValue)
{
return new NullableDateTimeOffsetAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -437,7 +437,7 @@ public static DateOnlyAssertions Should(this DateOnly actualValue)
/// current nullable .
///
[Pure]
- public static NullableDateOnlyAssertions Should(this DateOnly? actualValue)
+ public static NullableDateOnlyAssertions Should([NotNull] this DateOnly? actualValue)
{
return new NullableDateOnlyAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -457,7 +457,7 @@ public static TimeOnlyAssertions Should(this TimeOnly actualValue)
/// current nullable .
///
[Pure]
- public static NullableTimeOnlyAssertions Should(this TimeOnly? actualValue)
+ public static NullableTimeOnlyAssertions Should([NotNull] this TimeOnly? actualValue)
{
return new NullableTimeOnlyAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -489,7 +489,7 @@ public static NumericAssertions Should(this int actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this int? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this int? actualValue)
{
return new NullableInt32Assertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -509,7 +509,7 @@ public static NumericAssertions Should(this uint actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this uint? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this uint? actualValue)
{
return new NullableUInt32Assertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -529,7 +529,7 @@ public static NumericAssertions Should(this decimal actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this decimal? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this decimal? actualValue)
{
return new NullableDecimalAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -549,7 +549,7 @@ public static NumericAssertions Should(this byte actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this byte? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this byte? actualValue)
{
return new NullableByteAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -569,7 +569,7 @@ public static NumericAssertions Should(this sbyte actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this sbyte? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this sbyte? actualValue)
{
return new NullableSByteAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -589,7 +589,7 @@ public static NumericAssertions Should(this short actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this short? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this short? actualValue)
{
return new NullableInt16Assertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -609,7 +609,7 @@ public static NumericAssertions Should(this ushort actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this ushort? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this ushort? actualValue)
{
return new NullableUInt16Assertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -629,7 +629,7 @@ public static NumericAssertions Should(this long actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this long? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this long? actualValue)
{
return new NullableInt64Assertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -649,7 +649,7 @@ public static NumericAssertions Should(this ulong actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this ulong? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this ulong? actualValue)
{
return new NullableUInt64Assertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -669,7 +669,7 @@ public static NumericAssertions Should(this float actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this float? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this float? actualValue)
{
return new NullableSingleAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -689,7 +689,7 @@ public static NumericAssertions Should(this double actualValue)
/// current nullable .
///
[Pure]
- public static NullableNumericAssertions Should(this double? actualValue)
+ public static NullableNumericAssertions Should([NotNull] this double? actualValue)
{
return new NullableDoubleAssertions(actualValue, AssertionChain.GetOrCreate());
}
@@ -719,7 +719,7 @@ public static SimpleTimeSpanAssertions Should(this TimeSpan actualValue)
/// current nullable .
///
[Pure]
- public static NullableSimpleTimeSpanAssertions Should(this TimeSpan? actualValue)
+ public static NullableSimpleTimeSpanAssertions Should([NotNull] this TimeSpan? actualValue)
{
return new NullableSimpleTimeSpanAssertions(actualValue, AssertionChain.GetOrCreate());
}
diff --git a/Src/FluentAssertions/CallerIdentification/CallerStatementBuilder.cs b/Src/FluentAssertions/CallerIdentification/CallerStatementBuilder.cs
deleted file mode 100644
index 7f922f2009..0000000000
--- a/Src/FluentAssertions/CallerIdentification/CallerStatementBuilder.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace FluentAssertions.CallerIdentification;
-
-internal class CallerStatementBuilder
-{
- private readonly StringBuilder statement;
- private readonly List priorityOrderedParsingStrategies;
- private ParsingState parsingState = ParsingState.InProgress;
-
- internal CallerStatementBuilder()
- {
- statement = new StringBuilder();
-
- priorityOrderedParsingStrategies =
- [
- new QuotesParsingStrategy(),
- new MultiLineCommentParsingStrategy(),
- new SingleLineCommentParsingStrategy(),
- new SemicolonParsingStrategy(),
- new ShouldCallParsingStrategy(),
- new AwaitParsingStrategy(),
- new AddNonEmptySymbolParsingStrategy()
- ];
- }
-
- internal void Append(string symbols)
- {
- using var symbolEnumerator = symbols.GetEnumerator();
-
- while (symbolEnumerator.MoveNext() && parsingState != ParsingState.Done)
- {
- var hasParsingStrategyWaitingForEndContext = priorityOrderedParsingStrategies
- .Exists(s => s.IsWaitingForContextEnd());
-
- parsingState = ParsingState.InProgress;
-
- foreach (var parsingStrategy in
- priorityOrderedParsingStrategies
- .SkipWhile(parsingStrategy =>
- hasParsingStrategyWaitingForEndContext
- && !parsingStrategy.IsWaitingForContextEnd()))
- {
- parsingState = parsingStrategy.Parse(symbolEnumerator.Current, statement);
-
- if (parsingState != ParsingState.InProgress)
- {
- break;
- }
- }
- }
-
- if (IsDone())
- {
- return;
- }
-
- priorityOrderedParsingStrategies
- .ForEach(strategy => strategy.NotifyEndOfLineReached());
- }
-
- internal bool IsDone() => parsingState == ParsingState.Done;
-
- public override string ToString() => statement.ToString();
-}
diff --git a/Src/FluentAssertions/CallerIdentification/ParsingState.cs b/Src/FluentAssertions/CallerIdentification/ParsingState.cs
index 6548ae2627..18ac092068 100644
--- a/Src/FluentAssertions/CallerIdentification/ParsingState.cs
+++ b/Src/FluentAssertions/CallerIdentification/ParsingState.cs
@@ -2,7 +2,24 @@
internal enum ParsingState
{
+ ///
+ /// Is returned by a parser when the next one can take a look at the current symbol
+ ///
InProgress,
+
+ ///
+ /// Is returned by a parser when it decides a symbol has been processed enough and no
+ /// other parsers need to look at the current symbol anymore.
+ ///
GoToNextSymbol,
- Done
+
+ ///
+ /// Is returned by a parser if it has found a candidate identifier.
+ ///
+ CandidateFound,
+
+ ///
+ /// Is returned by a parser to indicate that the parsing has been fully completed.
+ ///
+ Completed
}
diff --git a/Src/FluentAssertions/CallerIdentification/SemicolonParsingStrategy.cs b/Src/FluentAssertions/CallerIdentification/SemicolonParsingStrategy.cs
index 17a6601cc0..c5480ef6b5 100644
--- a/Src/FluentAssertions/CallerIdentification/SemicolonParsingStrategy.cs
+++ b/Src/FluentAssertions/CallerIdentification/SemicolonParsingStrategy.cs
@@ -9,7 +9,7 @@ public ParsingState Parse(char symbol, StringBuilder statement)
if (symbol is ';')
{
statement.Clear();
- return ParsingState.Done;
+ return ParsingState.Completed;
}
return ParsingState.InProgress;
diff --git a/Src/FluentAssertions/CallerIdentification/ShouldCallParsingStrategy.cs b/Src/FluentAssertions/CallerIdentification/ShouldCallParsingStrategy.cs
index 4ff4555312..e7a82a85dd 100644
--- a/Src/FluentAssertions/CallerIdentification/ShouldCallParsingStrategy.cs
+++ b/Src/FluentAssertions/CallerIdentification/ShouldCallParsingStrategy.cs
@@ -2,37 +2,49 @@
namespace FluentAssertions.CallerIdentification;
+///
+/// Tries to determine if the statement ends with ".Should(" or ".Should.", and assumes
+/// the preceding identifier is the actual identifier.
+///
internal class ShouldCallParsingStrategy : IParsingStrategy
{
- private const string ShouldCall = ".Should";
+ private const string ExpectedPhrase = ".Should";
public ParsingState Parse(char symbol, StringBuilder statement)
{
- if (statement.Length >= ShouldCall.Length + 1)
+ if (IsLongEnough(statement) && EndsWithExpectedPhrase(statement) && EndsWithInvocation(statement))
{
- var leftIndex = statement.Length - 2;
- var rightIndex = ShouldCall.Length - 1;
+ // Remove the ".Should." or ".Should(" part from the statement, so we keep the actual identifier
+ statement.Remove(statement.Length - (ExpectedPhrase.Length + 1), ExpectedPhrase.Length + 1);
+ return ParsingState.CandidateFound;
+ }
- for (var i = 0; i < ShouldCall.Length; i++)
- {
- if (statement[leftIndex - i] != ShouldCall[rightIndex - i])
- {
- return ParsingState.InProgress;
- }
- }
+ return ParsingState.InProgress;
+ }
+
+ private static bool IsLongEnough(StringBuilder statement) => statement.Length >= ExpectedPhrase.Length + 1;
- if (statement[^1] is not ('(' or '.'))
+ private static bool EndsWithExpectedPhrase(StringBuilder statement)
+ {
+ // Start from the index on the character just before the last ( or .
+ var rightIndexInStatement = statement.Length - 2;
+
+ var rightIndexInExpectedPhrase = ExpectedPhrase.Length - 1;
+
+ // Do a reverse comparison to see if the statement ends with ".Should"
+ for (var i = 0; i < ExpectedPhrase.Length; i++)
+ {
+ if (statement[rightIndexInStatement - i] != ExpectedPhrase[rightIndexInExpectedPhrase - i])
{
- return ParsingState.InProgress;
+ return false;
}
-
- statement.Remove(statement.Length - (ShouldCall.Length + 1), ShouldCall.Length + 1);
- return ParsingState.Done;
}
- return ParsingState.InProgress;
+ return true;
}
+ private static bool EndsWithInvocation(StringBuilder statement) => statement[^1] is '(' or '.';
+
public bool IsWaitingForContextEnd()
{
return false;
diff --git a/Src/FluentAssertions/CallerIdentification/StatementParser.cs b/Src/FluentAssertions/CallerIdentification/StatementParser.cs
new file mode 100644
index 0000000000..2b9497653d
--- /dev/null
+++ b/Src/FluentAssertions/CallerIdentification/StatementParser.cs
@@ -0,0 +1,77 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace FluentAssertions.CallerIdentification;
+
+internal class StatementParser
+{
+ private readonly StringBuilder statement;
+ private readonly List parsingStrategies;
+ private readonly List candidates = new();
+ private ParsingState state = ParsingState.InProgress;
+
+ internal StatementParser()
+ {
+ statement = new StringBuilder();
+
+ parsingStrategies =
+ [
+ new QuotesParsingStrategy(),
+ new MultiLineCommentParsingStrategy(),
+ new SingleLineCommentParsingStrategy(),
+ new SemicolonParsingStrategy(),
+ new ShouldCallParsingStrategy(),
+ new WhichParsingStrategy(),
+ new AwaitParsingStrategy(),
+ new AddNonEmptySymbolParsingStrategy()
+ ];
+ }
+
+ ///
+ /// Gets the identifiers preceding a Should or Which clause as extracted from lines of code passed to
+ ///
+ public string[] Identifiers => candidates.ToArray();
+
+ public void Append(string symbols)
+ {
+ using var symbolEnumerator = symbols.GetEnumerator();
+
+ while (symbolEnumerator.MoveNext() && state != ParsingState.Completed)
+ {
+ // The logic ensures that parsing does not continue with irrelevant strategies when a strategy is currently in the middle
+ // of a multi-symbol context (e.g., waiting for "_/" to match the beginning "/_"). In such cases, it skips over strategies
+ // that aren't waiting for the "end of context" while allowing the active (waiting) strategy to resume processing.
+ IEnumerable activeParsers = parsingStrategies;
+ if (parsingStrategies.Exists(s => s.IsWaitingForContextEnd()))
+ {
+ activeParsers = parsingStrategies.SkipWhile(parsingStrategy => !parsingStrategy.IsWaitingForContextEnd());
+ }
+
+ state = ParsingState.InProgress;
+
+ foreach (IParsingStrategy parser in activeParsers)
+ {
+ state = parser.Parse(symbolEnumerator.Current, statement);
+ if (state == ParsingState.CandidateFound)
+ {
+ candidates.Add(statement.ToString());
+ }
+
+ if (state != ParsingState.InProgress)
+ {
+ break;
+ }
+ }
+ }
+
+ if (IsDone())
+ {
+ return;
+ }
+
+ parsingStrategies.ForEach(strategy => strategy.NotifyEndOfLineReached());
+ }
+
+ public bool IsDone() => state == ParsingState.Completed;
+}
diff --git a/Src/FluentAssertions/CallerIdentification/WhichParsingStrategy.cs b/Src/FluentAssertions/CallerIdentification/WhichParsingStrategy.cs
new file mode 100644
index 0000000000..872deb78e5
--- /dev/null
+++ b/Src/FluentAssertions/CallerIdentification/WhichParsingStrategy.cs
@@ -0,0 +1,54 @@
+using System.Text;
+
+namespace FluentAssertions.CallerIdentification;
+
+///
+/// Tries to find the .Which. construct and assumes everything preceding it has become irrelevant
+/// for the chained assertion.
+///
+internal class WhichParsingStrategy : IParsingStrategy
+{
+ private const string ExpectedPhrase = ".Which.";
+
+ public ParsingState Parse(char symbol, StringBuilder statement)
+ {
+ if (IsLongEnough(statement) && EndsWithExpectedPhrase(statement))
+ {
+ // Remove everything we collected up to know and assume everything following the
+ // .Which. property is a new assertion
+ statement.Clear();
+ }
+
+ return ParsingState.InProgress;
+ }
+
+ private static bool IsLongEnough(StringBuilder statement) => statement.Length >= ExpectedPhrase.Length;
+
+ private static bool EndsWithExpectedPhrase(StringBuilder statement)
+ {
+ // Start from the index of the last character
+ var rightIndexInStatement = statement.Length - 1;
+
+ var rightIndexInExpectedPhrase = ExpectedPhrase.Length - 1;
+
+ // Do a reverse comparison to see if the statement ends with ".Which."
+ for (var i = 0; i < ExpectedPhrase.Length; i++)
+ {
+ if (statement[rightIndexInStatement - i] != ExpectedPhrase[rightIndexInExpectedPhrase - i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public bool IsWaitingForContextEnd()
+ {
+ return false;
+ }
+
+ public void NotifyEndOfLineReached()
+ {
+ }
+}
diff --git a/Src/FluentAssertions/CallerIdentifier.cs b/Src/FluentAssertions/CallerIdentifier.cs
index fc815fc972..358931da78 100644
--- a/Src/FluentAssertions/CallerIdentifier.cs
+++ b/Src/FluentAssertions/CallerIdentifier.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
@@ -19,9 +20,28 @@ public static class CallerIdentifier
{
public static Action Logger { get; set; } = _ => { };
+ ///
+ /// Gets the identifier that precedes the first Should call in the chain.
+ ///
public static string DetermineCallerIdentity()
{
- string caller = null;
+ return DetermineCallerIdentities().FirstOrDefault();
+ }
+
+ ///
+ /// Gets all identifiers of all assertions in order of appearance.
+ ///
+ ///
+ /// For example, given the following code
+ /// collection.Should().ContainSingle()
+ /// .Which.Parameters.Should().ContainSingle()
+ /// .Which.Should().Be(3)
+ ///
+ /// will return collection, Parameters and an empty string.
+ ///
+ public static string[] DetermineCallerIdentities()
+ {
+ string[] callers = [];
try
{
@@ -57,7 +77,7 @@ public static string DetermineCallerIdentity()
&& !IsCustomAssertion(frame)
&& !IsCurrentAssembly(frame))
{
- caller = ExtractVariableNameFrom(frame);
+ callers = ExtractCallersFrom(frame).ToArray();
break;
}
}
@@ -68,7 +88,7 @@ public static string DetermineCallerIdentity()
Logger(e.ToString());
}
- return caller;
+ return callers;
}
private sealed class StackFrameReference : IDisposable
@@ -169,26 +189,23 @@ private static bool IsCompilerServices(StackFrame frame)
return frame.GetMethod()?.DeclaringType?.Namespace is "System.Runtime.CompilerServices";
}
- private static string ExtractVariableNameFrom(StackFrame frame)
+ private static IEnumerable ExtractCallersFrom(StackFrame frame)
{
- string caller = null;
- string statement = GetSourceCodeStatementFrom(frame);
+ string[] identifiers = GetCallerIdentifiersFrom(frame);
- if (!string.IsNullOrEmpty(statement))
+ foreach (string identifier in identifiers)
{
- Logger(statement);
+ Logger(identifier);
- if (!IsBooleanLiteral(statement) && !IsNumeric(statement) && !IsStringLiteral(statement) &&
- !StartsWithNewKeyword(statement))
+ if (!IsBooleanLiteral(identifier) && !IsNumeric(identifier) && !IsStringLiteral(identifier) &&
+ !StartsWithNewKeyword(identifier))
{
- caller = statement;
+ yield return identifier;
}
}
-
- return caller;
}
- private static string GetSourceCodeStatementFrom(StackFrame frame)
+ private static string[] GetCallerIdentifiersFrom(StackFrame frame)
{
string fileName = frame.GetFileName();
int expectedLineNumber = frame.GetFileLineNumber();
@@ -209,19 +226,18 @@ private static string GetSourceCodeStatementFrom(StackFrame frame)
currentLine++;
}
- return currentLine == expectedLineNumber
- && line != null
- ? GetSourceCodeStatementFrom(frame, reader, line)
+ return currentLine == expectedLineNumber && line != null
+ ? GetCallerIdentifiersFrom(frame, reader, line)
: null;
}
catch
{
// We don't care. Just assume the symbol file is not available or unreadable
- return null;
+ return [];
}
}
- private static string GetSourceCodeStatementFrom(StackFrame frame, StreamReader reader, string line)
+ private static string[] GetCallerIdentifiersFrom(StackFrame frame, StreamReader reader, string line)
{
int column = frame.GetFileColumnNumber();
@@ -230,15 +246,15 @@ private static string GetSourceCodeStatementFrom(StackFrame frame, StreamReader
line = line.Substring(Math.Min(column - 1, line.Length - 1));
}
- var sb = new CallerStatementBuilder();
+ var parser = new StatementParser();
do
{
- sb.Append(line);
+ parser.Append(line);
}
- while (!sb.IsDone() && (line = reader.ReadLine()) != null);
+ while (!parser.IsDone() && (line = reader.ReadLine()) != null);
- return sb.ToString();
+ return parser.Identifiers;
}
private static bool StartsWithNewKeyword(string candidate)
diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
index ebc69508e9..7984f73a72 100644
--- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
+++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
@@ -725,7 +725,7 @@ public AndWhichConstraint Contain(T expected,
matches = collection.Where(item => EqualityComparer.Default.Equals(item, expected));
}
- return new AndWhichConstraint((TAssertions)this, matches);
+ return new AndWhichConstraint((TAssertions)this, matches, assertionChain);
}
///
@@ -775,9 +775,7 @@ public AndWhichConstraint Contain(Expression> pred
matches = Subject.Where(func);
}
- assertionChain.WithCallerPostfix($"[{firstMatchingIndex}]").ReuseOnce();
-
- return new AndWhichConstraint((TAssertions)this, matches);
+ return new AndWhichConstraint((TAssertions)this, matches, assertionChain, $"[{firstMatchingIndex}]");
}
///
diff --git a/Src/FluentAssertions/Common/StringExtensions.cs b/Src/FluentAssertions/Common/StringExtensions.cs
index b871f3f60a..9730aaf3b9 100644
--- a/Src/FluentAssertions/Common/StringExtensions.cs
+++ b/Src/FluentAssertions/Common/StringExtensions.cs
@@ -60,24 +60,25 @@ public static string EscapePlaceholders(this string value) =>
value.Replace("{", "{{", StringComparison.Ordinal).Replace("}", "}}", StringComparison.Ordinal);
///
- /// Joins a string with one or more other strings using a specified separator.
+ /// Joins a string with another string using the specified separator.
///
///
- /// Any string that is empty (including the original string) is ignored.
+ /// Any string that is empty or null (including the original string) is ignored. Also, if the second
+ /// string starts with an index operator, the separator is omitted.
///
public static string Combine(this string @this, string other, string separator = ".")
{
- if (@this.Length == 0)
+ if (@this.IsNullOrEmpty())
{
- return other.Length != 0 ? other : string.Empty;
+ return !other.IsNullOrEmpty() ? other : string.Empty;
}
- if (other.StartsWith('['))
+ if (other is null || other.StartsWith('['))
{
separator = string.Empty;
}
- return @this + separator + other;
+ return @this + separator + (other ?? "");
}
///
diff --git a/Src/FluentAssertions/EnumAssertionsExtensions.cs b/Src/FluentAssertions/EnumAssertionsExtensions.cs
index da15e34440..828ca328cb 100644
--- a/Src/FluentAssertions/EnumAssertionsExtensions.cs
+++ b/Src/FluentAssertions/EnumAssertionsExtensions.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using FluentAssertions.Execution;
using FluentAssertions.Primitives;
@@ -28,7 +29,7 @@ public static EnumAssertions Should(this TEnum @enum)
/// current .
///
[Pure]
- public static NullableEnumAssertions Should(this TEnum? @enum)
+ public static NullableEnumAssertions Should([NotNull] this TEnum? @enum)
where TEnum : struct, Enum
{
return new NullableEnumAssertions(@enum, AssertionChain.GetOrCreate());
diff --git a/Src/FluentAssertions/Execution/AssertionChain.cs b/Src/FluentAssertions/Execution/AssertionChain.cs
index a565ca8d79..8be7c48ee9 100644
--- a/Src/FluentAssertions/Execution/AssertionChain.cs
+++ b/Src/FluentAssertions/Execution/AssertionChain.cs
@@ -17,14 +17,13 @@ public sealed class AssertionChain
{
private readonly Func getCurrentScope;
private readonly ContextDataDictionary contextData = new();
+ private readonly SubjectIdentificationBuilder identifierBuilder;
private string fallbackIdentifier = "object";
- private Func getCallerIdentifier;
private Func reason;
private bool? succeeded;
// We need to keep track of this because we don't want the second successful assertion hide the first unsuccessful assertion
private Func expectation;
- private string callerPostfix = string.Empty;
private static readonly AsyncLocal Instance = new();
@@ -52,7 +51,7 @@ public void ReuseOnce()
/// This property is used to track if the caller identifier has been customized using the
/// method or similar methods that modify the identifier.
///
- public bool HasOverriddenCallerIdentifier { get; private set; }
+ public bool HasOverriddenCallerIdentifier => identifierBuilder.HasOverriddenIdentifier;
///
/// Either starts a new assertion chain, or, when was called, for once, will return
@@ -68,31 +67,14 @@ public static AssertionChain GetOrCreate()
}
return new AssertionChain(() => AssertionScope.Current,
- () => FluentAssertions.CallerIdentifier.DetermineCallerIdentity());
+ () => FluentAssertions.CallerIdentifier.DetermineCallerIdentities());
}
- private AssertionChain(Func getCurrentScope, Func getCallerIdentifier)
+ private AssertionChain(Func getCurrentScope, Func getCallerIdentifiers)
{
this.getCurrentScope = getCurrentScope;
- this.getCallerIdentifier = () =>
- {
- var scopeName = getCurrentScope().Name();
- var callerIdentifier = getCallerIdentifier();
-
- if (scopeName is null)
- {
- return callerIdentifier;
- }
- else if (callerIdentifier is null)
- {
- return scopeName;
- }
- else
- {
- return $"{scopeName}/{callerIdentifier}";
- }
- };
+ identifierBuilder = new SubjectIdentificationBuilder(getCallerIdentifiers, () => getCurrentScope().Name());
}
///
@@ -102,7 +84,7 @@ private AssertionChain(Func getCurrentScope, Func getCal
///
/// Can be overridden with .
///
- public string CallerIdentifier => getCallerIdentifier() + callerPostfix;
+ public string CallerIdentifier => identifierBuilder.Build();
///
/// Adds an explanation of why the assertion is supposed to succeed to the scope.
@@ -291,8 +273,7 @@ private Continuation FailWith(Func getFailureReason)
///
public void OverrideCallerIdentifier(Func getCallerIdentifier)
{
- this.getCallerIdentifier = getCallerIdentifier;
- HasOverriddenCallerIdentifier = true;
+ identifierBuilder.OverrideSubjectIdentifier(getCallerIdentifier);
}
///
@@ -305,12 +286,20 @@ public void OverrideCallerIdentifier(Func getCallerIdentifier)
///
public AssertionChain WithCallerPostfix(string postfix)
{
- callerPostfix = postfix;
- HasOverriddenCallerIdentifier = true;
+ identifierBuilder.UsePostfix(postfix);
return this;
}
+ ///
+ /// Marks the next assertion as being part of a chained call to Should where it needs to find the next
+ /// caller identifier.
+ ///
+ internal void AdvanceToNextIdentifier()
+ {
+ identifierBuilder.AdvanceToNextSubject();
+ }
+
///
/// Adds some information to the assertion that will be included in the message
/// that is emitted if an assertion fails.
diff --git a/Src/FluentAssertions/Execution/SubjectIdentificationBuilder.cs b/Src/FluentAssertions/Execution/SubjectIdentificationBuilder.cs
new file mode 100644
index 0000000000..b30f391c9f
--- /dev/null
+++ b/Src/FluentAssertions/Execution/SubjectIdentificationBuilder.cs
@@ -0,0 +1,102 @@
+using System;
+using FluentAssertions.Common;
+
+namespace FluentAssertions.Execution;
+
+///
+/// Responsible for constructing the code phrase that identifies the subject-under-test and which is used
+/// in assertion failures managed by a single .
+///
+///
+/// Takes into account the caller identifiers extracted from the invoking code, any scopes created through
+/// (nested) instances of and modifications made by the
+/// .
+///
+internal class SubjectIdentificationBuilder
+{
+ private readonly Func getScopeName;
+ private readonly Lazy identifiersExtractedFromTheCode;
+ private int identifierIndex;
+ private Func getSubject;
+
+ public SubjectIdentificationBuilder(Func getCallerIdentifiers, Func getScopeName)
+ {
+ this.getScopeName = getScopeName;
+
+ identifiersExtractedFromTheCode = new(() => getCallerIdentifiers());
+
+ getSubject = () => GetIdentifier(0);
+ }
+
+ ///
+ /// Tells the builder to assume the assertion has moved to the next identifier in a chained assertion.
+ ///
+ public void AdvanceToNextSubject()
+ {
+ identifierIndex++;
+ var localIndex = identifierIndex;
+
+ getSubject = () => GetIdentifier(localIndex);
+ }
+
+ ///
+ /// Indicates whether the subject identifier has been overridden with a custom value during the assertion process.
+ ///
+ ///
+ /// When or is called,
+ /// this property is set to true, signaling that a manually defined identifier is now used instead of
+ /// automatically constructed identifiers.
+ ///
+ public bool HasOverriddenIdentifier { get; private set; }
+
+ ///
+ /// Completely overrides the construction process with the specified lazy-evaluated identifier.
+ ///
+ public void OverrideSubjectIdentifier(Func getSubject)
+ {
+ HasOverriddenIdentifier = true;
+ this.getSubject = getSubject;
+ }
+
+ ///
+ /// Appends the given postfix to the current subject and combines that with next subject.
+ ///
+ ///
+ /// If the postfix is [0], the current identifier is collection, and the next one
+ /// is Parameters, this method will assume the subject is collection[0].Parameters.
+ ///
+ public void UsePostfix(string postfix)
+ {
+ var localIndex = identifierIndex;
+ getSubject = () => (GetIdentifier(localIndex) + postfix).Combine(GetIdentifier(localIndex + 1));
+
+ HasOverriddenIdentifier = true;
+ }
+
+ ///
+ /// Returns the constructed subject identifier.
+ ///
+ public string Build()
+ {
+ var scopeName = getScopeName();
+ var callerIdentifier = getSubject();
+
+ if (scopeName is null)
+ {
+ return callerIdentifier;
+ }
+ else if (callerIdentifier is null)
+ {
+ return scopeName;
+ }
+ else
+ {
+ return $"{scopeName}/{callerIdentifier}";
+ }
+ }
+
+ private string GetIdentifier(int index)
+ {
+ return identifiersExtractedFromTheCode.Value.Length > index ? identifiersExtractedFromTheCode.Value[index] : null;
+ }
+}
diff --git a/Src/FluentAssertions/FluentAssertions.csproj b/Src/FluentAssertions/FluentAssertions.csproj
index 6b83035ca1..4f27656ded 100644
--- a/Src/FluentAssertions/FluentAssertions.csproj
+++ b/Src/FluentAssertions/FluentAssertions.csproj
@@ -21,14 +21,15 @@
BDD-style unit tests. Works with .NET Standard 2.0 and higher, .NET Framework 4.7 and higher and .NET 6 and higher.
Supports the unit test frameworks MSTest2, NUnit3, NUnit4, XUnit2, XUnit3, MSpec and TUnit.
- IMPORTANT: This version is free for open-source projects and non-commercial use, but commercial use requires [a paid license](https://xceed.com/products/unit-testing/fluent-assertions/).
-Check out the [license page](LICENSE.md) for more information.
+ IMPORTANT: This version is free for open-source projects and non-commercial use, but commercial use requires [a paid license](https://xceed.com/products/unit-testing/fluent-assertions/).
+Check out the [license page](LICENSE) for more information.
https://xceed.com/products/unit-testing/fluent-assertions/
https://github.com/fluentassertions/fluentassertions
git
MSTest2;xUnit;NUnit;MSpec;TUnit;TDD;BDD;Fluent;netstandard;uwp
- LICENSE.md
+ LICENSE
+ true
FluentAssertions.png
See https://fluentassertions.com/releases/
Copyright 2024-$([System.DateTime]::Now.ToString(yyyy)) Xceed Software Inc., all rights reserved
@@ -42,7 +43,7 @@ Check out the [license page](LICENSE.md) for more information.
-
+
diff --git a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
index 7a120af2a0..6f5d0cc81a 100644
--- a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
+++ b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
@@ -55,7 +55,7 @@ public async Task> NotCompleteWithinAsync(TimeSpan ti
if (remainingTime >= TimeSpan.Zero)
{
- bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime);
+ bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime, _ => Task.CompletedTask);
assertionChain
.ForCondition(!completesWithinTimeout)
@@ -228,7 +228,7 @@ private async Task InvokeWithInterceptionAsync(TimeSpan timeout)
// Here we do not need to know whether the task completes (successfully) in timeout
// or does not complete. We are only interested in the exception which is thrown, not returned.
// So, we can ignore the result.
- _ = await CompletesWithinTimeoutAsync(task, remainingTime);
+ _ = await CompletesWithinTimeoutAsync(task, remainingTime, cancelledTask => cancelledTask);
}
return null;
@@ -289,7 +289,7 @@ private protected (TTask result, TimeSpan remainingTime) InvokeWithTimer(TimeSpa
///
/// Monitors the specified task whether it completes withing the remaining time span.
///
- private protected async Task CompletesWithinTimeoutAsync(Task target, TimeSpan remainingTime)
+ private protected async Task CompletesWithinTimeoutAsync(Task target, TimeSpan remainingTime, Func onTaskCanceled)
{
using var delayCancellationTokenSource = new CancellationTokenSource();
@@ -310,8 +310,7 @@ private protected async Task CompletesWithinTimeoutAsync(Task target, Time
if (target.IsCanceled)
{
- // Rethrow the exception causing the task be canceled.
- await target;
+ await onTaskCanceled(target);
}
// The monitored task is completed, we shall cancel the clock.
diff --git a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs
index ea78c3b77b..f5860af3ad 100644
--- a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs
+++ b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs
@@ -65,7 +65,7 @@ public async Task, TR
if (assertionChain.Succeeded)
{
- bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime);
+ bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime, _ => Task.CompletedTask);
assertionChain
.ForCondition(completesWithinTimeout)
diff --git a/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs
index 8c672ff0d2..f68a8dac85 100644
--- a/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs
+++ b/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs
@@ -62,7 +62,7 @@ public async Task> CompleteWith
if (assertionChain.Succeeded)
{
- bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime);
+ bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime, _ => Task.CompletedTask);
assertionChain
.ForCondition(completesWithinTimeout)
diff --git a/Src/FluentAssertions/Types/MemberInfoAssertions.cs b/Src/FluentAssertions/Types/MemberInfoAssertions.cs
index 348d8f484f..3e214fd28c 100644
--- a/Src/FluentAssertions/Types/MemberInfoAssertions.cs
+++ b/Src/FluentAssertions/Types/MemberInfoAssertions.cs
@@ -104,7 +104,7 @@ public AndWhichConstraint, TAttribut
", but that attribute was not found.");
}
- return new AndWhichConstraint, TAttribute>(this, attributes);
+ return new AndWhichConstraint, TAttribute>(this, attributes, assertionChain);
}
///
diff --git a/Src/FluentAssertions/Types/TypeAssertions.cs b/Src/FluentAssertions/Types/TypeAssertions.cs
index 1efa74d961..37699196a0 100644
--- a/Src/FluentAssertions/Types/TypeAssertions.cs
+++ b/Src/FluentAssertions/Types/TypeAssertions.cs
@@ -251,7 +251,7 @@ public AndWhichConstraint BeDecoratedWith(this, attributes);
+ return new AndWhichConstraint(this, attributes, assertionChain);
}
///
@@ -287,7 +287,7 @@ public AndWhichConstraint BeDecoratedWith(this, attributes);
+ return new AndWhichConstraint(this, attributes, assertionChain);
}
///
@@ -312,7 +312,7 @@ public AndWhichConstraint BeDecoratedWithOrInherit(this, attributes);
+ return new AndWhichConstraint(this, attributes, assertionChain);
}
///
@@ -348,7 +348,7 @@ public AndWhichConstraint BeDecoratedWithOrInherit(this, attributes);
+ return new AndWhichConstraint(this, attributes, assertionChain);
}
///
diff --git a/Tests/Approval.Tests/Approval.Tests.csproj b/Tests/Approval.Tests/Approval.Tests.csproj
index 84f1b3a10c..7fc4bea4f2 100644
--- a/Tests/Approval.Tests/Approval.Tests.csproj
+++ b/Tests/Approval.Tests/Approval.Tests.csproj
@@ -5,15 +5,15 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
+
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
index 73557f0f23..05b4c07fd9 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
@@ -19,6 +19,7 @@ namespace FluentAssertions
{
public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects) { }
public AndWhichConstraint(TParent parent, TSubject subject) { }
+ public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain) { }
public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix) { }
public AndWhichConstraint(TParent parent, TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix = "") { }
public TSubject Subject { get; }
@@ -52,33 +53,33 @@ namespace FluentAssertions
public static FluentAssertions.Events.IMonitor Monitor(this T eventSource) { }
public static FluentAssertions.Events.IMonitor Monitor(this T eventSource, System.Action configureOptions) { }
public static FluentAssertions.Specialized.ExecutionTimeAssertions Should(this FluentAssertions.Specialized.ExecutionTime executionTime) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { }
public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Types.MethodInfoSelectorAssertions _) { }
public static FluentAssertions.Types.PropertyInfoSelectorAssertions Should(this FluentAssertions.Types.PropertyInfoSelector propertyInfoSelector) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Types.PropertyInfoSelectorAssertions _) { }
public static FluentAssertions.Types.TypeSelectorAssertions Should(this FluentAssertions.Types.TypeSelector typeSelector) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Types.TypeSelectorAssertions _) { }
public static FluentAssertions.Specialized.ActionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Action action) { }
public static FluentAssertions.Collections.StringCollectionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Collections.Generic.IEnumerable @this) { }
public static FluentAssertions.Primitives.DateTimeAssertions Should(this System.DateTime actualValue) { }
- public static FluentAssertions.Primitives.NullableDateTimeAssertions Should(this System.DateTime? actualValue) { }
+ public static FluentAssertions.Primitives.NullableDateTimeAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.DateTime? actualValue) { }
public static FluentAssertions.Primitives.DateTimeOffsetAssertions Should(this System.DateTimeOffset actualValue) { }
- public static FluentAssertions.Primitives.NullableDateTimeOffsetAssertions Should(this System.DateTimeOffset? actualValue) { }
+ public static FluentAssertions.Primitives.NullableDateTimeOffsetAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.DateTimeOffset? actualValue) { }
public static FluentAssertions.Specialized.NonGenericAsyncFunctionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Func action) { }
public static FluentAssertions.Primitives.GuidAssertions Should(this System.Guid actualValue) { }
- public static FluentAssertions.Primitives.NullableGuidAssertions Should(this System.Guid? actualValue) { }
+ public static FluentAssertions.Primitives.NullableGuidAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Guid? actualValue) { }
public static FluentAssertions.Streams.BufferedStreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.BufferedStream actualValue) { }
public static FluentAssertions.Streams.StreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.Stream actualValue) { }
public static FluentAssertions.Types.AssemblyAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Reflection.Assembly assembly) { }
@@ -86,63 +87,63 @@ namespace FluentAssertions
public static FluentAssertions.Types.MethodInfoAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Reflection.MethodInfo methodInfo) { }
public static FluentAssertions.Types.PropertyInfoAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Reflection.PropertyInfo propertyInfo) { }
public static FluentAssertions.Primitives.SimpleTimeSpanAssertions Should(this System.TimeSpan actualValue) { }
- public static FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions Should(this System.TimeSpan? actualValue) { }
+ public static FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.TimeSpan? actualValue) { }
public static FluentAssertions.Types.TypeAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Type subject) { }
public static FluentAssertions.Xml.XAttributeAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Xml.Linq.XAttribute actualValue) { }
public static FluentAssertions.Xml.XDocumentAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Xml.Linq.XDocument actualValue) { }
public static FluentAssertions.Xml.XElementAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Xml.Linq.XElement actualValue) { }
public static FluentAssertions.Primitives.BooleanAssertions Should(this bool actualValue) { }
- public static FluentAssertions.Primitives.NullableBooleanAssertions Should(this bool? actualValue) { }
+ public static FluentAssertions.Primitives.NullableBooleanAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this bool? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this byte actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this byte? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this byte? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this decimal actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this decimal? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this decimal? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this double actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this double? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this double? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this float actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this float? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this float? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this int actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this int? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this int? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this long actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this long? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this long? actualValue) { }
public static FluentAssertions.Primitives.ObjectAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this object actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this sbyte actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this sbyte? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this sbyte? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this short actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this short? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this short? actualValue) { }
public static FluentAssertions.Primitives.StringAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this string actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this uint actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this uint? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this uint? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this ulong actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this ulong? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this ulong? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this ushort actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this ushort? actualValue) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this ushort? actualValue) { }
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.BooleanAssertions _)
where TAssertions : FluentAssertions.Primitives.BooleanAssertions { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.DateTimeAssertions _)
where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.DateTimeOffsetAssertions _)
where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [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)]
+ [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)]
+ [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" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.SimpleTimeSpanAssertions _)
where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { }
public static FluentAssertions.Collections.GenericCollectionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Collections.Generic.IEnumerable actualValue) { }
@@ -150,18 +151,18 @@ namespace FluentAssertions
public static FluentAssertions.Specialized.FunctionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Func func) { }
public static FluentAssertions.Numeric.ComparableTypeAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IComparable comparableValue) { }
public static FluentAssertions.Specialized.TaskCompletionSourceAssertions Should(this System.Threading.Tasks.TaskCompletionSource tcs) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Numeric.NumericAssertionsBase _)
where TSubject : struct, System.IComparable
where TAssertions : FluentAssertions.Numeric.NumericAssertions { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.EnumAssertions _)
where TEnum : struct, System.Enum
where TAssertions : FluentAssertions.Primitives.EnumAssertions { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Primitives.ReferenceTypeAssertions _)
where TAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { }
public static FluentAssertions.Collections.GenericDictionaryAssertions, TKey, TValue> Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Collections.Generic.IDictionary actualValue) { }
@@ -191,6 +192,7 @@ namespace FluentAssertions
public static class CallerIdentifier
{
public static System.Action Logger { get; set; }
+ public static string[] DetermineCallerIdentities() { }
public static string DetermineCallerIdentity() { }
}
[System.AttributeUsage(System.AttributeTargets.Method)]
@@ -207,7 +209,7 @@ namespace FluentAssertions
{
public static FluentAssertions.Primitives.EnumAssertions Should(this TEnum @enum)
where TEnum : struct, System.Enum { }
- public static FluentAssertions.Primitives.NullableEnumAssertions Should(this TEnum? @enum)
+ public static FluentAssertions.Primitives.NullableEnumAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this TEnum? @enum)
where TEnum : struct, System.Enum { }
}
public static class EventRaisingExtensions
@@ -2403,8 +2405,8 @@ namespace FluentAssertions.Types
public FluentAssertions.AndConstraint Be(System.Type expected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint Be(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint BeAbstract(string because = "", params object[] becauseArgs) { }
- public FluentAssertions.AndConstraint BeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { }
- public FluentAssertions.AndConstraint BeAssignableTo(string because = "", params object[] becauseArgs) { }
+ public new FluentAssertions.AndConstraint BeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { }
+ public new FluentAssertions.AndConstraint BeAssignableTo(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint BeDecoratedWith(string because = "", params object[] becauseArgs)
where TAttribute : System.Attribute { }
public FluentAssertions.AndWhichConstraint BeDecoratedWith(System.Linq.Expressions.Expression> isMatchingAttributePredicate, string because = "", params object[] becauseArgs)
@@ -2441,8 +2443,8 @@ namespace FluentAssertions.Types
public FluentAssertions.AndConstraint NotBe(System.Type unexpected, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBe(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeAbstract(string because = "", params object[] becauseArgs) { }
- public FluentAssertions.AndConstraint NotBeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { }
- public FluentAssertions.AndConstraint NotBeAssignableTo(string because = "", params object[] becauseArgs) { }
+ public new FluentAssertions.AndConstraint NotBeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { }
+ public new FluentAssertions.AndConstraint NotBeAssignableTo(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint NotBeDecoratedWith(string because = "", params object[] becauseArgs)
where TAttribute : System.Attribute { }
public FluentAssertions.AndConstraint NotBeDecoratedWith(System.Linq.Expressions.Expression> isMatchingAttributePredicate, 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 b1e1a87992..784751b7cd 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
@@ -19,6 +19,7 @@ namespace FluentAssertions
{
public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects) { }
public AndWhichConstraint(TParent parent, TSubject subject) { }
+ public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain) { }
public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix) { }
public AndWhichConstraint(TParent parent, TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix = "") { }
public TSubject Subject { get; }
@@ -52,35 +53,35 @@ namespace FluentAssertions
public static FluentAssertions.Events.IMonitor Monitor(this T eventSource) { }
public static FluentAssertions.Events.IMonitor Monitor(this T eventSource, System.Action configureOptions) { }
public static FluentAssertions.Specialized.ExecutionTimeAssertions Should(this FluentAssertions.Specialized.ExecutionTime executionTime) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { }
public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Types.MethodInfoSelectorAssertions _) { }
public static FluentAssertions.Types.PropertyInfoSelectorAssertions Should(this FluentAssertions.Types.PropertyInfoSelector propertyInfoSelector) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Types.PropertyInfoSelectorAssertions _) { }
public static FluentAssertions.Types.TypeSelectorAssertions Should(this FluentAssertions.Types.TypeSelector typeSelector) { }
- [System.Obsolete(("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
- "ly following \'And\'"), true)]
+ [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" +
+ "ly following \'And\'", true)]
public static void Should(this FluentAssertions.Types.TypeSelectorAssertions _) { }
public static FluentAssertions.Specialized.ActionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Action action) { }
public static FluentAssertions.Collections.StringCollectionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Collections.Generic.IEnumerable @this) { }
public static FluentAssertions.Primitives.DateOnlyAssertions Should(this System.DateOnly actualValue) { }
- public static FluentAssertions.Primitives.NullableDateOnlyAssertions Should(this System.DateOnly? actualValue) { }
+ public static FluentAssertions.Primitives.NullableDateOnlyAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.DateOnly? 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.NullableDateTimeAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.DateTime? actualValue) { }
public static FluentAssertions.Primitives.DateTimeOffsetAssertions Should(this System.DateTimeOffset actualValue) { }
- public static FluentAssertions.Primitives.NullableDateTimeOffsetAssertions Should(this System.DateTimeOffset? actualValue) { }
+ public static FluentAssertions.Primitives.NullableDateTimeOffsetAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.DateTimeOffset? actualValue) { }
public static FluentAssertions.Specialized.NonGenericAsyncFunctionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Func action) { }
public static FluentAssertions.Primitives.GuidAssertions Should(this System.Guid actualValue) { }
- public static FluentAssertions.Primitives.NullableGuidAssertions Should(this System.Guid? actualValue) { }
+ public static FluentAssertions.Primitives.NullableGuidAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Guid? actualValue) { }
public static FluentAssertions.Streams.BufferedStreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.BufferedStream actualValue) { }
public static FluentAssertions.Streams.StreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.Stream actualValue) { }
public static FluentAssertions.Types.AssemblyAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Reflection.Assembly assembly) { }
@@ -89,73 +90,73 @@ namespace FluentAssertions
public static FluentAssertions.Types.PropertyInfoAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Reflection.PropertyInfo propertyInfo) { }
public static FluentAssertions.Specialized.TaskCompletionSourceAssertions Should(this System.Threading.Tasks.TaskCompletionSource tcs) { }
public static FluentAssertions.Primitives.TimeOnlyAssertions Should(this System.TimeOnly actualValue) { }
- public static FluentAssertions.Primitives.NullableTimeOnlyAssertions Should(this System.TimeOnly? actualValue) { }
+ public static FluentAssertions.Primitives.NullableTimeOnlyAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.TimeOnly? actualValue) { }
public static FluentAssertions.Primitives.SimpleTimeSpanAssertions Should(this System.TimeSpan actualValue) { }
- public static FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions Should(this System.TimeSpan? actualValue) { }
+ public static FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.TimeSpan? actualValue) { }
public static FluentAssertions.Types.TypeAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Type subject) { }
public static FluentAssertions.Xml.XAttributeAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Xml.Linq.XAttribute actualValue) { }
public static FluentAssertions.Xml.XDocumentAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Xml.Linq.XDocument actualValue) { }
public static FluentAssertions.Xml.XElementAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Xml.Linq.XElement actualValue) { }
public static FluentAssertions.Primitives.BooleanAssertions Should(this bool actualValue) { }
- public static FluentAssertions.Primitives.NullableBooleanAssertions Should(this bool? actualValue) { }
+ public static FluentAssertions.Primitives.NullableBooleanAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this bool? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this byte actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this byte? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this byte? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions Should(this decimal actualValue) { }
- public static FluentAssertions.Numeric.NullableNumericAssertions Should(this decimal? actualValue) { }
+ public static FluentAssertions.Numeric.NullableNumericAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this decimal? actualValue) { }
public static FluentAssertions.Numeric.NumericAssertions