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 10 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
56 changes: 55 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,60 @@ public void NotThrow(string because = "", params object[] becauseArgs)
}
}

#if NET45 || NET47 || NETSTANDARD2_0 || NETCOREAPP2_0
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
/// <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 non-negative.");

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 non-negative.");


TimeSpan? invocationEndTime = null;
Exception exception = null;
var watch = Stopwatch.StartNew();

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 exceptions after {0}{reason}, but found {1}.", waitTime, exception);
}
#endif

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

using static FluentAssertions.Extensions.FluentTimeSpanExtensions;

namespace FluentAssertions.Specs
{
public class ExceptionAssertionSpecs
Expand Down Expand Up @@ -863,6 +866,136 @@ public void When_no_exception_should_be_thrown_and_none_was_it_should_not_throw(
//-----------------------------------------------------------------------------------------------------------
foo.Invoking(f => f.Do()).Should().NotThrow();
}

#if NET45 || NET47 || NETSTANDARD2_0 || NETCOREAPP2_0
#pragma warning disable CS1998
[Fact]
public void NotThrowAfter_when_subject_is_async_it_should_throw()
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 non-negative*");
}

[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 non-negative*");
}

[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 = () =>
{
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 exceptions 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 = () =>
{
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
//-----------------------------------------------------------------------------------------------------------

Action act = () => throwShorterThanWaitTime.Should().NotThrowAfter(waitTime, pollInterval);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------

act.Should().NotThrow();
}
#endif // NotThrowAfter tests

}

#endregion
Expand Down