Skip to content

Commit

Permalink
Add throwsAsync for NonGeneric
Browse files Browse the repository at this point in the history
  • Loading branch information
johnthcall committed Feb 17, 2022
1 parent 4d052c1 commit 002ae79
Show file tree
Hide file tree
Showing 2 changed files with 278 additions and 7 deletions.
114 changes: 114 additions & 0 deletions src/Moq/ReturnsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, Task
});
}

/// <summary>
/// Specifies the exception to throw when the asynchronous method is invoked.
/// </summary>
/// <typeparam name="TMock">Mocked type.</typeparam>
/// <param name="mock">Returns verb which represents the mocked type and the valuetask return type</param>
/// <param name="exception">Exception instance to throw.</param>
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, ValueTask> mock, Exception exception) where TMock : class
{
return mock.Returns(() =>
{
var tcs = new TaskCompletionSource<bool>();
tcs.SetException(exception);
return new ValueTask(tcs.Task);
});
}

/// <summary>
/// Specifies the exception to throw when the asynchronous method is invoked.
/// </summary>
Expand Down Expand Up @@ -197,6 +213,24 @@ public static IReturnsResult<TMock> ReturnsAsync<TMock, TResult>(this IReturns<T
return DelayedResult(mock, value, delay);
}

/// <summary>
/// Allows to specify the exception thrown by an asynchronous method.
/// </summary>
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, Task> mock,
Exception exception, TimeSpan delay) where TMock : class
{
return DelayedException(mock, exception, delay);
}

/// <summary>
/// Allows to specify the exception thrown by an asynchronous method.
/// </summary>
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, ValueTask> mock,
Exception exception, TimeSpan delay) where TMock : class
{
return DelayedException(mock, exception, delay);
}

/// <summary>
/// Allows to specify the exception thrown by an asynchronous method.
/// </summary>
Expand All @@ -215,6 +249,28 @@ public static IReturnsResult<TMock> ThrowsAsync<TMock, TResult>(this IReturns<TM
return DelayedException(mock, exception, delay);
}

/// <summary>
/// Allows to specify the exception thrown by an asynchronous method.
/// </summary>
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, Task> mock,
Exception exception, TimeSpan minDelay, TimeSpan maxDelay) where TMock : class
{
var delay = GetDelay(minDelay, maxDelay, Random);

return DelayedException(mock, exception, delay);
}

/// <summary>
/// Allows to specify the exception thrown by an asynchronous method.
/// </summary>
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, ValueTask> mock,
Exception exception, TimeSpan minDelay, TimeSpan maxDelay) where TMock : class
{
var delay = GetDelay(minDelay, maxDelay, Random);

return DelayedException(mock, exception, delay);
}

/// <summary>
/// Allows to specify the exception thrown by an asynchronous method.
/// </summary>
Expand All @@ -237,6 +293,36 @@ public static IReturnsResult<TMock> ThrowsAsync<TMock, TResult>(this IReturns<TM
return DelayedException(mock, exception, delay);
}

/// <summary>
/// <para>Allows to specify the exception thrown by an asynchronous method.</para>
/// <para>Use the <see cref="Random"/> argument to pass in (seeded) random generators used across your unit test.</para>
/// </summary>
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, Task> mock,
Exception exception, TimeSpan minDelay, TimeSpan maxDelay, Random random) where TMock : class
{
if (random == null)
throw new ArgumentNullException(nameof(random));

var delay = GetDelay(minDelay, maxDelay, random);

return DelayedException(mock, exception, delay);
}

/// <summary>
/// <para>Allows to specify the exception thrown by an asynchronous method.</para>
/// <para>Use the <see cref="Random"/> argument to pass in (seeded) random generators used across your unit test.</para>
/// </summary>
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, ValueTask> mock,
Exception exception, TimeSpan minDelay, TimeSpan maxDelay, Random random) where TMock : class
{
if (random == null)
throw new ArgumentNullException(nameof(random));

var delay = GetDelay(minDelay, maxDelay, random);

return DelayedException(mock, exception, delay);
}

/// <summary>
/// <para>Allows to specify the exception thrown by an asynchronous method.</para>
/// <para>Use the <see cref="Random"/> argument to pass in (seeded) random generators used across your unit test.</para>
Expand Down Expand Up @@ -314,6 +400,34 @@ private static IReturnsResult<TMock> DelayedResult<TMock, TResult>(IReturns<TMoc
});
}

private static IReturnsResult<TMock> DelayedException<TMock>(IReturns<TMock, Task> mock,
Exception exception, TimeSpan delay)
where TMock : class
{
Guard.Positive(delay);

return mock.Returns(() =>
{
var tcs = new TaskCompletionSource<bool>();
Task.Delay(delay).ContinueWith(task => tcs.SetException(exception));
return tcs.Task;
});
}

