C# 在 Moq 中分配 out/ref 参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1068095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Assigning out/ref parameters in Moq
提问by Richard Szalay
Is it possible to assign an out
/ref
parameter using Moq (3.0+)?
是否可以使用 Moq (3.0+)分配out
/ref
参数?
I've looked at using Callback()
, but Action<>
does not support ref parameters because it's based on generics. I'd also preferably like to put a constraint (It.Is
) on the input of the ref
parameter, though I can do that in the callback.
我看过 using Callback()
,但Action<>
不支持 ref 参数,因为它基于泛型。我还希望It.Is
在ref
参数的输入上设置一个约束 ( ) ,尽管我可以在回调中这样做。
I know that Rhino Mocks supports this functionality, but the project I'm working on is already using Moq.
我知道 Rhino Mocks 支持这个功能,但我正在做的项目已经在使用 Moq。
采纳答案by stakx - no longer contributing
Moq version 4.8 (or later) has much improved support for by-ref parameters:
Moq 4.8 版(或更高版本)大大改进了对 by-ref 参数的支持:
public interface IGobbler
{
bool Gobble(ref int amount);
}
delegate void GobbleCallback(ref int amount); // needed for Callback
delegate bool GobbleReturns(ref int amount); // needed for Returns
var mock = new Mock<IGobbler>();
mock.Setup(m => m.Gobble(ref It.Ref<int>.IsAny)) // match any value passed by-ref
.Callback(new GobbleCallback((ref int amount) =>
{
if (amount > 0)
{
Console.WriteLine("Gobbling...");
amount -= 1;
}
}))
.Returns(new GobbleReturns((ref int amount) => amount > 0));
int a = 5;
bool gobbleSomeMore = true;
while (gobbleSomeMore)
{
gobbleSomeMore = mock.Object.Gobble(ref a);
}
The same pattern works for out
parameters.
相同的模式适用于out
参数。
It.Ref<T>.IsAny
also works for C# 7 in
parameters (since they are also by-ref).
It.Ref<T>.IsAny
也适用于 C# 7in
参数(因为它们也是 by-ref)。
回答by Gishu
Seems like it is not possible out of the box. Looks like someone attempted a solution
似乎无法开箱即用。看起来有人尝试了解决方案
See this forum post http://code.google.com/p/moq/issues/detail?id=176
请参阅此论坛帖子 http://code.google.com/p/moq/issues/detail?id=176
this question Verify value of reference parameter with Moq
这个问题 用最小起订量验证参考参数的值
回答by Craig Celeste
For 'out', the following seems to work for me.
对于“出”,以下内容似乎对我有用。
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(expectedValue, actualValue);
}
I'm guessing that Moq looks at the value of 'expectedValue' when you call Setup and remembers it.
我猜当您调用 Setup 并记住它时,Moq 会查看 'expectedValue' 的值。
For ref
, I'm looking for an answer also.
对于ref
,我也在寻找答案。
I found the following QuickStart guide useful: https://github.com/Moq/moq4/wiki/Quickstart
我发现以下快速入门指南很有用:https: //github.com/Moq/moq4/wiki/Quickstart
回答by Fabrice
This can be a solution .
这可以是一个解决方案。
[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());
}
回答by Kosau
This is documentation from Moq site:
这是来自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);
回答by Scott Wegner
EDIT: In Moq 4.10, you can now pass a delegate that has an out or ref parameter directly to the Callback function:
编辑:在 Moq 4.10 中,您现在可以将具有 out 或 ref 参数的委托直接传递给回调函数:
mock
.Setup(x=>x.Method(out d))
.Callback(myDelegate)
.Returns(...);
You will have to define a delegate and instantiate it:
您必须定义一个委托并实例化它:
...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...
For Moq version before 4.10:
对于 4.10 之前的 Moq 版本:
Avner Kashtan provides an extension method in his blog which allows setting the out parameter from a callback: Moq, Callbacks and Out parameters: a particularly tricky edge case
Avner Kashtan 在他的博客中提供了一个扩展方法,它允许从回调中设置 out 参数:Moq、Callbacks 和 Out 参数:一个特别棘手的边缘情况
The solution is both elegant and hacky. Elegant in that it provides a fluent syntax that feels at-home with other Moq callbacks. And hacky because it relies on calling some internal Moq APIs via reflection.
解决方案既优雅又笨拙。优雅之处在于它提供了一种流畅的语法,让您感觉与其他 Moq 回调相得益彰。并且 hacky,因为它依赖于通过反射调用一些内部 Moq API。
The extension method provided at the above link didn't compile for me, so I've provided an edited version below. You'll need to create a signature for each number of input parameters you have; I've provided 0 and 1, but extending it further should be simple:
上面链接中提供的扩展方法没有为我编译,所以我在下面提供了一个编辑过的版本。您需要为您拥有的每个输入参数创建一个签名;我提供了 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>;
}
}
With the above extension method, you can test an interface with out parameters such as:
使用上面的扩展方法,你可以测试一个没有参数的接口,例如:
public interface IParser
{
bool TryParse(string token, out int value);
}
.. with the following Moq setup:
.. 使用以下最小起订量设置:
[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);
}
Edit: To support void-return methods, you simply need to add new overload methods:
编辑:要支持 void-return 方法,您只需添加新的重载方法:
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;
}
This allows testing interfaces such as:
这允许测试接口,例如:
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);
}
回答by maxshuty
I struggled with this for an hour this afternoon and could not find an answer anywhere. After playing around on my own with it I was able to come up with a solution which worked for me.
今天下午我为此苦苦挣扎了一个小时,但在任何地方都找不到答案。在我自己玩弄它之后,我能够想出一个对我有用的解决方案。
string firstOutParam = "first out parameter string";
string secondOutParam = 100;
mock.SetupAllProperties();
mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);
The key here is mock.SetupAllProperties();
which will stub out all of the properties for you. This may not work in every test case scenario, but if all you care about is getting the return value
of YourMethod
then this will work fine.
这里的关键是mock.SetupAllProperties();
它会为您剔除所有属性。这可能不适用于每个测试用例场景,但如果您只关心获取return value
ofYourMethod
那么这将正常工作。
回答by Victor Mukherjee
To return a value along with setting ref parameter, here is a piece of code:
要在设置 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;
}
}
Then declare your own delegate matching the signature of to-be-mocked method and provide your own method implementation.
然后声明您自己的委托与待模拟方法的签名匹配并提供您自己的方法实现。
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;
}));
}
回答by Casey O'Brien
I struggled with many of the suggestions here before I simple created an instance of a new 'Fake' class that implements whatever interface you are trying to Mock out. Then you can simply set the value of the out parameter with the method itself.
在我简单地创建了一个新的“Fake”类的实例之前,我在这里遇到了许多建议,该实例实现了您试图模拟的任何接口。然后您可以简单地使用方法本身设置 out 参数的值。
回答by Billy Jake O'Connor
I'm sure Scott's solution worked at one point,
我确信 Scott 的解决方案曾经奏效,
But it's a good argument for not using reflection to peek at private apis. It's broken now.
但这是不使用反射来窥视私有 api 的一个很好的论据。现在坏了。
I was able to set out parameters using a delegate
我能够使用委托设置参数
delegate void MockOutDelegate(string s, out int value);
public void SomeMethod()
{
....
int value;
myMock.Setup(x => x.TryDoSomething(It.IsAny<string>(), out value))
.Callback(new MockOutDelegate((string s, out int output) => output = userId))
.Returns(true);
}