【问题标题】:Define a Stub for a method with ref parameters, where ref value is not modified为带有 ref 参数的方法定义一个 Stub,其中 ref 值不被修改
【发布时间】:2015-08-14 08:31:03
【问题描述】:

我正在使用无法更改的界面(出于遗留原因)。该接口上有采用ref 参数的方法。

在我的单元测试中,我希望在该接口上删除其中一个方法。至关重要的是,我想更改其中一个 ref 参数,但保持另一个不变。

我可以更改我想要更改的参数,但我不知道如何告诉 Rhino Mocks 让另一个保持不变。

这是一个突出问题的失败测试(请注意,实际上我在定义存根之前无权访问poco,因为它是在外部模块中实例化的):

using Rhino.Mocks;
using Rhino.Mocks.Constraints;
using NUnit.Framework;

[Test]
public void Main()
{
    var serviceMock = MockRepository.GenerateMock<IService>();

    serviceMock.Stub(x => x.DoSomething(
            ref Arg<Poco>.Ref(Rhino.Mocks.Constraints.Is.Anything(), null).Dummy, // I don't want to specify null here
            ref Arg<int>.Ref(Rhino.Mocks.Constraints.Is.Equal(1), 2).Dummy));

    Poco poco = new Poco{Data = "One"};
    int returnValue = 1;
    serviceMock.DoSomething(ref poco, ref returnValue);

    Assert.AreEqual(2, returnValue); // passes
    Assert.AreEqual("One", poco.Data); // fails
}

public class Poco
{
    public string Data { get; set; }
}

public interface IService
{
    void DoSomething(ref Poco poco, ref int returnValue);
}

【问题讨论】:

    标签: c# unit-testing mocking rhino-mocks


    【解决方案1】:

    替换:

    serviceMock.Stub(x => x.DoSomething(
            ref Arg<Poco>.Ref(Rhino.Mocks.Constraints.Is.Anything(), null).Dummy,
            ref Arg<int>.Ref(Rhino.Mocks.Constraints.Is.Equal(1), 2).Dummy));
    

    与:

    serviceMock.Stub(x => x.DoSomething(
                     ref Arg<Poco>.Ref(Rhino.Mocks.Constraints.Is.Anything(), poco).Dummy,
                     ref Arg<int>.Ref(Rhino.Mocks.Constraints.Is.Equal(1), 2).Dummy));
    

    更改将用poco 替换调用参数。那么你的测试就会通过。

    编辑:

    要实现您正在寻找的行为,您必须实现自定义AbstractConstraint。这个对象会记录传入的值,那么你需要在RhinoMocks中指定一个interceptor(WhenCalled方法):

    class PassValue : AbstractConstraint
    {
    
        public Poco Obj { get; private set; }
    
        public override bool Eval(object obj)
        {
            Obj = (Poco)obj;
    
            return true;
        }
    
        public override string Message
        {
            get { throw new NotImplementedException(); }
        }
    }
    

    在存根声明中使用它:

    var p = new PassValue();
    
    serviceMock.Stub(x => x.DoSomething(
                ref Arg<Poco>.Ref(p, p.Obj).Dummy, 
                ref Arg<int>.Ref(Rhino.Mocks.Constraints.Is.Equal(1), 2).Dummy))
                .WhenCalled(invocation =>
                {
                    invocation.Arguments[0] = p.Obj;
                });
    

    【讨论】:

    • 虽然测试会通过,但参数仍然会改变——在你的例子中,它是为完全相同的实例改变的,但实际上我无权访问相同的实例来执行此操作替换。我已经更新了问题以反映这一点
    • 太好了,谢谢!一个小点,我认为你不需要引用 p.Obj 两次,即ref Arg&lt;Poco&gt;.Ref(p, p.Obj).Dummy, 可以是ref Arg&lt;Poco&gt;.Ref(p, null).Dummy,
    • @ChrisPriest 你对这个例子中的 null 是正确的,但是在我的真实代码中 PassValuePassValue&lt;T&gt;Arg&lt;Poco&gt;.Ref(p, p.Obj) 包装在一个辅助方法中......
    【解决方案2】:

    试试这个:

     var serviceMock = MockRepository.GenerateMock<IService>();
    
     var poco = new Poco { Data = "One" };
    
     serviceMock.Stub(x => x.DoSomething(
                    ref Arg<Poco>.Ref(Rhino.Mocks.Constraints.Is.Anything(), new Poco { Data = "One" }).Dummy, // I don't want to specify null here
                    ref Arg<int>.Ref(Rhino.Mocks.Constraints.Is.Equal(1), 2).Dummy));
    
     int returnValue = 1;
     serviceMock.DoSomething(ref poco, ref returnValue);
    
     Assert.AreEqual(2, returnValue); // pass
     Assert.AreEqual("One", poco.Data); // pass
    

    【讨论】:

    • 感谢您的回答,但根据@old-fox,测试将通过,但参数仍会更改 - 在您的示例中,它会更改为恰好具有相同属性的另一个实例.实际上,我无权访问 poco 以了解替换应具有的属性。我已经更新了问题以反映这一点
    猜你喜欢
    • 2011-08-06
    • 2015-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 2021-11-27
    相关资源
    最近更新 更多