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

Non-generic overload for WithInnerExceptionExactly #1769

Merged
merged 9 commits into from Jan 13, 2022
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
44 changes: 44 additions & 0 deletions Src/FluentAssertions/ExceptionAssertionsExtensions.cs
Expand Up @@ -80,6 +80,28 @@ public static class ExceptionAssertionsExtensions
return (await task).WithInnerException<TInnerException>(because, becauseArgs);
}

/// <summary>
/// Asserts that the thrown exception contains an inner exception of type <param name="innerException" />.
/// </summary>
/// <typeparam name="TException">The expected type of the exception.</typeparam>
/// <param name="task">The <see cref="ExceptionAssertions{TException}"/> containing the thrown exception.</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public static async Task<ExceptionAssertions<Exception>> WithInnerException<TException>(
this Task<ExceptionAssertions<TException>> task,
Type innerException,
string because = "",
params object[] becauseArgs)
where TException : Exception
{
return (await task).WithInnerException(innerException, because, becauseArgs);
}

/// <summary>
/// Asserts that the thrown exception contains an inner exception of the exact type <typeparamref name="TInnerException" /> (and not a derived exception type).
/// </summary>
Expand All @@ -103,6 +125,28 @@ public static class ExceptionAssertionsExtensions
return (await task).WithInnerExceptionExactly<TInnerException>(because, becauseArgs);
}

/// <summary>
/// Asserts that the thrown exception contains an inner exception of the exact type <param name="innerException" /> (and not a derived exception type).
/// </summary>
/// <typeparam name="TException">The expected type of the exception.</typeparam>
/// <param name="task">The <see cref="ExceptionAssertions{TException}"/> containing the thrown exception.</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public static async Task<ExceptionAssertions<Exception>> WithInnerExceptionExactly<TException>(
this Task<ExceptionAssertions<TException>> task,
Type innerException,
string because = "",
params object[] becauseArgs)
where TException : Exception
{
return (await task).WithInnerExceptionExactly(innerException, because, becauseArgs);
}

/// <summary>
/// Asserts that the thrown exception has a parameter which name matches <paramref name="paramName" />.
/// </summary>
Expand Down
120 changes: 84 additions & 36 deletions Src/FluentAssertions/Specialized/ExceptionAssertions.cs
Expand Up @@ -3,9 +3,7 @@
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;

using FluentAssertions.Common;
using FluentAssertions.Equivalency;
using FluentAssertions.Equivalency.Steps;
using FluentAssertions.Execution;
using FluentAssertions.Formatting;
Expand Down Expand Up @@ -89,7 +87,8 @@ public ExceptionAssertions(IEnumerable<TException> exceptions)
.ForCondition(Subject.Any())
.FailWith("Expected exception with message {0}{reason}, but no exception was thrown.", expectedWildcardPattern);

OuterMessageAssertion.Execute(Subject.Select(exc => exc.Message).ToArray(), expectedWildcardPattern, because, becauseArgs);
OuterMessageAssertion.Execute(Subject.Select(exc => exc.Message).ToArray(), expectedWildcardPattern, because,
becauseArgs);

return this;
}
Expand All @@ -109,28 +108,24 @@ public ExceptionAssertions(IEnumerable<TException> exceptions)
params object[] becauseArgs)
where TInnerException : Exception
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected inner {0}{reason}, but ", typeof(TInnerException))
.ForCondition(Subject is not null)
.FailWith("no exception was thrown.")
.Then
.ForCondition(Subject.Any(e => e.InnerException is not null))
.FailWith("the thrown exception has no inner exception.")
.Then
.ClearExpectation();

TInnerException[] expectedInnerExceptions = Subject
.Select(e => e.InnerException)
.OfType<TInnerException>()
.ToArray();

Execute.Assertion
.ForCondition(expectedInnerExceptions.Any())
.BecauseOf(because, becauseArgs)
.FailWith("Expected inner {0}{reason}, but found {1}.", typeof(TInnerException), SingleSubject.InnerException);
var expectedInnerExceptions = AssertInnerExceptions(typeof(TInnerException), because, becauseArgs);
return new ExceptionAssertions<TInnerException>(expectedInnerExceptions.Cast<TInnerException>());
}

