在Moq中指定/ ref参数
是否可以使用Moq(3.0)分配out / ref参数?
我已经看过使用Callback()
,但Action<>
不支持ref参数,因为它基于generics。 我还希望在ref
参数的input上放置一个约束( It.Is
),尽pipe我可以在callback中做到这一点。
我知道Rhino Mocks支持这个function,但是我正在使用的项目已经使用了Moq。
Avner Kashtan在他的博客中提供了一个扩展方法,允许从callback中设置out参数: Moq,Callbacks和Out参数:一个特别棘手的边缘情况
解决scheme既优雅又好看。 其优雅之处在于它提供了一种stream畅的语法,可以与其他Moqcallback一起在家中使用。 哈克因为它依靠通过reflection调用一些内部的Moq API。
上面链接提供的扩展方法没有为我编译,所以我在下面提供了一个编辑版本。 您需要为每个input参数创build一个签名; 我提供了0和1,但进一步扩展应该很简单:
public static class MoqExtensions { public delegate void OutAction<TOut>(out TOut outVal); public delegate void OutAction<in T1,TOut>(T1 arg1, out TOut outVal); public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, TOut>(this ICallback<TMock, TReturn> mock, OutAction<TOut> action) where TMock : class { return OutCallbackInternal(mock, action); } public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, TOut>(this ICallback<TMock, TReturn> mock, OutAction<T1, TOut> action) where TMock : class { return OutCallbackInternal(mock, action); } private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(ICallback<TMock, TReturn> mock, object action) where TMock : class { mock.GetType() .Assembly.GetType("Moq.MethodCall") .InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock, new[] { action }); return mock as IReturnsThrows<TMock, TReturn>; } }
使用上面的扩展方法,你可以testing一个带有out参数的接口,比如:
public interface IParser { bool TryParse(string token, out int value); }
..以下Moq设置:
[TestMethod] public void ParserTest() { Mock<IParser> parserMock = new Mock<IParser>(); int outVal; parserMock .Setup(p => p.TryParse("6", out outVal)) .OutCallback((string t, out int v) => v = 6) .Returns(true); int actualValue; bool ret = parserMock.Object.TryParse("6", out actualValue); Assert.IsTrue(ret); Assert.AreEqual(6, actualValue); }
编辑 :为了支持无效返回方法,你只需要添加新的重载方法:
public static ICallbackResult OutCallback<TOut>(this ICallback mock, OutAction<TOut> action) { return OutCallbackInternal(mock, action); } public static ICallbackResult OutCallback<T1, TOut>(this ICallback mock, OutAction<T1, TOut> action) { return OutCallbackInternal(mock, action); } private static ICallbackResult OutCallbackInternal(ICallback mock, object action) { mock.GetType().Assembly.GetType("Moq.MethodCall") .InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock, new[] { action }); return (ICallbackResult)mock; }
这允许testing接口,例如:
public interface IValidationRule { void Validate(string input, out string message); } [TestMethod] public void ValidatorTest() { Mock<IValidationRule> validatorMock = new Mock<IValidationRule>(); string outMessage; validatorMock .Setup(v => v.Validate("input", out outMessage)) .OutCallback((string i, out string m) => m = "success"); string actualMessage; validatorMock.Object.Validate("input", out actualMessage); Assert.AreEqual("success", actualMessage); }
对于'出',以下似乎为我工作。
public interface IService { void DoSomething(out string a); } [TestMethod] public void Test() { var service = new Mock<IService>(); var expectedValue = "value"; service.Setup(s => s.DoSomething(out expectedValue)); string actualValue; service.Object.DoSomething(out actualValue); Assert.AreEqual(actualValue, expectedValue); }
我猜,当你打电话给安装程序并记住它时,Moq会查看“expectedValue”的值。
对于'ref',我也在寻找答案。
我发现以下快速入门指南很有用: https : //github.com/Moq/moq4/wiki/Quickstart
这是来自Moq网站的文档:
// out arguments var outString = "ack"; // TryParse will return true, and the out argument will return "ack", lazy evaluated mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true); // ref arguments var instance = new Bar(); // Only matches if the ref argument to the invocation is the same instance mock.Setup(foo => foo.Submit(ref instance)).Returns(true);
似乎是不可能的。 看起来像有人试图解决
看到这个论坛posthttp://code.google.com/p/moq/issues/detail?id=176
这个问题用Moqvalidation参考参数的值
这可以是一个解决scheme。
[Test] public void TestForOutParameterInMoq() { //Arrange _mockParameterManager= new Mock<IParameterManager>(); Mock<IParameter > mockParameter= new Mock<IParameter >(); //Parameter affectation should be useless but is not. It's really used by Moq IParameter parameter= mockParameter.Object; //Mock method used in UpperParameterManager _mockParameterManager.Setup(x => x.OutMethod(out parameter)); //Act with the real instance _UpperParameterManager.UpperOutMethod(out parameter); //Assert that method used on the out parameter of inner out method are really called mockParameter.Verify(x => x.FunctionCalledInOutMethodAfterInnerOutMethod(),Times.Once()); }
要返回一个值以及设置ref参数,下面是一段代码:
public static class MoqExtensions { public static IReturnsResult<TMock> DelegateReturns<TMock, TReturn, T>(this IReturnsThrows<TMock, TReturn> mock, T func) where T : class where TMock : class { mock.GetType().Assembly.GetType("Moq.MethodCallReturn`2").MakeGenericType(typeof(TMock), typeof(TReturn)) .InvokeMember("SetReturnDelegate", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock, new[] { func }); return (IReturnsResult<TMock>)mock; } }
然后声明你自己的代表匹配待模拟方法的签名,并提供你自己的方法实现。
public delegate int MyMethodDelegate(int x, ref int y); [TestMethod] public void TestSomething() { //Arrange var mock = new Mock<ISomeInterface>(); var y = 0; mock.Setup(m => m.MyMethod(It.IsAny<int>(), ref y)) .DelegateReturns((MyMethodDelegate)((int x, ref int y)=> { y = 1; return 2; })); }
我今天下午奋斗了一个小时,在任何地方都找不到答案。 在我自己玩了之后,我能够想出一个适合我的解决scheme。
string firstOutParam = "first out parameter string"; string secondOutParam = 100; mock.SetupAllProperties(); mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);
这里的关键是mock.SetupAllProperties();
这将为您清除所有的属性。 这可能不适用于每个testing用例场景,但是如果您关心的是获取YourMethod
的return value
,那么这将工作正常。