Skip to content

Instantly share code, notes, and snippets.

@AdrienPoupa
Forked from jeremybeavon/MoqExtensions.cs
Created June 20, 2017 09:11
Show Gist options
  • Save AdrienPoupa/9d9cb6ea5aa4be6cdcf2a6ed1e0eb1f7 to your computer and use it in GitHub Desktop.
Save AdrienPoupa/9d9cb6ea5aa4be6cdcf2a6ed1e0eb1f7 to your computer and use it in GitHub Desktop.
Moq support for ref and out callbacks
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Moq;
using Moq.Language;
using Moq.Language.Flow;
public static class MoqExtensions
{
private const BindingFlags privateInstanceFlags = BindingFlags.Instance | BindingFlags.NonPublic;
public static ICallbackResult OutCallback(this ICallback mock, Delegate callback)
{
RefCallbackHelper.SetCallback(mock, callback);
return (ICallbackResult)mock;
}
public static ICallbackResult RefCallback(this ICallback mock, Delegate callback)
{
RefCallbackHelper.SetCallback(mock, callback);
return (ICallbackResult)mock;
}
public static IReturnsThrows<TMock, TResult> OutCallback<TMock, TResult>(this ICallback<TMock, TResult> mock, Delegate callback)
where TMock : class
{
RefCallbackHelper.SetCallback(mock, callback);
return (IReturnsThrows<TMock, TResult>)mock;
}
public static IReturnsThrows<TMock, TResult> RefCallback<TMock, TResult>(this ICallback<TMock, TResult> mock, Delegate callback)
where TMock : class
{
RefCallbackHelper.SetCallback(mock, callback);
return (IReturnsThrows<TMock, TResult>)mock;
}
private static class RefCallbackHelper
{
private static readonly Action<object, Delegate> setCallbackWithoutArgumentsAction = CreateSetCallbackWithoutArgumentsAction();
public static void SetCallback(object mock, Delegate callback)
{
setCallbackWithoutArgumentsAction(mock, callback);
}
private static Action<object, Delegate> CreateSetCallbackWithoutArgumentsAction()
{
ParameterExpression mockParameter = Expression.Parameter(typeof(object));
ParameterExpression actionParameter = Expression.Parameter(typeof(Delegate));
Type type = typeof(Mock<>).Assembly.GetType("Moq.MethodCall", true);
MethodInfo method = type.GetMethod("SetCallbackWithArguments", privateInstanceFlags);
if (method == null)
throw new InvalidOperationException();
return Expression.Lambda<Action<object, Delegate>>(
Expression.Call(Expression.Convert(mockParameter, type), method, actionParameter),
mockParameter,
actionParameter).Compile();
}
}
}
using Moq;
using NUnit.Framework;
[TestFixture]
public sealed class MoqExtensionsTests : MockTest
{
private delegate void TryGetAction(out int value);
private delegate void IncrementAction(ref int value);
[Test]
public void Test_OutCallback_SetsValueWhenReturnTypeIsVoid()
{
// Arrange
Mock<IOutActionTest> mockOutActionTest = CreateMock<IOutActionTest>();
int tempValue;
mockOutActionTest.Setup(test => test.TryGet(out tempValue)).OutCallback(new TryGetAction((out int value) => value = 10));
// Act
int actualValue;
mockOutActionTest.Object.TryGet(out actualValue);
// Assert
Assert.That(actualValue, Is.EqualTo(10));
}
[Test]
public void Test_RefCallback_IncrementsValueWhenReturnTypeIsVoid()
{
// Arrange
Mock<IRefActionTest> mockOutActionTest = CreateMock<IRefActionTest>();
int tempValue = 1;
mockOutActionTest.SetupIgnoreArguments(test => test.Increment(ref tempValue)).RefCallback(new IncrementAction((ref int value) => value++));
// Act
int actualValue = 10;
mockOutActionTest.Object.Increment(ref actualValue);
// Assert
Assert.That(actualValue, Is.EqualTo(11));
}
[Test]
public void Test_OutCallback_SetsValueWhenReturnTypeIsNotVoid()
{
// Arrange
Mock<IOutFuncTest> mockOutActionTest = CreateMock<IOutFuncTest>();
int tempValue;
mockOutActionTest.Setup(test => test.TryGet(out tempValue)).OutCallback(new TryGetAction((out int value) => value = 10)).Returns(true);
// Act
int actualValue;
bool result = mockOutActionTest.Object.TryGet(out actualValue);
// Assert
Assert.That(result, Is.True);
Assert.That(actualValue, Is.EqualTo(10));
}
[Test]
public void Test_RefCallback_IncrementsValueWhenReturnTypeIsNotVoid()
{
// Arrange
Mock<IRefFuncTest> mockOutActionTest = CreateMock<IRefFuncTest>();
int tempValue = 10;
mockOutActionTest.Setup(test => test.Increment(ref tempValue)).RefCallback(new IncrementAction((ref int value) => value++)).Returns(10);
// Act
int actualValue = 10;
int result = mockOutActionTest.Object.Increment(ref actualValue);
// Assert
Assert.That(result, Is.EqualTo(10));
Assert.That(actualValue, Is.EqualTo(11));
}
private static Mock<T> CreateMock<T>() where T : class
{
return new Mock<T>(MockBehavior.Strict);
}
public interface IRefActionTest
{
void Increment(ref int integer);
}
public interface IRefFuncTest
{
int Increment(ref int integer);
}
public interface IOutActionTest
{
void TryGet(out int value);
}
public interface IOutFuncTest
{
bool TryGet(out int value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment