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

Added exception extensions for ValueTask without return type #864

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions src/NSubstitute/Extensions/ExceptionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,66 @@ public static ConfiguredCall ThrowsAsyncForAnyArgs<T>(this ValueTask<T> value, E
/// <returns></returns>
public static ConfiguredCall ThrowsAsyncForAnyArgs<T>(this ValueTask<T> value, Func<CallInfo, Exception> createException) =>
value.ReturnsForAnyArgs(ci => ValueTask.FromException<T>(createException(ci)));

/// <summary>
/// Throw an exception for this call.
/// </summary>
/// <param name="value"></param>
/// <param name="ex">Exception to throw</param>
/// <returns></returns>
public static ConfiguredCall ThrowsAsync(this ValueTask value, Exception ex) =>
value.Returns(_ => ValueTask.FromException(ex));

/// <summary>
/// Throw an exception of the given type for this call.
/// </summary>
/// <typeparam name="TException">Type of exception to throw</typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static ConfiguredCall ThrowsAsync<TException>(this ValueTask value)
where TException : notnull, Exception, new()
{
return value.Returns(_ => ValueTask.FromException(new TException()));
}

/// <summary>
/// Throw an exception for this call, as generated by the specified function.
/// </summary>
/// <param name="value"></param>
/// <param name="createException">Func creating exception object</param>
/// <returns></returns>
public static ConfiguredCall ThrowsAsync(this ValueTask value, Func<CallInfo, Exception> createException) =>
value.Returns(ci => ValueTask.FromException(createException(ci)));

/// <summary>
/// Throws an exception of the given type for this call made with any arguments.
/// </summary>
/// <typeparam name="TException">Type of exception to throw</typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static ConfiguredCall ThrowsAsyncForAnyArgs<TException>(this ValueTask value)
where TException : notnull, Exception, new()
{
return value.ReturnsForAnyArgs(_ => ValueTask.FromException(new TException()));
}

/// <summary>
/// Throw an exception for this call made with any arguments.
/// </summary>
/// <param name="value"></param>
/// <param name="ex">Exception to throw</param>
/// <returns></returns>
public static ConfiguredCall ThrowsAsyncForAnyArgs(this ValueTask value, Exception ex) =>
value.ReturnsForAnyArgs(_ => ValueTask.FromException(ex));

/// <summary>
/// Throws an exception for this call made with any arguments, as generated by the specified function.
/// </summary>
/// <param name="value"></param>
/// <param name="createException">Func creating exception object</param>
/// <returns></returns>
public static ConfiguredCall ThrowsAsyncForAnyArgs(this ValueTask value, Func<CallInfo, Exception> createException) =>
value.ReturnsForAnyArgs(ci => ValueTask.FromException(createException(ci)));
#endif

private static object FromException(object value, Exception exception)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ public interface ISomething
Task<int?> NullableCountAsync();
Task<int?> NullableWithParamsAsync(int i, string s);

ValueTask VoidValueTaskAsync();
ValueTask<int> CountValueTaskAsync();
ValueTask<string> EchoValueTaskAsync(int i);
ValueTask AnythingVoidValueTaskAsync(object stuff);
ValueTask<int> AnythingValueTaskAsync(object stuff);
ValueTask<string> SayValueTaskAsync(string s);
ValueTask<SomeClass> SomeActionValueTaskAsync();
Expand Down
98 changes: 98 additions & 0 deletions tests/NSubstitute.Acceptance.Specs/ThrowingAsyncExceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,88 @@ public void ThrowExceptionCreatedByFactoryFuncForAnyArgs()
AssertFaultedTaskException<int, ArgumentException>(() => _something.AnythingValueTaskAsync(new object()));
}

[Test]
public void ThrowVoidAsyncException()
{
var exception = new Exception();
_something.VoidValueTaskAsync().ThrowsAsync(exception);

AssertFaultedTaskException<Exception>(() => _something.VoidValueTaskAsync());
}

[Test]
public void ThrowVoidAsyncExceptionWithDefaultConstructor()
{
_something.VoidValueTaskAsync().ThrowsAsync<ArgumentException>();

AssertFaultedTaskException<ArgumentException>(() => _something.VoidValueTaskAsync());
}

[Test]
public void ThrowVoidExceptionWithMessage()
{
const string exceptionMessage = "This is exception's message";

_something.VoidValueTaskAsync().ThrowsAsync(new Exception(exceptionMessage));

Exception exceptionThrown = AssertFaultedTaskException<Exception>(() => _something.VoidValueTaskAsync());
ClassicAssert.AreEqual(exceptionMessage, exceptionThrown.Message);
}

[Test]
public void ThrowVoidExceptionWithInnerException()
{
ArgumentException innerException = new ArgumentException();
_something.VoidValueTaskAsync().ThrowsAsync(new Exception("Exception message", innerException));

Exception exceptionThrown = AssertFaultedTaskException<Exception>(() => _something.VoidValueTaskAsync());

ClassicAssert.IsNotNull(exceptionThrown.InnerException);
ClassicAssert.IsInstanceOf<ArgumentException>(exceptionThrown.InnerException);
}

[Test]
public void ThrowVoidExceptionUsingFactoryFunc()
{
_something.AnythingVoidValueTaskAsync("abc").ThrowsAsync(ci => new ArgumentException("Args:" + ci.Args()[0]));

AssertFaultedTaskException<ArgumentException>(() => _something.AnythingVoidValueTaskAsync("abc"));
}

[Test]
public void DoesNotThrowVoidForNonMatchingArgs()
{
_something.AnythingVoidValueTaskAsync(12).ThrowsAsync(new Exception());

AssertFaultedTaskException<Exception>(() => _something.AnythingVoidValueTaskAsync(12));
AssertDoesNotThrow(() => _something.AnythingVoidValueTaskAsync(11));
}

[Test]
public void ThrowVoidExceptionForAnyArgs()
{
_something.AnythingVoidValueTaskAsync(12).ThrowsAsyncForAnyArgs(new Exception());

AssertFaultedTaskException<Exception>(() => _something.AnythingVoidValueTaskAsync(null));
AssertFaultedTaskException<Exception>(() => _something.AnythingVoidValueTaskAsync(12));
}

[Test]
public void ThrowVoidExceptionWithDefaultConstructorForAnyArgs()
{
_something.AnythingVoidValueTaskAsync(12).ThrowsAsyncForAnyArgs<InvalidOperationException>();

AssertFaultedTaskException<InvalidOperationException>(() => _something.AnythingVoidValueTaskAsync(null));
}

[Test]
public void ThrowVoidExceptionCreatedByFactoryFuncForAnyArgs()
{
_something.AnythingVoidValueTaskAsync(null).ThrowsAsyncForAnyArgs(ci => new ArgumentException("Args:" + ci.Args()[0]));

AssertFaultedTaskException<ArgumentException>(() => _something.AnythingVoidValueTaskAsync(new object()));
}

[SetUp]
public void SetUp()
{
Expand Down Expand Up @@ -322,6 +404,22 @@ public static void AssertDoesNotThrow<T>(Func<ValueTask<T>> act)

Assert.That(actual.IsFaulted, Is.False);
}

public static TException AssertFaultedTaskException<TException>(Func<ValueTask> act)
where TException : Exception
{
var actual = act();

Assert.That(actual.IsFaulted, Is.True);
return Assert.CatchAsync<TException>(async () => await actual);
}

public static void AssertDoesNotThrow(Func<ValueTask> act)
{
var actual = act();

Assert.That(actual.IsFaulted, Is.False);
}
}
#endif

Expand Down