diff --git a/Src/FluentAssertions/AssertionExtensions.Actions.cs b/Src/FluentAssertions/AssertionExtensions.Actions.cs
index 1258b18ae1..8b33fd771b 100644
--- a/Src/FluentAssertions/AssertionExtensions.Actions.cs
+++ b/Src/FluentAssertions/AssertionExtensions.Actions.cs
@@ -64,6 +64,32 @@ public static partial class AssertionExtensions
return exceptionAssertions;
}
+ ///
+ /// Asserts that the subject throws the exact exception (and not a derived exception type).
+ ///
+ /// A reference to the method or property.
+ ///
+ /// The type of the exception it should throw.
+ ///
+ ///
+ /// A formatted phrase explaining why the assertion should be satisfied. If the phrase does not
+ /// start with the word because, it is prepended to the message.
+ ///
+ ///
+ /// Zero or more values to use for filling in any compatible placeholders.
+ ///
+ ///
+ /// Returns an object that allows asserting additional members of the thrown exception.
+ ///
+ public static async Task> ThrowExactlyAsync(this AsyncFunctionAssertions asyncActionAssertions, string because = "",
+ params object[] becauseArgs)
+ where TException : Exception
+ {
+ var exceptionAssertions = await asyncActionAssertions.ThrowAsync(because, becauseArgs);
+ exceptionAssertions.Which.GetType().Should().Be(because, becauseArgs);
+ return exceptionAssertions;
+ }
+
private class AggregateExceptionExtractor : IExtractExceptions
{
public IEnumerable OfType(Exception actualException)
diff --git a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
index 65a2ae3fc7..c045ff9b9b 100644
--- a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
+++ b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
@@ -40,19 +40,24 @@ public ExceptionAssertions Throw(string because = "", pa
where TException : Exception
{
Exception exception = InvokeSubjectWithInterception();
- var exceptions = extractor.OfType(exception);
-
- Execute.Assertion
- .ForCondition(exception != null)
- .BecauseOf(because, becauseArgs)
- .FailWith("Expected {0}{reason}, but no exception was thrown.", typeof(TException));
-
- Execute.Assertion
- .ForCondition(exceptions.Any())
- .BecauseOf(because, becauseArgs)
- .FailWith("Expected {0}{reason}, but found {1}.", typeof(TException), exception);
+ return Throw(exception, because, becauseArgs);
+ }
- return new ExceptionAssertions(exceptions);
+ ///
+ /// Asserts that the current throws an exception of type .
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public async Task> ThrowAsync(string because = "", params object[] becauseArgs)
+ where TException : Exception
+ {
+ Exception exception = await InvokeSubjectWithInterceptionAsync();
+ return Throw(exception, because, becauseArgs);
}
///
@@ -73,12 +78,29 @@ public void NotThrow(string because = "", params object[] becauseArgs)
}
catch (Exception exception)
{
- Exception nonAggregateException = GetFirstNonAggregateException(exception);
+ NotThrow(exception, because, becauseArgs);
+ }
+ }
- Execute.Assertion
- .BecauseOf(because, becauseArgs)
- .FailWith("Did not expect any exception{reason}, but found a {0} with message {1}.",
- nonAggregateException.GetType(), nonAggregateException.ToString());
+ ///
+ /// Asserts that the current does not throw any exception.
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public async Task NotThrowAsync(string because = "", params object[] becauseArgs)
+ {
+ try
+ {
+ await Task.Run(Subject);
+ }
+ catch (Exception exception)
+ {
+ NotThrow(exception, because, becauseArgs);
}
}
@@ -101,16 +123,54 @@ public void NotThrow(string because = "", params object[] becauseArg
}
catch (Exception exception)
{
- Exception nonAggregateException = GetFirstNonAggregateException(exception);
-
- if (nonAggregateException != null)
- {
- Execute.Assertion
- .ForCondition(!(nonAggregateException is TException))
- .BecauseOf(because, becauseArgs)
- .FailWith("Did not expect {0}{reason}, but found one with message {1}.",
- typeof(TException), nonAggregateException.ToString());
- }
+ NotThrow(exception, because, becauseArgs);
+ }
+ }
+
+ ///
+ /// Asserts that the current does not throw an exception of type .
+ ///
+ ///
+ /// A formatted phrase as is supported by explaining why the assertion
+ /// is needed. If the phrase does not start with the word because, it is prepended automatically.
+ ///
+ ///
+ /// Zero or more objects to format using the placeholders in .
+ ///
+ public async Task NotThrowAsync(string because = "", params object[] becauseArgs)
+ where TException : Exception
+ {
+ try
+ {
+ await Task.Run(Subject);
+ }
+ catch (Exception exception)
+ {
+ NotThrow(exception, because, becauseArgs);
+ }
+ }
+
+ private static void NotThrow(Exception exception, string because, object[] becauseArgs)
+ {
+ Exception nonAggregateException = GetFirstNonAggregateException(exception);
+
+ Execute.Assertion
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Did not expect any exception{reason}, but found a {0} with message {1}.",
+ nonAggregateException.GetType(), nonAggregateException.ToString());
+ }
+
+ private static void NotThrow(Exception exception, string because, object[] becauseArgs) where TException : Exception
+ {
+ Exception nonAggregateException = GetFirstNonAggregateException(exception);
+
+ if (nonAggregateException != null)
+ {
+ Execute.Assertion
+ .ForCondition(!(nonAggregateException is TException))
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Did not expect {0}{reason}, but found one with message {1}.",
+ typeof(TException), nonAggregateException.ToString());
}
}
@@ -125,28 +185,61 @@ private static Exception GetFirstNonAggregateException(Exception exception)
return nonAggregateException;
}
- private Exception InvokeSubjectWithInterception()
+ private ExceptionAssertions Throw(Exception exception, string because, object[] becauseArgs)
+ where TException : Exception
{
- Exception actualException = null;
+ var exceptions = extractor.OfType(exception);
+
+ Execute.Assertion
+ .ForCondition(exception != null)
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Expected {0}{reason}, but no exception was thrown.", typeof(TException));
+
+ Execute.Assertion
+ .ForCondition(exceptions.Any())
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Expected {0}{reason}, but found {1}.", typeof(TException), exception);
+ return new ExceptionAssertions(exceptions);
+ }
+
+ private Exception InvokeSubjectWithInterception()
+ {
try
{
Task.Run(Subject).Wait();
}
catch (Exception exception)
{
- var ar = exception as AggregateException;
- if (ar?.InnerException is AggregateException)
- {
- actualException = ar.InnerException;
- }
- else
- {
- actualException = exception;
- }
+ return InterceptException(exception);
+ }
+
+ return null;
+ }
+
+ private async Task InvokeSubjectWithInterceptionAsync()
+ {
+ try
+ {
+ await Task.Run(Subject);
+ }
+ catch (Exception exception)
+ {
+ return InterceptException(exception);
+ }
+
+ return null;
+ }
+
+ private Exception InterceptException(Exception exception)
+ {
+ var ar = exception as AggregateException;
+ if (ar?.InnerException is AggregateException)
+ {
+ return ar.InnerException;
}
- return actualException;
+ return exception;
}
}
}
diff --git a/Tests/Shared.Specs/AsyncFunctionExceptionAssertionSpecs.cs b/Tests/Shared.Specs/AsyncFunctionExceptionAssertionSpecs.cs
index 3d5ac85572..09ed56561e 100644
--- a/Tests/Shared.Specs/AsyncFunctionExceptionAssertionSpecs.cs
+++ b/Tests/Shared.Specs/AsyncFunctionExceptionAssertionSpecs.cs
@@ -67,6 +67,26 @@ public void When_async_method_throws_expected_exception_it_should_succeed()
action.Should().NotThrow();
}
+ [Fact]
+ public async void When_async_method_throws_async_expected_exception_it_should_succeed()
+ {
+ //-----------------------------------------------------------------------------------------------------------
+ // Arrange
+ //-----------------------------------------------------------------------------------------------------------
+ var asyncObject = new AsyncClass();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ Func action = async () => await asyncObject.ThrowAsync();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ await action.Should().ThrowAsync();
+
+ }
+
[Fact]
public void When_async_method_does_not_throw_expected_exception_it_should_fail()
{
@@ -132,6 +152,83 @@ public void When_async_method_does_not_throw_exception_and_that_was_expected_it_
action.Should().NotThrow();
}
+ [Fact]
+ public async Task When_async_method_does_not_throw_async_exception_and_that_was_expected_it_should_succeed()
+ {
+ //-----------------------------------------------------------------------------------------------------------
+ // Arrange
+ //-----------------------------------------------------------------------------------------------------------
+ var asyncObject = new AsyncClass();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ Func action = async () => await asyncObject.SucceedAsync();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ await action.Should().NotThrowAsync();
+ }
+
+ [Fact]
+ public async Task When_subject_throws_subclass_of_expected_async_exception_it_should_succeed()
+ {
+ //-----------------------------------------------------------------------------------------------------------
+ // Arrange
+ //-----------------------------------------------------------------------------------------------------------
+ var asyncObject = new AsyncClass();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ Func action = async () => await asyncObject.ThrowAsync();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ await action.Should().ThrowAsync("because {0} should do that", "IFoo.Do");
+ }
+
+ [Fact]
+ public async Task When_subject_throws_subclass_of_expected_async_exact_exception_it_should_throw()
+ {
+ //-----------------------------------------------------------------------------------------------------------
+ // Arrange
+ //-----------------------------------------------------------------------------------------------------------
+ var asyncObject = new AsyncClass();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ Func action = async () => await asyncObject.ThrowAsync();
+ Func testAction = async () => await action.Should().ThrowExactlyAsync("ABCDE");
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ (await testAction.Should().ThrowAsync()).WithMessage("*ArgumentException*ABCDE*ArgumentNullException*");
+ }
+
+ [Fact]
+ public async Task When_subject_throws_expected_async_exact_exception_it_should_succeed()
+ {
+ //-----------------------------------------------------------------------------------------------------------
+ // Arrange
+ //-----------------------------------------------------------------------------------------------------------
+ var asyncObject = new AsyncClass();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ Func action = async () => await asyncObject.ThrowAsync();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ await action.Should().ThrowExactlyAsync("because {0} should do that", "IFoo.Do");
+ }
+
[Fact]
public void When_async_method_throws_exception_and_no_exception_was_expected_it_should_fail()
{
@@ -175,6 +272,26 @@ public void When_async_method_throws_exception_and_expected_not_to_throw_another
action.Should().NotThrow();
}
+ [Fact]
+ public async Task When_async_method_throws_exception_and_expected_not_to_throw_async_another_one_it_should_succeed()
+ {
+ //-----------------------------------------------------------------------------------------------------------
+ // Arrange
+ //-----------------------------------------------------------------------------------------------------------
+ var asyncObject = new AsyncClass();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ Func action = async () => await asyncObject.ThrowAsync();
+
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ await action.Should().NotThrowAsync();
+ }
+
[Fact]
public void When_async_method_succeeds_and_expected_not_to_throw_particular_exception_it_should_succeed()
{
diff --git a/docs/_pages/documentation.md b/docs/_pages/documentation.md
index 080dced9e9..9f873b0edc 100644
--- a/docs/_pages/documentation.md
+++ b/docs/_pages/documentation.md
@@ -714,6 +714,8 @@ Talking about the `async` keyword, you can also verify that an asynchronously ex
```csharp
Func act = async () => { await asyncObject.ThrowAsync(); };
+await act.Should().ThrowAsync();
+await act.Should().NotThrowAsync();
act.Should().Throw();
act.Should().NotThrow();
```