return new ExceptionAssertions<TInnerException>(expectedInnerExceptions);
/// <summary>
/// Asserts that the thrown exception contains an inner exception of type <param name="innerException" />.
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public ExceptionAssertions<Exception> WithInnerException(Type innerException, string because = null,
params object[] becauseArgs)
{
return new ExceptionAssertions<Exception>(AssertInnerExceptions(innerException, because, becauseArgs));
}

/// <summary>
Expand All @@ -148,19 +143,24 @@ public ExceptionAssertions(IEnumerable<TException> exceptions)
params object[] becauseArgs)
where TInnerException : Exception
{
WithInnerException<TInnerException>(because, becauseArgs);

TInnerException[] expectedExceptions = Subject
.Select(e => e.InnerException)
.OfType<TInnerException>()
.Where(e => e?.GetType() == typeof(TInnerException)).ToArray();

Execute.Assertion
.ForCondition(expectedExceptions.Any())
.BecauseOf(because, becauseArgs)
.FailWith("Expected inner {0}{reason}, but found {1}.", typeof(TInnerException), SingleSubject.InnerException);
var exceptionExpression = AssertInnerExceptionExactly(typeof(TInnerException), because, becauseArgs);
return new ExceptionAssertions<TInnerException>(exceptionExpression.Cast<TInnerException>());
}

return new ExceptionAssertions<TInnerException>(expectedExceptions);
/// <summary>
/// Asserts that the thrown exception contains an inner exception of the exact type <param name="innerException" /> (and not a derived exception type).
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public ExceptionAssertions<Exception> WithInnerExceptionExactly(Type innerException, string because = null,
params object[] becauseArgs)
{
return new ExceptionAssertions<Exception>(AssertInnerExceptionExactly(innerException, because, becauseArgs));
}

/// <summary>
Expand Down Expand Up @@ -191,6 +191,54 @@ public ExceptionAssertions(IEnumerable<TException> exceptions)
return this;
}

private IEnumerable<Exception> AssertInnerExceptionExactly(Type innerException, string because = null,
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(innerException, nameof(innerException));

AssertInnerExceptions(innerException, because, becauseArgs);

Exception[] expectedExceptions = Subject
.Select(e => e.InnerException)
.Where(e => e?.GetType() == innerException).ToArray();

Execute.Assertion
.ForCondition(expectedExceptions.Any())
.BecauseOf(because, becauseArgs)
.FailWith("Expected inner {0}{reason}, but found {1}.", innerException, SingleSubject.InnerException);

return expectedExceptions;
}

private IEnumerable<Exception> AssertInnerExceptions(Type innerException, string because = null,
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(innerException, nameof(innerException));

Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected inner {0}{reason}, but ", innerException)
.ForCondition(Subject is not null)
.FailWith("no exception was thrown.")
.Then
.ForCondition(Subject.Any(e => e.InnerException is not null))
.FailWith("the thrown exception has no inner exception.")
.Then
.ClearExpectation();

Exception[] expectedInnerExceptions = Subject
.Select(e => e.InnerException)
.Where(e => e != null && e.GetType().IsSameOrInherits(innerException))
.ToArray();

Execute.Assertion
.ForCondition(expectedInnerExceptions.Any())
.BecauseOf(because, becauseArgs)
.FailWith("Expected inner {0}{reason}, but found {1}.", innerException, SingleSubject.InnerException);

return expectedInnerExceptions;
}