private static IReturnsResult<TMock> DelayedException<TMock>(IReturns<TMock, ValueTask> mock,
Exception exception, TimeSpan delay)
where TMock : class
{
Guard.Positive(delay);

return mock.Returns(() =>
{
var tcs = new TaskCompletionSource<bool>();
Task.Delay(delay).ContinueWith(task => tcs.SetException(exception));
return new ValueTask(tcs.Task);
});
}

private static IReturnsResult<TMock> DelayedException<TMock, TResult>(IReturns<TMock, Task<TResult>> mock,
Exception exception, TimeSpan delay)
where TMock : class
Expand Down
171 changes: 164 additions & 7 deletions tests/Moq.Tests/ReturnsExtensionsFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public interface IAsyncInterface

public interface IValueTaskAsyncInterface
{
ValueTask NoParametersNonGenericValueTaskReturnType();

ValueTask<string> NoParametersRefReturnType();

ValueTask<int> NoParametersValueReturnType();
Expand Down Expand Up @@ -424,6 +426,76 @@ public async Task ThrowsWithDelay()
{
var mock = new Mock<IAsyncInterface>();

mock.Setup(x => x.NoParametersNonGenericTaskReturnType())
.ThrowsAsync(new InvalidOperationException("yikes"), TimeSpan.FromMilliseconds(1));

Func<Task> task = () => mock.Object.NoParametersNonGenericTaskReturnType();

var exception = await Assert.ThrowsAsync<InvalidOperationException>(task);
Assert.Equal("yikes", exception.Message);
}

[Fact]
public async Task ThrowsWithRandomDelay()
{
var mock = new Mock<IAsyncInterface>();

var minDelay = TimeSpan.FromMilliseconds(1);
var maxDelay = TimeSpan.FromMilliseconds(2);

mock
.Setup(x => x.NoParametersNonGenericTaskReturnType())
.ThrowsAsync(new ArithmeticException("yikes"), minDelay, maxDelay);

Func<Task> test = () => mock.Object.NoParametersNonGenericTaskReturnType();

var exception = await Assert.ThrowsAsync<ArithmeticException>(test);
Assert.Equal("yikes", exception.Message);
}

[Fact]
public async Task ThrowsWithRandomDelayAndOwnRandomGenerator()
{
var mock = new Mock<IAsyncInterface>();

var minDelay = TimeSpan.FromMilliseconds(1);
var maxDelay = TimeSpan.FromMilliseconds(2);

mock
.Setup(x => x.NoParametersNonGenericTaskReturnType())
.ThrowsAsync(new ArithmeticException("yikes"), minDelay, maxDelay, new Random());

Func<Task> test = () => mock.Object.NoParametersNonGenericTaskReturnType();

var exception = await Assert.ThrowsAsync<ArithmeticException>(test);
Assert.Equal("yikes", exception.Message);
}

[Fact]
public void ThrowsAsyncWithNullRandomGenerator()
{
var mock = new Mock<IAsyncInterface>();

Action setup = () =>
{
var anyException = new Exception();
var minDelay = TimeSpan.FromMilliseconds(1);
var maxDelay = TimeSpan.FromMilliseconds(2);
mock
.Setup(x => x.NoParametersNonGenericTaskReturnType())
.ThrowsAsync(anyException, minDelay, maxDelay, null);
};

var paramName = Assert.Throws<ArgumentNullException>(setup).ParamName;
Assert.Equal("random", paramName);
}

[Fact]
public async Task ThrowsWithDelayGeneric()
{
var mock = new Mock<IAsyncInterface>();

mock
.Setup(x => x.RefParameterValueReturnType("test"))
.ThrowsAsync(new ArithmeticException("yikes"), TimeSpan.FromMilliseconds(1));
Expand All @@ -435,7 +507,7 @@ public async Task ThrowsWithDelay()
}

[Fact]
public async Task ThrowsWithRandomDelay()
public async Task ThrowsWithRandomDelayGeneric()
{
var mock = new Mock<IAsyncInterface>();

Expand All @@ -453,7 +525,7 @@ public async Task ThrowsWithRandomDelay()
}

[Fact]
public async Task ThrowsWithRandomDelayAndOwnRandomGenerator()
public async Task ThrowsWithRandomDelayAndOwnRandomGeneratorGeneric()
{
var mock = new Mock<IAsyncInterface>();

Expand All @@ -471,7 +543,7 @@ public async Task ThrowsWithRandomDelayAndOwnRandomGenerator()
}

[Fact]
public void ThrowsAsyncWithNullRandomGenerator()
public void ThrowsAsyncWithNullRandomGeneratorGeneric()
{
var mock = new Mock<IAsyncInterface>();

Expand Down Expand Up @@ -674,6 +746,20 @@ public void ValueTaskReturnsAsyncFunc_onEachInvocation_RefReturnTypeLazyEvaluati
Assert.NotSame(firstTask.Result, secondTask.Result);
}

[Fact]
public void ValueTaskThrowsAsync_on_NoParametersNonGenericValueTaskReturnType()
{
var mock = new Mock<IValueTaskAsyncInterface>();
var exception = new InvalidOperationException();
mock.Setup(x => x.NoParametersNonGenericValueTaskReturnType()).ThrowsAsync(exception);

var task = mock.Object.NoParametersNonGenericValueTaskReturnType();

Assert.IsType<ValueTask>(task);
Assert.True(task.IsFaulted);
Assert.Equal(exception, task.AsTask().Exception.InnerException);
}

[Fact]
public void ValueTaskThrowsAsync_on_NoParametersRefReturnType()
{
Expand Down Expand Up @@ -840,7 +926,7 @@ public void ValueTaskReturnsAsyncWithNullRandomGenerator()
}

[Fact]
public async Task ValueTaskThrowsWithDelay()
public async Task GenericValueTaskThrowsWithDelay()
{
var mock = new Mock<IValueTaskAsyncInterface>();

Expand All @@ -855,7 +941,7 @@ public async Task ValueTaskThrowsWithDelay()
}

[Fact]
public async Task ValueTaskThrowsWithRandomDelay()
public async Task GenericValueTaskThrowsWithRandomDelay()
{
var mock = new Mock<IValueTaskAsyncInterface>();

Expand All @@ -873,7 +959,7 @@ public async Task ValueTaskThrowsWithRandomDelay()
}

[Fact]
public async Task ValueTaskThrowsWithRandomDelayAndOwnRandomGenerator()
public async Task GenericValueTaskThrowsWithRandomDelayAndOwnRandomGenerator()
{
var mock = new Mock<IValueTaskAsyncInterface>();

Expand All @@ -891,7 +977,7 @@ public async Task ValueTaskThrowsWithRandomDelayAndOwnRandomGenerator()
}

[Fact]
public void ValueTaskThrowsAsyncWithNullRandomGenerator()
public void GenericValueTaskThrowsAsyncWithNullRandomGenerator()
{
var mock = new Mock<IValueTaskAsyncInterface>();

Expand All @@ -910,6 +996,77 @@ public void ValueTaskThrowsAsyncWithNullRandomGenerator()
Assert.Equal("random", paramName);
}

[Fact]
public async Task ValueTaskThrowsWithDelay()
{
var mock = new Mock<IValueTaskAsyncInterface>();

mock
.Setup(x => x.NoParametersNonGenericValueTaskReturnType())
.ThrowsAsync(new ArithmeticException("yikes"), TimeSpan.FromMilliseconds(1));

Func<ValueTask> test = () => mock.Object.NoParametersNonGenericValueTaskReturnType();

var exception = await Assert.ThrowsAsync<ArithmeticException>(() => test().AsTask());
Assert.Equal("yikes", exception.Message);
}

[Fact]
public async Task ValueTaskThrowsWithRandomDelay()
{
var mock = new Mock<IValueTaskAsyncInterface>();

var minDelay = TimeSpan.FromMilliseconds(1);
var maxDelay = TimeSpan.FromMilliseconds(2);

mock
.Setup(x => x.NoParametersNonGenericValueTaskReturnType())
.ThrowsAsync(new ArithmeticException("yikes"), minDelay, maxDelay);

Func<ValueTask> test = () => mock.Object.NoParametersNonGenericValueTaskReturnType();

var exception = await Assert.ThrowsAsync<ArithmeticException>(() => test().AsTask());
Assert.Equal("yikes", exception.Message);
}

[Fact]
public async Task ValueTaskThrowsWithRandomDelayAndOwnRandomGenerator()
{
var mock = new Mock<IValueTaskAsyncInterface>();

var minDelay = TimeSpan.FromMilliseconds(1);
var maxDelay = TimeSpan.FromMilliseconds(2);

mock
.Setup(x => x.NoParametersNonGenericValueTaskReturnType())
.ThrowsAsync(new ArithmeticException("yikes"), minDelay, maxDelay, new Random());

Func<ValueTask> test = () => mock.Object.NoParametersNonGenericValueTaskReturnType();

var exception = await Assert.ThrowsAsync<ArithmeticException>(() => test().AsTask());
Assert.Equal("yikes", exception.Message);
}

[Fact]
public void ValueTaskThrowsAsyncWithNullRandomGenerator()
{
var mock = new Mock<IValueTaskAsyncInterface>();

Action setup = () =>
{
var anyException = new Exception();
var minDelay = TimeSpan.FromMilliseconds(1);
var maxDelay = TimeSpan.FromMilliseconds(2);
mock
.Setup(x => x.NoParametersNonGenericValueTaskReturnType())
.ThrowsAsync(anyException, minDelay, maxDelay, null);
};

var paramName = Assert.Throws<ArgumentNullException>(setup).ParamName;
Assert.Equal("random", paramName);
}

[Fact]
public async void No_parameters_object_return_type__ReturnsAsync_null__returns_completed_Task_with_null_result()
{
Expand Down

0 comments on commit 002ae79

Please sign in to comment.