Skip to content

Commit

Permalink
introduce CompleteWithin(TimeSpan) for Task lambdas to implement flue…
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukas Grützmacher committed Mar 14, 2019
1 parent 6aa2939 commit c50e84c
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Src/FluentAssertions/Specialized/AsyncActionAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,25 @@ async Task assertionTask()
}
}

/// <summary>
/// Asserts that the current <see cref="Func{T}"/> will complete within specified time range.
/// </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 <see cref="because" />.
/// </param>
public void CompleteWithin(TimeSpan timeSpan, string because = "", params object[] becauseArgs)
{
var completed = Task.Run(Subject).Wait(timeSpan);
Execute.Assertion
.ForCondition(completed)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan);
}

private static Exception GetFirstNonAggregateException(Exception exception)
{
Exception nonAggregateException = exception;
Expand Down
27 changes: 27 additions & 0 deletions Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public AsyncFunctionAssertions(Func<Task<T>> subject, IExtractExceptions extract
/// </summary>
public Func<Task<T>> Subject { get; private set; }

/// <summary>
/// Gets the result of the <see cref="Func{Task}"/>.
/// </summary>
public T Result => this.Subject().Result;

/// <summary>
/// Asserts that the current <see cref="Func{Task}"/> throws an exception of type <typeparamref name="TException"/>.
/// </summary>
Expand Down Expand Up @@ -349,6 +354,28 @@ async Task assertionTask()
}
}

/// <summary>
/// Asserts that the current <see cref="Func{T}"/> will complete within specified time range.
/// </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 <see cref="because" />.
/// </param>
public AndWhichConstraint<AsyncFunctionAssertions<T>, T> CompleteWithin(TimeSpan timeSpan, string because = "", params object[] becauseArgs)
{
var task = Task.Run(Subject);
var completed = task.Wait(timeSpan);
Execute.Assertion
.ForCondition(completed)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan);

return new AndWhichConstraint<AsyncFunctionAssertions<T>, T>(this, task.Result);
}

private static Exception GetFirstNonAggregateException(Exception exception)
{
Exception nonAggregateException = exception;
Expand Down
98 changes: 98 additions & 0 deletions Tests/Shared.Specs/AsyncFunctionExceptionAssertionSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,48 @@ public async Task When_subject_throws_subclass_of_expected_async_exception_it_sh
await action.Should().ThrowAsync<ArgumentException>("because {0} should do that", "IFoo.Do");
}

[Fact]
public void When_function_of_task_completes_fast_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var asyncObject = new AsyncClass();

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => asyncObject
.Awaiting(x => x.SucceedAsync())
.Should().CompleteWithin(200.Milliseconds());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().NotThrow();
}

[Fact]
public void When_function_of_task_completes_slow_it_should_fail()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var asyncObject = new AsyncClass();

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => asyncObject
.Awaiting(x => x.DelayAsync(200.Milliseconds()))
.Should().CompleteWithin(10.Milliseconds());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>();
}

[Fact]
public void When_function_of_task_int_in_async_method_throws_the_expected_exception_it_should_succeed()
{
Expand Down Expand Up @@ -231,6 +273,46 @@ public void When_function_of_task_int_in_async_method_throws_not_excepted_except
f.Should().NotThrow<ArgumentNullException>();
}

[Fact]
public void When_function_of_task_int_completes_fast_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var asyncObject = new AsyncClass();

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Func<Task<int>> func = () => asyncObject.ReturnAsync(42);
Action action = () => func.Should().CompleteWithin(200.Milliseconds()).And.Result.Should().Be(42);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().NotThrow();
}

[Fact]
public void When_function_of_task_int_completes_slow_it_should_fail()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var asyncObject = new AsyncClass();

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Func<Task<int>> func = () => asyncObject.SlowReturnAsync(200.Milliseconds(), 42);
Action action = () => func.Should().CompleteWithin(10.Milliseconds());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>();
}

[Fact]
public async Task When_action_throws_subclass_of_expected_async_exact_exception_it_should_throw()
{
Expand Down Expand Up @@ -824,6 +906,22 @@ public async Task SucceedAsync()
await Task.FromResult(42);
}

public Task DelayAsync(TimeSpan timeSpan)
{
return Task.Delay(timeSpan);
}

public async Task<int> ReturnAsync(int value)
{
return await Task.FromResult(value);
}

public async Task<int> SlowReturnAsync(TimeSpan timeSpan, int value)
{
await Task.Delay(timeSpan);
return await Task.FromResult(value);
}

public Task IncompleteTask()
{
return new TaskCompletionSource<bool>().Task;
Expand Down

0 comments on commit c50e84c

Please sign in to comment.