Skip to content

Instantly share code, notes, and snippets.

@martincostello
Last active August 11, 2023 11:05
Show Gist options
  • Save martincostello/5760e8b22d43c967d42f8cf6d7d5060e to your computer and use it in GitHub Desktop.
Save martincostello/5760e8b22d43c967d42f8cf6d7d5060e to your computer and use it in GitHub Desktop.
Mocking Moq for NSubstitute
// Copyright (c) Martin Costello, 2023. All rights reserved.
// Licensed under the Apache 2.0 license.
using System.Linq.Expressions;
using NSubstitute.Core;
using NSubstitute.ExceptionExtensions;
namespace NSubstitute;
/// <summary>
/// This class acts as a bridge from Moq to NSubstitute to allow it to be removed without too much code churn.
/// </summary>
public static class NSubstituteExtensions
{
public static ConfiguredCall ReturnsAsync<T>(this Task<T> value, T returnThis, params T[] returnThese)
{
var task = Task.FromResult(returnThis);
var tasks = Array.Empty<Task<T>>();
if (returnThese?.Length > 0)
{
tasks = returnThese.Select((p) => Task.FromResult(p)).ToArray();
}
return value.Returns(task, tasks);
}
public static T Setup<T>(this Mock<T> value, Action<T> selector)
where T : class
{
selector(value.Object);
return value.Object;
}
public static TResult Setup<T, TResult>(this Mock<T> value, Func<T, TResult> selector)
where T : class
{
return selector(value.Object);
}
public static TResult Setup<T, TResult>(this T value, Func<T, TResult> selector)
{
return selector(value);
}
public static ConfiguredCall Throws<T>(this Task<T> value, Exception exception)
{
return value.Throws((_) => throw exception);
}
public static ConfiguredCall ThrowsAsync<T>(this Task<T> value, Exception exception)
{
return value.Throws((_) => throw exception);
}
public static void Verify<T>(this Mock<T> value, Action<T> selector)
where T : class
{
value.Verify(selector, (Times?)null);
}
public static void Verify<T>(this Mock<T> value, Action<T> selector, Times? times = null)
where T : class
{
if (times is { } number)
{
value.Object.Received(number);
}
else
{
value.Object.Received();
}
value.Setup(selector);
}
public static void Verify<T>(this Mock<T> value, Action<T> selector, Func<Times>? times = null)
where T : class
{
if (times is { } number)
{
value.Object.Received(number());
}
else
{
value.Object.Received();
}
value.Setup(selector);
}
public static void Verify<T, TResult>(this Mock<T> value, Func<T, TResult> selector, Times? times = null)
where T : class
{
if (times is { } number)
{
value.Object.Received(number);
}
else
{
value.Object.Received();
}
value.Setup(selector);
}
}
public readonly struct Times
{
private Times(int value)
{
Value = value;
}
private int Value { get; }
public static implicit operator int(Times times) => times.Value;
public static Times Never() => new(0);
public static Times Once() => new(1);
public static Times Exactly(int count) => new(count);
}
public static class It
{
public static ref T Is<T>(T value) => ref Arg.Is(value);
public static ref T Is<T>(Expression<Predicate<T>> predicate) => ref Arg.Is(predicate);
public static ref T IsAny<T>() => ref Arg.Any<T>();
public static ref T IsNotNull<T>() => ref Arg.Is<T>((p) => p != null);
public static ref string IsRegex(string pattern) => ref Arg.Is<string>((p) => Regex.IsMatch(p, pattern));
}
public static class Mock
{
public static T Of<T>(params object[] constructorArguments)
where T : class
{
return Substitute.For<T>(constructorArguments);
}
}
public class Mock<T>
where T : class
{
public Mock(params object[] constructorArguments)
{
Object = Substitute.For<T>(constructorArguments);
}
public T Object { get; }
public static implicit operator T(Mock<T> mock) => mock.Object;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment