Created
August 31, 2017 16:37
-
-
Save AdamDotNet/9394db2f89cada7c83bd6fc55a6c7695 to your computer and use it in GitHub Desktop.
Moq ValueTask ReturnsExtensions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Moq.Language; | |
using Moq.Language.Flow; | |
using System; | |
using System.Threading.Tasks; | |
/* See the System.Threading.Tasks.Extensions NuGet */ | |
namespace Moq | |
{ | |
/// <summary> | |
/// Defines async extension methods on IReturns. | |
/// </summary> | |
public static class ReturnsExtensions | |
{ | |
/// <summary> | |
/// Specifies the value to return from an asynchronous method. | |
/// </summary> | |
/// <typeparam name="TMock">Mocked type.</typeparam> | |
/// <typeparam name="TResult">Type of the return value.</typeparam> | |
/// <param name="mock">Returns verb which represents the mocked type and the task of return type</param> | |
/// <param name="value">The value to return, or <see longword="null"/>.</param> | |
public static IReturnsResult<TMock> ReturnsAsync<TMock, TResult>(this IReturns<TMock, ValueTask<TResult>> mock, TResult value) where TMock : class | |
{ | |
return mock.ReturnsAsync(() => value); | |
} | |
/// <summary> | |
/// Specifies a function that will calculate the value to return from the asynchronous method. | |
/// </summary> | |
/// <typeparam name="TMock">Mocked type.</typeparam> | |
/// <typeparam name="TResult">Type of the return value.</typeparam> | |
/// <param name="mock">Returns verb which represents the mocked type and the task of return type</param> | |
/// <param name="valueFunction">The function that will calculate the return value.</param> | |
public static IReturnsResult<TMock> ReturnsAsync<TMock, TResult>(this IReturns<TMock, ValueTask<TResult>> mock, Func<TResult> valueFunction) where TMock : class | |
{ | |
return mock.Returns(() => new ValueTask<TResult>(valueFunction())); | |
} | |
/// <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 task return type</param> | |
/// <param name="exception">Exception instance to throw.</param> | |
public static IReturnsResult<TMock> ThrowsAsync<TMock>(this IReturns<TMock, Task> mock, Exception exception) where TMock : class | |
{ | |
var tcs = new TaskCompletionSource<bool>(); | |
tcs.SetException(exception); | |
return mock.Returns(tcs.Task); | |
} | |
/// <summary> | |
/// Specifies the exception to throw when the asynchronous method is invoked. | |
/// </summary> | |
/// <typeparam name="TMock">Mocked type.</typeparam> | |
/// <typeparam name="TResult">Type of the return value.</typeparam> | |
/// <param name="mock">Returns verb which represents the mocked type and the task of return type</param> | |
/// <param name="exception">Exception instance to throw.</param> | |
public static IReturnsResult<TMock> ThrowsAsync<TMock, TResult>(this IReturns<TMock, ValueTask<TResult>> mock, Exception exception) where TMock : class | |
{ | |
var tcs = new TaskCompletionSource<TResult>(); | |
tcs.SetException(exception); | |
return mock.Returns(new ValueTask<TResult>(tcs.Task)); | |
} | |
private static readonly Random random = new Random(); | |
/// <summary> | |
/// Allows to specify the delayed return value of an asynchronous method. | |
/// </summary> | |
public static IReturnsResult<TMock> ReturnsAsync<TMock, TResult>(this IReturns<TMock, ValueTask<TResult>> mock, | |
TResult value, TimeSpan delay) where TMock : class | |
{ | |
return DelayedResult(mock, value, delay); | |
} | |
/// <summary> | |
/// Allows to specify the delayed return value of an asynchronous method. | |
/// </summary> | |
public static IReturnsResult<TMock> ReturnsAsync<TMock, TResult>(this IReturns<TMock, ValueTask<TResult>> mock, | |
TResult value, TimeSpan minDelay, TimeSpan maxDelay) where TMock : class | |
{ | |
var delay = GetDelay(minDelay, maxDelay, random); | |
return DelayedResult(mock, value, delay); | |
} | |
/// <summary> | |
/// <para>Allows to specify the delayed return value of 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> ReturnsAsync<TMock, TResult>(this IReturns<TMock, ValueTask<TResult>> mock, | |
TResult value, TimeSpan minDelay, TimeSpan maxDelay, Random random) where TMock : class | |
{ | |
if (random == null) | |
throw new ArgumentNullException(nameof(random)); | |
var delay = GetDelay(minDelay, maxDelay, random); | |
return DelayedResult(mock, value, delay); | |
} | |
/// <summary> | |
/// Allows to specify the exception thrown by an asynchronous method. | |
/// </summary> | |
public static IReturnsResult<TMock> ThrowsAsync<TMock, TResult>(this IReturns<TMock, ValueTask<TResult>> 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, TResult>(this IReturns<TMock, ValueTask<TResult>> mock, | |
Exception exception, TimeSpan minDelay, TimeSpan maxDelay) where TMock : class | |
{ | |
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, TResult>(this IReturns<TMock, ValueTask<TResult>> 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); | |
} | |
private static TimeSpan GetDelay(TimeSpan minDelay, TimeSpan maxDelay, Random random) | |
{ | |
if (!(minDelay < maxDelay)) | |
throw new ArgumentException(/*Resources.MinDelayMustBeLessThanMaxDelay*/); | |
var min = (int)minDelay.Ticks; | |
var max = (int)maxDelay.Ticks; | |
return new TimeSpan(random.Next(min, max)); | |
} | |
private static void GuardPositiveDelay(TimeSpan delay) | |
{ | |
if (!(delay > TimeSpan.Zero)) | |
throw new ArgumentException(/*Resources.DelaysMustBeGreaterThanZero*/); | |
} | |
private static IReturnsResult<TMock> DelayedResult<TMock, TResult>(IReturns<TMock, ValueTask<TResult>> mock, | |
TResult value, TimeSpan delay) | |
where TMock : class | |
{ | |
GuardPositiveDelay(delay); | |
var tcs = new TaskCompletionSource<TResult>(); | |
Task.Delay(delay).ContinueWith(t => tcs.SetResult(value)); | |
return mock.Returns(new ValueTask<TResult>(tcs.Task)); | |
} | |
private static IReturnsResult<TMock> DelayedException<TMock, TResult>(IReturns<TMock, ValueTask<TResult>> mock, | |
Exception exception, TimeSpan delay) | |
where TMock : class | |
{ | |
GuardPositiveDelay(delay); | |
var tcs = new TaskCompletionSource<TResult>(); | |
Task.Delay(delay).ContinueWith(t => tcs.SetException(exception)); | |
return mock.Returns(new ValueTask<TResult>(tcs.Task)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment