Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4322 from uecasm/that-async
ThatAsync and MultipleAsync
- Loading branch information
Showing
4 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt | ||
|
||
#nullable enable | ||
|
||
using System.Runtime.ExceptionServices; | ||
using System.Threading.Tasks; | ||
using System; | ||
using NUnit.Framework.Constraints; | ||
|
||
namespace NUnit.Framework | ||
{ | ||
public abstract partial class Assert | ||
{ | ||
#region Assert.ThatAsync | ||
|
||
/// <summary> | ||
/// Apply a constraint to an async delegate. Returns without throwing an exception when inside a multiple assert block. | ||
/// </summary> | ||
/// <param name="code">An AsyncTestDelegate to be executed</param> | ||
/// <param name="constraint">A Constraint expression to be applied</param> | ||
/// <returns>Awaitable.</returns> | ||
public static Task ThatAsync(AsyncTestDelegate code, IResolveConstraint constraint) | ||
{ | ||
return ThatAsync(code, constraint, null, null); | ||
} | ||
|
||
/// <summary> | ||
/// Apply a constraint to an async delegate. Returns without throwing an exception when inside a multiple assert block. | ||
/// </summary> | ||
/// <param name="code">An AsyncTestDelegate to be executed</param> | ||
/// <param name="constraint">A Constraint expression to be applied</param> | ||
/// <param name="message">The message that will be displayed on failure</param> | ||
/// <param name="args">Arguments to be used in formatting the message</param> | ||
/// <returns>Awaitable.</returns> | ||
public static async Task ThatAsync(AsyncTestDelegate code, IResolveConstraint constraint, string? message, params object?[]? args) | ||
{ | ||
try | ||
{ | ||
await code(); | ||
Assert.That(() => { }, constraint, message, args); | ||
} | ||
catch (Exception ex) | ||
{ | ||
var edi = ExceptionDispatchInfo.Capture(ex); | ||
Assert.That(() => edi.Throw(), constraint, message, args); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Apply a constraint to an async delegate. Returns without throwing an exception when inside a multiple assert block. | ||
/// </summary> | ||
/// <param name="code">An async method to be executed</param> | ||
/// <param name="constraint">A Constraint expression to be applied</param> | ||
/// <returns>Awaitable.</returns> | ||
public static Task ThatAsync<T>(Func<Task<T>> code, IResolveConstraint constraint) | ||
{ | ||
return ThatAsync(code, constraint, null, null); | ||
} | ||
|
||
/// <summary> | ||
/// Apply a constraint to an async delegate. Returns without throwing an exception when inside a multiple assert block. | ||
/// </summary> | ||
/// <param name="code">An async method to be executed</param> | ||
/// <param name="constraint">A Constraint expression to be applied</param> | ||
/// <param name="message">The message that will be displayed on failure</param> | ||
/// <param name="args">Arguments to be used in formatting the message</param> | ||
/// <returns>Awaitable.</returns> | ||
public static async Task ThatAsync<T>(Func<Task<T>> code, IResolveConstraint constraint, string? message, params object?[]? args) | ||
{ | ||
try | ||
{ | ||
var result = await code(); | ||
Assert.That(() => result, constraint, message, args); | ||
} | ||
catch (Exception ex) | ||
{ | ||
var edi = ExceptionDispatchInfo.Capture(ex); | ||
Assert.That(() => edi.Throw(), constraint, message, args); | ||
} | ||
} | ||
|
||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
src/NUnitFramework/tests/Assertions/AssertThatAsyncTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt | ||
|
||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using NUnit.Framework.Internal; | ||
|
||
namespace NUnit.Framework.Assertions | ||
{ | ||
[TestFixture] | ||
public class AssertThatAsyncTests | ||
{ | ||
[Test] | ||
public async Task AssertionPasses_CompletedTask_ThrowsNothing() | ||
{ | ||
await Assert.ThatAsync(() => Task.CompletedTask, Throws.Nothing); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_CanceledTask_ThrowsCanceled() | ||
{ | ||
var cancel = new CancellationTokenSource(); | ||
cancel.Cancel(); | ||
|
||
await Assert.ThatAsync(() => Task.FromCanceled(cancel.Token), | ||
Throws.InstanceOf<TaskCanceledException>() | ||
.With.Property(nameof(TaskCanceledException.CancellationToken)).EqualTo(cancel.Token)); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_FaultedTask_ThrowsMatchingException() | ||
{ | ||
await Assert.ThatAsync(() => Task.FromException(new InvalidOperationException()), Throws.InvalidOperationException); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_CompletedTask_ThrowsNothingWithMessage() | ||
{ | ||
await Assert.ThatAsync(() => Task.CompletedTask, Throws.Nothing, "Success"); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_CanceledTask_ThrowsCanceledWithMessage() | ||
{ | ||
var cancel = new CancellationTokenSource(); | ||
cancel.Cancel(); | ||
|
||
await Assert.ThatAsync(() => Task.FromCanceled(cancel.Token), | ||
Throws.InstanceOf<TaskCanceledException>() | ||
.With.Property(nameof(TaskCanceledException.CancellationToken)).EqualTo(cancel.Token), | ||
"Cancelled"); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_FaultedTask_ThrowsMatchingExceptionWithMessage() | ||
{ | ||
await Assert.ThatAsync(() => Task.FromException(new InvalidOperationException()), Throws.InvalidOperationException, "Faulted"); | ||
} | ||
|
||
[Test] | ||
public async Task Failure_CompletedTask_ThrowsException() | ||
{ | ||
await AssertAssertionFailsAsync(async () => await Assert.ThatAsync(() => Task.CompletedTask, Throws.InvalidOperationException)); | ||
} | ||
|
||
[Test] | ||
public async Task Failure_CanceledTask_ThrowsNothing() | ||
{ | ||
var cancel = new CancellationTokenSource(); | ||
cancel.Cancel(); | ||
|
||
await AssertAssertionFailsAsync(async () => await Assert.ThatAsync(() => Task.FromCanceled(cancel.Token), Throws.Nothing)); | ||
} | ||
|
||
[Test] | ||
public async Task Failure_FaultedTask_ThrowsNothing() | ||
{ | ||
await AssertAssertionFailsAsync(async () => await Assert.ThatAsync(() => Task.FromException(new InvalidOperationException()), Throws.Nothing)); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_CompletedTaskWithResult_ThrowsNothing() | ||
{ | ||
await Assert.ThatAsync(() => Task.FromResult(42), Throws.Nothing); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_CompletedTaskWithResult_EqualsResult() | ||
{ | ||
await Assert.ThatAsync(() => Task.FromResult(42), Is.EqualTo(42)); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_CanceledTaskWithResult_ThrowsCanceled() | ||
{ | ||
var cancel = new CancellationTokenSource(); | ||
cancel.Cancel(); | ||
|
||
await Assert.ThatAsync(() => Task.FromCanceled<int>(cancel.Token), | ||
Throws.InstanceOf<TaskCanceledException>() | ||
.With.Property(nameof(TaskCanceledException.CancellationToken)).EqualTo(cancel.Token)); | ||
} | ||
|
||
[Test] | ||
public async Task AssertionPasses_FaultedTaskWithResult_ThrowsMatchingException() | ||
{ | ||
await Assert.ThatAsync(() => Task.FromException<int>(new InvalidOperationException()), Throws.InvalidOperationException); | ||
} | ||
|
||
private static async Task AssertAssertionFailsAsync(Func<Task> assertion) | ||
{ | ||
await Assert.ThatAsync( | ||
async () => | ||
{ | ||
using (new TestExecutionContext.IsolatedContext()) | ||
{ | ||
await assertion(); | ||
} | ||
}, | ||
Throws.InstanceOf<AssertionException>()); | ||
} | ||
} | ||
} |