Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Avoid exceptions when wrapping in AssertionScope #2103

Merged
merged 3 commits into from Jan 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 19 additions & 9 deletions Src/FluentAssertions/NumericAssertionsExtensions.cs
Expand Up @@ -674,13 +674,16 @@ public static class NumericAssertionsExtensions
{
Guard.ThrowIfArgumentIsNegative(precision);

Execute.Assertion
bool success = Execute.Assertion
.ForCondition(parent.Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was <null>.", expectedValue, precision);

var nonNullableAssertions = new SingleAssertions(parent.Subject.Value);
nonNullableAssertions.BeApproximately(expectedValue, precision, because, becauseArgs);
if (success)
{
var nonNullableAssertions = new SingleAssertions(parent.Subject.Value);
nonNullableAssertions.BeApproximately(expectedValue, precision, because, becauseArgs);
}

return new AndConstraint<NullableNumericAssertions<float>>(parent);
}
Expand Down Expand Up @@ -800,13 +803,16 @@ public static class NumericAssertionsExtensions
{
Guard.ThrowIfArgumentIsNegative(precision);

Execute.Assertion
bool success = Execute.Assertion
.ForCondition(parent.Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was <null>.", expectedValue, precision);

var nonNullableAssertions = new DoubleAssertions(parent.Subject.Value);
BeApproximately(nonNullableAssertions, expectedValue, precision, because, becauseArgs);
if (success)
{
var nonNullableAssertions = new DoubleAssertions(parent.Subject.Value);
BeApproximately(nonNullableAssertions, expectedValue, precision, because, becauseArgs);
}

return new AndConstraint<NullableNumericAssertions<double>>(parent);
}
Expand Down Expand Up @@ -926,13 +932,17 @@ public static class NumericAssertionsExtensions
{
Guard.ThrowIfArgumentIsNegative(precision);

Execute.Assertion
bool success = Execute.Assertion
.ForCondition(parent.Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was <null>.", expectedValue, precision);

var nonNullableAssertions = new DecimalAssertions(parent.Subject.Value);
BeApproximately(nonNullableAssertions, expectedValue, precision, because, becauseArgs);
if (success)
{
var nonNullableAssertions = new DecimalAssertions(parent.Subject.Value);
BeApproximately(nonNullableAssertions, expectedValue, precision, because, becauseArgs);
}

return new AndConstraint<NullableNumericAssertions<decimal>>(parent);
}

Expand Down
35 changes: 30 additions & 5 deletions Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
Expand Up @@ -127,11 +127,16 @@ public AsyncFunctionAssertions(Func<TTask> subject, IExtractExceptions extractor
{
Type expectedType = typeof(TException);

Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} to throw exactly {0}{reason}, but found <null>.", expectedType);

if (!success)
{
return new ExceptionAssertions<TException>(Array.Empty<TException>());
jnyrup marked this conversation as resolved.
Show resolved Hide resolved
}

Exception exception = await InvokeWithInterceptionAsync(Subject);

Execute.Assertion
Expand All @@ -158,11 +163,16 @@ public AsyncFunctionAssertions(Func<TTask> subject, IExtractExceptions extractor
params object[] becauseArgs)
where TException : Exception
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} to throw {0}{reason}, but found <null>.", typeof(TException));

if (!success)
{
return new ExceptionAssertions<TException>(Array.Empty<TException>());
}

Exception exception = await InvokeWithInterceptionAsync(Subject);
return ThrowInternal<TException>(exception, because, becauseArgs);
}
Expand All @@ -179,11 +189,16 @@ public AsyncFunctionAssertions(Func<TTask> subject, IExtractExceptions extractor
/// </param>
public async Task<AndConstraint<TAssertions>> NotThrowAsync(string because = "", params object[] becauseArgs)
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw{reason}, but found <null>.");

if (!success)
{
return new AndConstraint<TAssertions>((TAssertions)this);
}

try
{
await Subject.Invoke();
Expand All @@ -209,11 +224,16 @@ public async Task<AndConstraint<TAssertions>> NotThrowAsync(string because = "",
public async Task<AndConstraint<TAssertions>> NotThrowAsync<TException>(string because = "", params object[] becauseArgs)
where TException : Exception
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw{reason}, but found <null>.");

if (!success)
{
return new AndConstraint<TAssertions>((TAssertions)this);
}

try
{
await Subject.Invoke();
Expand Down Expand Up @@ -254,11 +274,16 @@ public Task<AndConstraint<TAssertions>> NotThrowAfterAsync(TimeSpan waitTime, Ti
Guard.ThrowIfArgumentIsNegative(waitTime);
Guard.ThrowIfArgumentIsNegative(pollInterval);

Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found <null>.", waitTime);

if (!success)
{
return Task.FromResult(new AndConstraint<TAssertions>((TAssertions)this));
}

return AssertionTaskAsync();

async Task<AndConstraint<TAssertions>> AssertionTaskAsync()
Expand Down
40 changes: 33 additions & 7 deletions Src/FluentAssertions/Specialized/DelegateAssertions.cs
Expand Up @@ -38,11 +38,16 @@ private protected DelegateAssertions(TDelegate @delegate, IExtractExceptions ext
public ExceptionAssertions<TException> Throw<TException>(string because = "", params object[] becauseArgs)
where TException : Exception
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} to throw {0}{reason}, but found <null>.", typeof(TException));

if (!success)
{
return new ExceptionAssertions<TException>(Array.Empty<TException>());
}

FailIfSubjectIsAsyncVoid();
Exception exception = InvokeSubjectWithInterception();
return ThrowInternal<TException>(exception, because, becauseArgs);
Expand All @@ -61,11 +66,16 @@ public ExceptionAssertions<TException> Throw<TException>(string because = "", pa
public AndConstraint<TAssertions> NotThrow<TException>(string because = "", params object[] becauseArgs)
where TException : Exception
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw {0}{reason}, but found <null>.", typeof(TException));

if (!success)
{
return new AndConstraint<TAssertions>((TAssertions)this);
}

FailIfSubjectIsAsyncVoid();
Exception exception = InvokeSubjectWithInterception();
return NotThrowInternal<TException>(exception, because, becauseArgs);
Expand All @@ -83,11 +93,16 @@ public AndConstraint<TAssertions> NotThrow<TException>(string because = "", para
/// </param>
public AndConstraint<TAssertions> NotThrow(string because = "", params object[] becauseArgs)
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw{reason}, but found <null>.");

if (!success)
{
return new AndConstraint<TAssertions>((TAssertions)this);
}

FailIfSubjectIsAsyncVoid();
Exception exception = InvokeSubjectWithInterception();
return NotThrowInternal(exception, because, becauseArgs);
Expand All @@ -113,11 +128,16 @@ public AndConstraint<TAssertions> NotThrow(string because = "", params object[]
params object[] becauseArgs)
where TException : Exception
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} to throw exactly {0}{reason}, but found <null>.", typeof(TException));

if (!success)
{
return new ExceptionAssertions<TException>(Array.Empty<TException>());
}

FailIfSubjectIsAsyncVoid();
Exception exception = InvokeSubjectWithInterception();

Expand Down Expand Up @@ -158,14 +178,20 @@ public AndConstraint<TAssertions> NotThrow(string because = "", params object[]
/// <exception cref="ArgumentOutOfRangeException"><paramref name="waitTime"/> or <paramref name="pollInterval"/> are negative.</exception>
public AndConstraint<TAssertions> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs)
{
Execute.Assertion
Guard.ThrowIfArgumentIsNegative(waitTime);
Guard.ThrowIfArgumentIsNegative(pollInterval);

bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw after {0}{reason}, but found <null>.", waitTime);

if (!success)
{
return new AndConstraint<TAssertions>((TAssertions)this);
}

FailIfSubjectIsAsyncVoid();
Guard.ThrowIfArgumentIsNegative(waitTime);
Guard.ThrowIfArgumentIsNegative(pollInterval);

TimeSpan? invocationEndTime = null;
Exception exception = null;
Expand Down
21 changes: 17 additions & 4 deletions Src/FluentAssertions/Specialized/FunctionAssertions.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Security;
using FluentAssertions.Common;
using FluentAssertions.Execution;

Expand Down Expand Up @@ -40,12 +41,18 @@ protected override void InvokeSubject()
/// </param>
public new AndWhichConstraint<FunctionAssertions<T>, T> NotThrow(string because = "", params object[] becauseArgs)
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw{reason}, but found <null>.");

T result = FunctionAssertionHelpers.NotThrow(Subject, because, becauseArgs);
T result = default;

if (success)
{
result = FunctionAssertionHelpers.NotThrow(Subject, because, becauseArgs);
}

return new AndWhichConstraint<FunctionAssertions<T>, T>(this, result);
}

Expand Down Expand Up @@ -74,12 +81,18 @@ protected override void InvokeSubject()
/// <exception cref="ArgumentOutOfRangeException"><paramref name="waitTime"/> or <paramref name="pollInterval"/> are negative.</exception>
public new AndWhichConstraint<FunctionAssertions<T>, T> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs)
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found <null>.", waitTime);

T result = FunctionAssertionHelpers.NotThrowAfter(Subject, Clock, waitTime, pollInterval, because, becauseArgs);
T result = default;

if (success)
{
result = FunctionAssertionHelpers.NotThrowAfter(Subject, Clock, waitTime, pollInterval, because, becauseArgs);
}

return new AndWhichConstraint<FunctionAssertions<T>, T>(this, result);
}
}
15 changes: 13 additions & 2 deletions Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs
Expand Up @@ -79,11 +79,16 @@ public GenericAsyncFunctionAssertions(Func<Task<TResult>> subject, IExtractExcep
public new async Task<AndWhichConstraint<GenericAsyncFunctionAssertions<TResult>, TResult>> NotThrowAsync(
string because = "", params object[] becauseArgs)
{
Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw{reason}, but found <null>.");

if (!success)
{
return new AndWhichConstraint<GenericAsyncFunctionAssertions<TResult>, TResult>(this, default(TResult));
}

try
{
TResult result = await Subject.Invoke();
Expand Down Expand Up @@ -125,11 +130,17 @@ public GenericAsyncFunctionAssertions(Func<Task<TResult>> subject, IExtractExcep
Guard.ThrowIfArgumentIsNegative(waitTime);
Guard.ThrowIfArgumentIsNegative(pollInterval);

Execute.Assertion
bool success = Execute.Assertion
.ForCondition(Subject is not null)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found <null>.", waitTime);

if (!success)
{
var result = new AndWhichConstraint<GenericAsyncFunctionAssertions<TResult>, TResult>(this, default(TResult));
return Task.FromResult(result);
}

return AssertionTaskAsync();

async Task<AndWhichConstraint<GenericAsyncFunctionAssertions<TResult>, TResult>> AssertionTaskAsync()
Expand Down