diff --git a/Src/FluentAssertions/AsyncAssertionsExtensions.cs b/Src/FluentAssertions/AsyncAssertionsExtensions.cs
index 4935b97a50..53c6c349b9 100644
--- a/Src/FluentAssertions/AsyncAssertionsExtensions.cs
+++ b/Src/FluentAssertions/AsyncAssertionsExtensions.cs
@@ -18,6 +18,12 @@ public static class AsyncAssertionsExtensions
///
/// Zero or more objects to format using the placeholders in .
///
+ ///
+ /// Please note that this assertion cannot identify whether the previous assertion was successful or not.
+ /// In case it was not successful and it is running within an active
+ /// there is no current result to compare with.
+ /// So, this extension will compare with the default value.
+ ///
public static async Task, T>> WithResult(
this Task, T>> task,
T expected, string because = "", params object[] becauseArgs)
diff --git a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs
index 624184c4df..2c33ff9c58 100644
--- a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs
+++ b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs
@@ -51,13 +51,13 @@ public GenericAsyncFunctionAssertions(Func> subject, IExtractExcep
if (success)
{
bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime);
- Execute.Assertion
- .ForCondition(completesWithinTimeout)
- .BecauseOf(because, becauseArgs)
- .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan);
- }
+ success = Execute.Assertion
+ .ForCondition(completesWithinTimeout)
+ .BecauseOf(because, becauseArgs)
+ .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan);
+ }
- TResult result = task.IsCompleted ? task.Result : default;
+ TResult result = success ? task.Result : default;
return new AndWhichConstraint, TResult>(this, result);
}
diff --git a/Tests/FluentAssertions.Specs/FakeClock.cs b/Tests/FluentAssertions.Specs/FakeClock.cs
index 4d8a95be91..3ecdfd3afd 100644
--- a/Tests/FluentAssertions.Specs/FakeClock.cs
+++ b/Tests/FluentAssertions.Specs/FakeClock.cs
@@ -9,7 +9,7 @@ namespace FluentAssertions.Specs;
/// Implementation of for testing purposes only.
///
///
-/// It allows you to control the "current" time.
+/// It allows you to control the "current" date and time for test purposes.
///
internal class FakeClock : IClock
{
@@ -25,17 +25,26 @@ Task IClock.DelayAsync(TimeSpan delay, CancellationToken cancellationToken)
public ITimer StartTimer() => new TestTimer(() => elapsedTime);
+ ///
+ /// Advances the internal clock.
+ ///
public void Delay(TimeSpan timeToDelay)
{
elapsedTime += timeToDelay;
}
+ ///
+ /// Simulates the completion of the pending delay task.
+ ///
public void Complete()
{
// the value is not relevant
delayTask.SetResult(true);
}
+ ///
+ /// Simulates the completion of the pending delay task after the internal clock has been advanced.
+ ///
public void CompleteAfter(TimeSpan timeSpan)
{
Delay(timeSpan);
diff --git a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs
index 33f930ce5c..07213ba2b6 100644
--- a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs
+++ b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using FluentAssertions.Execution;
using FluentAssertions.Extensions;
@@ -10,6 +9,8 @@
using Xunit;
using Xunit.Sdk;
+using static FluentAssertions.FluentActions;
+
namespace FluentAssertions.Specs.Specialized;
public static class TaskOfTAssertionSpecs
@@ -165,6 +166,28 @@ public async Task When_task_completes_late_it_in_assertion_scope_should_fail()
await action.Should().ThrowAsync();
}
+ [Fact]
+ public async Task When_task_does_not_complete_the_result_extension_does_not_hang()
+ {
+ // Arrange
+ var timer = new FakeClock();
+ var taskFactory = new TaskCompletionSource();
+
+ // Act
+ Func action = () =>
+ {
+ Func> func = () => taskFactory.Task;
+ using var _ = new AssertionScope();
+ return func.Should(timer).CompleteWithinAsync(100.Milliseconds()).WithResult(2);
+ };
+ timer.Complete();
+
+ // Assert
+ var assertionTask = action.Should().ThrowAsync()
+ .WithMessage("Expected*to complete within 100ms.*Expected return*to be 2, but found 0.");
+ await Awaiting(() => assertionTask).Should().CompleteWithinAsync(200.Seconds());
+ }
+
[Fact]
public async Task When_task_consumes_time_in_sync_portion_it_should_fail()
{
diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md
index d3cefc6b07..f7c6f8ee61 100644
--- a/docs/_pages/releases.md
+++ b/docs/_pages/releases.md
@@ -12,6 +12,7 @@ sidebar:
### What's new
### Fixes
+* Fixed hanging of `CompleteWithinAsync` when used with `WithResult` and `AssertionScope` - [#2101](https://github.com/fluentassertions/fluentassertions/pull/2101)
* `BeEquivalentTo` no longer crashes on fields hiding base-class fields - [#1990](https://github.com/fluentassertions/fluentassertions/pull/1990)