private TException SingleSubject
{
get
Expand Down
Expand Up @@ -242,9 +242,13 @@ namespace FluentAssertions
{
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> Where<TException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, System.Linq.Expressions.Expression<System.Func<TException, bool>> exceptionExpression, string because = "", params object[] becauseArgs)
where TException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<System.Exception>> WithInnerException<TException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, System.Type innerException, string because = "", params object[] becauseArgs)
where TException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TInnerException>> WithInnerException<TException, TInnerException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, string because = "", params object[] becauseArgs)
where TException : System.Exception
where TInnerException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<System.Exception>> WithInnerExceptionExactly<TException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, System.Type innerException, string because = "", params object[] becauseArgs)
where TException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TInnerException>> WithInnerExceptionExactly<TException, TInnerException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, string because = "", params object[] becauseArgs)
where TException : System.Exception
where TInnerException : System.Exception { }
Expand Down Expand Up @@ -2209,8 +2213,10 @@ namespace FluentAssertions.Specialized
protected override string Identifier { get; }
public TException Which { get; }
public FluentAssertions.Specialized.ExceptionAssertions<TException> Where(System.Linq.Expressions.Expression<System.Func<TException, bool>> exceptionExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.Specialized.ExceptionAssertions<System.Exception> WithInnerException(System.Type innerException, string because = null, params object[] becauseArgs) { }
public virtual FluentAssertions.Specialized.ExceptionAssertions<TInnerException> WithInnerException<TInnerException>(string because = null, params object[] becauseArgs)
where TInnerException : System.Exception { }
public FluentAssertions.Specialized.ExceptionAssertions<System.Exception> WithInnerExceptionExactly(System.Type innerException, string because = null, params object[] becauseArgs) { }
public virtual FluentAssertions.Specialized.ExceptionAssertions<TInnerException> WithInnerExceptionExactly<TInnerException>(string because = null, params object[] becauseArgs)
where TInnerException : System.Exception { }
public virtual FluentAssertions.Specialized.ExceptionAssertions<TException> WithMessage(string expectedWildcardPattern, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -242,9 +242,13 @@ namespace FluentAssertions
{
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> Where<TException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, System.Linq.Expressions.Expression<System.Func<TException, bool>> exceptionExpression, string because = "", params object[] becauseArgs)
where TException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<System.Exception>> WithInnerException<TException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, System.Type innerException, string because = "", params object[] becauseArgs)
where TException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TInnerException>> WithInnerException<TException, TInnerException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, string because = "", params object[] becauseArgs)
where TException : System.Exception
where TInnerException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<System.Exception>> WithInnerExceptionExactly<TException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, System.Type innerException, string because = "", params object[] becauseArgs)
where TException : System.Exception { }
public static System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TInnerException>> WithInnerExceptionExactly<TException, TInnerException>(this System.Threading.Tasks.Task<FluentAssertions.Specialized.ExceptionAssertions<TException>> task, string because = "", params object[] becauseArgs)
where TException : System.Exception
where TInnerException : System.Exception { }
Expand Down Expand Up @@ -2209,8 +2213,10 @@ namespace FluentAssertions.Specialized
protected override string Identifier { get; }
public TException Which { get; }
public FluentAssertions.Specialized.ExceptionAssertions<TException> Where(System.Linq.Expressions.Expression<System.Func<TException, bool>> exceptionExpression, string because = "", params object[] becauseArgs) { }
public FluentAssertions.Specialized.ExceptionAssertions<System.Exception> WithInnerException(System.Type innerException, string because = null, params object[] becauseArgs) { }
public virtual FluentAssertions.Specialized.ExceptionAssertions<TInnerException> WithInnerException<TInnerException>(string because = null, params object[] becauseArgs)
where TInnerException : System.Exception { }
public FluentAssertions.Specialized.ExceptionAssertions<System.Exception> WithInnerExceptionExactly(System.Type innerException, string because = null, params object[] becauseArgs) { }
public virtual FluentAssertions.Specialized.ExceptionAssertions<TInnerException> WithInnerExceptionExactly<TInnerException>(string because = null, params object[] becauseArgs)
where TInnerException : System.Exception { }
public virtual FluentAssertions.Specialized.ExceptionAssertions<TException> WithMessage(string expectedWildcardPattern, string because = "", params object[] becauseArgs) { }
Expand Down