是否可以使用Moq(3.0+)分配一个out/ref参数?
我已经看过使用Callback(),但Action<>不支持ref参数,因为它是基于泛型的。我还希望在ref参数的输入上放置一个约束(It.Is),尽管我可以在回调中这样做。
我知道Rhino Mocks支持这个功能,但是我正在做的项目已经在使用Moq了。
是否可以使用Moq(3.0+)分配一个out/ref参数?
我已经看过使用Callback(),但Action<>不支持ref参数,因为它是基于泛型的。我还希望在ref参数的输入上放置一个约束(It.Is),尽管我可以在回调中这样做。
我知道Rhino Mocks支持这个功能,但是我正在做的项目已经在使用Moq了。
当前回答
下面是一个正在工作的例子。
[Fact]
public void DeclineLowIncomeApplicationsOutDemo()
{
var mockValidator = new Mock<IFrequentFlyerNumberValidator>();
var isValid = true; // Whatever we set here, thats what we will get.
mockValidator.Setup(x => x.IsValid(It.IsAny<string>(), out isValid));
var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
var application = new CreditCardApplication
{
GrossAnnualIncome = 19_999,
Age = 42
};
var decision = sut.EvaluateUsingOut(application);
Assert.Equal(CreditCardApplicationDecision.AutoDeclined, decision);
}
public interface IFrequentFlyerNumberValidator
{
bool IsValid(string frequentFlyerNumber);
void IsValid(string frequentFlyerNumber, out bool isValid);
}
注意,在设置中没有return,因为没有return。
其他回答
我相信斯科特的解决方案在某种程度上是有效的,
但是不使用反射来窥探私有api是一个很好的理由。它现在坏了。
我可以使用委托设置参数
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);
}
今天下午,我为此纠结了一个小时,但在任何地方都找不到答案。在我自己玩了一圈之后,我能够想出一个适合我的解决方案。
string firstOutParam = "first out parameter string";
string secondOutParam = 100;
mock.SetupAllProperties();
mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);
这里的关键是mock.SetupAllProperties();这将为您清除所有属性。这可能并不适用于每个测试用例场景,但如果您所关心的只是获取YourMethod的返回值,那么这将很好地工作。
就像这样:
被嘲笑的方法是
public bool GenerateClient(out Client client);
那么模拟部分将是:
Client client = new Client();
clintMockBll
.Setup(x => x.GenerateClient(out client))
.Returns((Client client1) =>
{
client = new Client(){Name="Something"};
return true;
});
这是来自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);
为了返回一个值和设置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;
}));
}