【问题标题】:how can I get an MOQ setup to alter an out parameter based on input?如何获得 MOQ 设置以根据输入更改输出参数?
【发布时间】:2016-01-07 13:33:35
【问题描述】:

MOQ 支持输出参数的设置 - 没问题。我希望能够通过 Returns() 或 Callback() 根据调用中传递的内容设置参数。我的用例涉及使用 out 参数模拟方法。

这是我用来做实验的,到目前为止每次都是负面结果:

public interface ITestClass
{
    string method(string inString, out string outString);
}

public class TestClass : ITestClass
{
    public string method(string inString, out string outString)
    {
        outString = inString + " was passed in";
        return (inString + " was returned");
    }
}

[TestFixture]
public class OutTest
{
    [Test]
    public void Test()
    {
        //Arrange
        Mock<ITestClass> mock = new Mock<ITestClass>(MockBehavior.Strict);
        string stringParm = "value that will be assigned to out parameter";
        mock.Setup(t => t.method(It.IsAny<string>(), out stringParm))
            .Returns((string i, string o) =>
            {
                return i + " was returned"; // o = stringParm already
            })
            .Callback((string s, string oo) =>
            {
                stringParm = s + " was passed in"; // oo = stringParm already
            });
        TestClass real = new TestClass();
        string testString = DateTime.Now.ToLongTimeString();

        //Act
        string realOut;
        string mockOut;
        string realResult = real.method(testString, out realOut);
        string mockResult = mock.Object.method(testString, out mockOut);

        //Assert
        realResult.Should().Be(mockResult); // passes
        realOut.Should().Be(mockOut); // fails - mockout = original stringParm
    }
}

【问题讨论】:

  • 我看到这是在 2009 年在 MOQ 基础之外实施的......但它还不能作为 MOQ 的一部分。 code.google.com/p/moq/issues/detail?id=176
  • @FabioSalvalai 不,那个问题是关于如何为 out 或 ref 赋值 - 我同意 MOQ 可以处理得很好。我的示例中的 stringParm 变量以这种方式工作。我想要做的是在回调或返回块中设置 out 参数的值 - 这样它的值就会受到其他传递参数的影响。
  • 好的,知道了。我将发布一个解决方法作为建议的答案。

标签: c# moq out


【解决方案1】:

我知道这并不理想,但为什么不传递预期的 mockOut 而不是 "value that will be assigned to out parameter" 呢?

string stringParm = "foo was passed in"

【讨论】:

  • 我怀疑我不理解您的建议 - 您介意在我发布的示例中显示会更改的代码行吗?
  • 好吧,我相信我做到了。将string stringParm = "value that will be assigned to out parameter"; 替换为string stringParm = "foo was passed in" 它将起作用,只是因为您在测试期间仅通过传递foo 进行调用,并且除了foo 之外没有其他值。
  • 是的 - 无论您将 stringParm 设置为什么值,该值都会在调用方法时传递到 out 参数中。这就是为什么我选择了我为 stringParm 所做的值;) - 它描述了会发生什么。我不能做的是根据传递给方法的内容设置 out 参数...必须从执行 Returns() 或 Callback() 的上下文中完成...我已经编辑了测试以强调this - 测试字符串现在在每次执行时都会改变。我想找到一种方法让测试通过。
  • 我明白,但您不需要用您的模拟逻辑复制 Subject Under Test 的逻辑。你不需要也不应该。相反,您应该定制一个能够正确响应您的 Act 的定制 Arrange
  • 完全同意——这就是我通常所做的。在这种特定情况下,我正在尝试测试合同 - 我需要在有限的可能性范围内模拟行为。我已经解决了它 - 但我仍然希望有能力做到这一点。我已经确认 Rhino 对此提供支持,所以如果需要我可以随时使用它。
【解决方案2】:

我最近不得不更新我使用的解决方案,以便在没有参数的设置上执行回调。

namespace SupRep.TestUtilities
{
    using System.Reflection;
    using Moq.Language;
    using Moq.Language.Flow;

    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 delegate void OutAction<in T1,in T2,TOut>(T1 arg1, T2 arg2, 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);
        }

        public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, T2, TOut>(this ICallback<TMock, TReturn> mock, OutAction<T1, T2, 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
        {
            var methodCall = mock.GetType().GetProperty("Setup").GetValue(mock);
            mock.GetType().Assembly.GetType("Moq.MethodCall")
                .InvokeMember("SetCallbackResponse", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, methodCall,
                    new[] { action });
            return mock as IReturnsThrows<TMock, TReturn>;
        }
    }}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多