开发者

Moq a function with 5+ parameters and access invocation arguments

开发者 https://www.devze.com 2022-12-25 04:33 出处:网络
I have a function I want to Moq.The problem is that it takes 5 parameters.The framework only contains Action<T1,T2,T3,T4> and Moq\'s generic CallBack() only overloads Action and the four generic

I have a function I want to Moq. The problem is that it takes 5 parameters. The framework only contains Action<T1,T2,T3,T4> and Moq's generic CallBack() only overloads Action and the four generic versions. Is there an elegant workaround for this?

This is what I want to do:

public class Filter : IFilter  
{  
    public int Filter(int i1, int i2, int i3, int i4, int i5){return 0;}  
}

//Moq code:
var mocker = new Mock<IFilter>();  
mocker.Setup(x => x.Filter(  
    It.IsAny<int>(),  
    It.IsAny<in开发者_高级运维t>(),  
    It.IsAny<int>(),  
    It.IsAny<int>(),  
    It.IsAny<int>(),  
    It.IsAny<int>())  
.Callback
(  
    (int i1, int i2, int i3, int i4, int i5) => i1 * 2  
);  

Moq doesn't allow this because there is no generic Action that takes 5+ parameters. I've resorted to making my own stub. Obviously, it would be better to use Moq with all of its verifications, etc.


This is supported in the final release of Moq 4.0 (4.0.10827), which was released on April 12, 2011. If you're using .NET 4, you'll be able to mock up to 16 parameters.


I know this is an old post, but I thought this code may help others having this problem with an older version of Moq. Here's an example with 5 and 6 arguments, and it can be extended to support any number.

I have tested this with version 3.1.0.0, but it should it should work with other versions too.

The secret is to use reflection to access the protected method 'SetCallbackWithArguments' on the underlying class...

Hope this is of use to someone!

public delegate void Action<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate void Action<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);

public static class MoqExtensions
{
    public static ICallbackResult Callback<T1, T2, T3, T4, T5>(this ICallback call, Action<T1, T2, T3, T4, T5> callback)
    {
        call.SetCallbackWithArguments(callback);
        return (ICallbackResult)call;
    }

    public static ICallbackResult Callback<T1, T2, T3, T4, T5, T6>(this ICallback call, Action<T1, T2, T3, T4, T5, T6> callback)
    {
        call.SetCallbackWithArguments(callback);
        return (ICallbackResult)call;
    }

    public static void SetCallbackWithArguments(this ICallback call, Delegate callback)
    {
        MethodInfo methodInfo = call.GetType().GetMethod("SetCallbackWithArguments", BindingFlags.NonPublic | BindingFlags.Instance);
        methodInfo.Invoke(call, new object[] { callback });
    }
}


I know this probably breaks your design, but with that many parameters wouldn't it be better to pass a param array?


Use an extension method to extend the moq framework if you dont want to modify the source.

//Extension Method

    public static class MoqExtensions
    {
        public static void Callback<T1, T2, T3, T4, T5>(this ICallback iCallBack, Action<T1, T2, T3, T4, T5> action)
        {
        }
    }

//Your class and interface

        public interface IMyClass
        {
            void Method(string arg1, string arg2, string arg3, string arg4, string arg5);
        }

        public class MyClass : IMyClass
        {
            public void Method(string arg1, string arg2, string arg3, string arg4, string arg5)
            {
                //Do something
            }
        }

//Your test

        [TestMethod]
        public void ExampleTest()
        {
            Mock<IMyClass> mockMyClass = new Mock<IMyClass>();
            mockMyClass.Setup(s => s.Method(It.IsAny<string>(),
                                            It.IsAny<string>(),
                                            It.IsAny<string>(),
                                            It.IsAny<string>(),
                                            It.IsAny<string>()))
                .Callback<string, string, string, string, string>((string arg1, string arg2, string arg3, string arg4, string arg5) 
                    => { /* Run your mock here */ });
        }


delegate void SubmitCallback(Bar bar1,Bar bar2,Bar bar3,Bar bar4,Bar bar5);

mock.Setup(foo => foo.Submit(It.IsAny<Bar>(),It.IsAny<Bar>(),It.IsAny<Bar>(),It.IsAny<Bar>(),It.IsAny<Bar>()))
    .Callback(new SubmitCallback((Bar bar1,Bar bar2,Bar bar3,Bar bar4,Bar bar5) => Console.WriteLine("Submitting a Bar!")));

You can use more than 16 aguments by delegate. :)


This is actually quite simple. Just define your own Action<T1, T2, T3, T4, T5> and you're good to go.

public delegate void Action<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);

This is all that the Action, Action<T>, ..., Action<T1, T2, T3, T4> are defined as in the framework. Nothing more. Simple!

0

精彩评论

暂无评论...
验证码 换一张
取 消