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

Add Should().NotThrowAfter assertion for actions #942

Merged
merged 23 commits into from Jan 11, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0e8d5f8
Add wait time to NotThrow
frederik-h Sep 18, 2018
b7a41f8
Rename NotThrow overload with wait time
frederik-h Oct 8, 2018
2da5e3b
Changes requested in review of NotThrowAfter
frederik-h Oct 12, 2018
d83dd87
Minor variable naming and exception message change
frederik-h Oct 13, 2018
b46d678
Use portable method for sleeping in NotThrowAfter
frederik-h Oct 15, 2018
b6a22e1
Fix test name
frederik-h Nov 9, 2018
5d80597
Fix exception message in NotThrowAfter
frederik-h Nov 9, 2018
cdd339e
Revert "Use portable method for sleeping in NotThrowAfter"
frederik-h Nov 19, 2018
fe90171
Disable NotThrowAfter for netstandard < 2.0
frederik-h Nov 19, 2018
a8bdaf9
Merge remote-tracking branch 'upstream/master' into NotThrow-Wait
frederik-h Nov 19, 2018
a9aa045
Minor coding style changes
frederik-h Nov 22, 2018
cc58f1d
Remove code literal names from test names
frederik-h Nov 22, 2018
670d278
Minor code formatting fixes
frederik-h Nov 22, 2018
f1525e5
Add NotThrowAfter to FunctionAssertions
frederik-h Dec 7, 2018
f22fcdb
Add NotThrowAfter for Func<Task>
frederik-h Dec 20, 2018
bb1e1a0
Wrap assertion code of NotThrowAfter in local function
frederik-h Dec 30, 2018
36a850c
Increase wait times in NotThrowAfter tests
frederik-h Jan 1, 2019
e3004c2
Change way NowThrowAfterAsync calls the assertion task
frederik-h Jan 1, 2019
cfc76ca
Add documentation for NotThrowAfter
frederik-h Jan 5, 2019
40258c0
Increase wait time in NotThrowAfter-tests
frederik-h Jan 6, 2019
7da866c
Fixup whitespace
jnyrup Jan 7, 2019
ccfeeca
Merge pull request #1 from jnyrup/NotBeInDescendingOrder
frederik-h Jan 8, 2019
49ec86f
Change NotThrowAfter for Func<T> to return result
frederik-h Jan 9, 2019
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
53 changes: 52 additions & 1 deletion Src/FluentAssertions/Specialized/ActionAssertions.cs
Expand Up @@ -4,7 +4,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

using System.Threading;
using FluentAssertions.Common;
using FluentAssertions.Execution;
using FluentAssertions.Primitives;
Expand Down Expand Up @@ -114,6 +114,57 @@ public void NotThrow(string because = "", params object[] becauseArgs)
}
}

/// <summary>
/// Asserts that the current <see cref="Action"/> stops throwing any exception
/// after a specified amount of time.
/// </summary>
/// <remarks>
/// The <see cref="Action"/> is invoked. If it raises an exception,
/// the invocation is repeated until it either stops raising any exceptions
/// or the specified wait time is exceeded.
/// </remarks>
/// <param name="waitTime">
/// The time after which the <see cref="Action"/> should have stopped throwing any exception.
/// </param>
/// <param name="pollInterval">
/// The time between subsequent invocations of the <see cref="Action"/>.
/// </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 <see cref="because" />.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">Throws if waitTime or pollInterval are negative.</exception>
public void NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs)
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
{
FailIfSubjectIsAsyncVoid();
jnyrup marked this conversation as resolved.
Show resolved Hide resolved

if(waitTime < TimeSpan.Zero)
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
throw new ArgumentOutOfRangeException(nameof(waitTime), $"The value of {nameof(waitTime)} must be positive.");

if(pollInterval < TimeSpan.Zero)
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
throw new ArgumentOutOfRangeException(nameof(pollInterval), $"The value of {nameof(pollInterval)} must be positive.");

var watch = Stopwatch.StartNew();
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
TimeSpan? invocationEndTime = null;
Exception exception = null;

while (invocationEndTime is null || invocationEndTime < waitTime)
{
exception = InvokeSubjectWithInterception();
if (exception is null)
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
return;

Thread.Sleep(pollInterval);
invocationEndTime = watch.Elapsed;
}
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect any exception after {0}{reason}, but found {1}.", waitTime, exception);
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
}

private Exception InvokeSubjectWithInterception()
{
Exception actualException = null;
Expand Down
127 changes: 123 additions & 4 deletions Tests/Shared.Specs/ExceptionAssertionSpecs.cs
@@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Xunit;
using Xunit.Sdk;

using FakeItEasy;

using FluentAssertions.Primitives;
using FluentAssertions.Extensions;

namespace FluentAssertions.Specs
{
Expand Down Expand Up @@ -889,12 +888,132 @@ public void When_no_exception_should_be_thrown_and_none_was_it_should_not_throw(
// Arrange
//-----------------------------------------------------------------------------------------------------------
var foo = A.Fake<IFoo>();

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
foo.Invoking(f => f.Do()).Should().NotThrow();
}

#pragma warning disable CS1998
[Fact]
public void NotThrowAfter_when_subject_is_async_it_should_not_throw()
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
Action someAsyncAction = async () => { };

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () =>
someAsyncAction.Should().NotThrowAfter(1.Milliseconds(), 1.Milliseconds());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<InvalidOperationException>()
.WithMessage("Cannot use action assertions on an async void method.*");
}
#pragma warning restore CS1998

[Fact]
public void NotThrowAfter_when_waitTime_is_negative_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var waitTime = -1.Milliseconds();
var pollInterval = 10.Milliseconds();
Action someAction = () => { };

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () =>
someAction.Should().NotThrowAfter(waitTime, pollInterval);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<ArgumentOutOfRangeException>()
.WithMessage("* value of waitTime must be positive*");
}

[Fact]
public void NotThrowAfter_when_pollInterval_is_negative_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var waitTime = 10.Milliseconds();
var pollInterval = -1.Milliseconds();
Action someAction = () => { };

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () =>
someAction.Should().NotThrowAfter(waitTime, pollInterval);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<ArgumentOutOfRangeException>()
.WithMessage("* value of pollInterval must be positive*");
}

[Fact]
public void NotThrowAfter_when_no_exception_should_be_thrown_after_wait_time_but_it_was_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var watch = Stopwatch.StartNew();
var waitTime = 100.Milliseconds();
var pollInterval = 10.Milliseconds();

Action ThrowLongerThanWaitTime = () =>
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
{
if (watch.Elapsed <= waitTime + (waitTime.Milliseconds / 2).Milliseconds())
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
throw new ArgumentException("An exception was forced");
};

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () =>
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
ThrowLongerThanWaitTime.Should().NotThrowAfter(waitTime, pollInterval, "we passed valid arguments");

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>()
.WithMessage("Did not expect any exception after 0.100s because we passed valid arguments*");
}

[Fact]
public void NotThrowAfter_when_no_exception_should_be_thrown_after_wait_time_and_none_was_it_should_not_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var watch = Stopwatch.StartNew();
var waitTime = 100.Milliseconds();
var pollInterval = 10.Milliseconds();

Action ThrowShorterThanWaitTime = () =>
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
{
if (watch.Elapsed <= (waitTime.Milliseconds / 2).Milliseconds())
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
throw new ArgumentException("An exception was forced");
};

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
ThrowShorterThanWaitTime.Should().NotThrowAfter(waitTime, pollInterval);
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
}

}

#endregion
Expand Down