【问题标题】:AssertWasCalled and Stub with method that has ref parameter带有 ref 参数的方法的 AssertWasCalled 和 Stub
【发布时间】:2011-08-06 02:32:53
【问题描述】:

我在使用 ref 参数存根方法时遇到问题。
我想为某个输入值存根该方法并检查它是否被调用。
我的尝试:

// Variables needed - can be skipped
var activity = MockRepository.GenerateMock<ICompositeActivity<object>>();
var context = new Context<object>(new object());
var inputValue = MockRepository.GenerateMock<IActivity<object>>();
var outputValue = MockRepository.GenerateMock<IActivity<object>>();
var executeCalled = 0;

// The stub:
activity.Stub(
    x =>
    x.Execute(Arg<Context<object>>.Is.Same(context),
              ref Arg<IActivity<object>>.Ref(Is.Same(inputValue), outputValue).Dummy))
    .WhenCalled(i => ++executeCalled).Return(true);

var tmp = inputValue;
tmp.ShouldBeTheSameAs(inputValue);

// The execution:
activity.Execute(context, ref tmp);

// The check:
inputValue.ShouldNotBeTheSameAs(outputValue); // Passes, ok
tmp.ShouldBeTheSameAs(outputValue); // Passes, ok
executeCalled.ShouldEqual(1); // Passes, ok

// Passes. Why?
activity.AssertWasCalled(
    x =>
    x.Execute(Arg<Context<object>>.Is.Same(context),
              ref Arg<IActivity<object>>.Ref(Is.Same(outputValue), null).Dummy));

// Doesn't pass. Why?
activity.AssertWasCalled(
    x =>
    x.Execute(Arg<Context<object>>.Is.Same(context),
              ref Arg<IActivity<object>>.Ref(Is.Same(inputValue), outputValue).Dummy));

顺便说一句:我知道,这个测试没有任何意义,因为它不测试任何真正的类。这是我真实测试的浓缩版来说明问题。

如您所见,发生了一些奇怪的事情:

execute方法的stub是正确的,它被调用了,因为executeCalled是1并且tmp参数已经从inputValue变成了outputValue
但是:

  • 第一次检查AssertWasCalled通过,虽然它检查Execute是否被outputValue调用,但它不是。
  • 第二次检查AssertWasCalled失败,虽然它检查Execute是否被inputValue调用,确实如此。

此外,当我检查存根的WhenCalled 内的i.Arguments[1] 时,它是outputValue,而不是inputValue...看起来Rhino Mocks 正在将输入值更改为指定的返回值,甚至之前调用存根...

这是 Rhino Mocks 中的错误吗?还是我错过了什么?如果是错误,除了executeCalled 计数器之外是否有任何解决方法?

【问题讨论】:

  • 投反对票的人能否详细说明一下?
  • 我同意丹尼尔..请解释..

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


【解决方案1】:

同样的测试,稍微整理了一下:

    public interface IX
    {
        void Execute(ref object param);
    }

    [TestMethod]
    public void TestMethod()
    {
        // Variables needed - can be skipped
        var inputValue = new object();
        var outputValue = new object();
        IX activity = MockRepository.GenerateMock<IX>();

        // The stub:
        activity
            .Stub(x => x.Execute(
                ref Arg<object>.Ref(Is.Same(inputValue), outputValue).Dummy));

        var tmp = inputValue;

        activity.Execute(ref tmp);

        activity
            .AssertWasCalled(x => x.Execute(
              ref Arg<object>.Ref(Is.Same(outputValue), null).Dummy));
    }

它通过了。它清楚地表明,Rhino 记录的是输出值,而不是原始输入值。它很少被识别,因为您需要这个临时变量来测试这种效果。

以下测试也通过了:

    [TestMethod]
    public void TestMethod()
    {
        // Variables needed - can be skipped
        var inputValue = new object();
        var outputValue = new object();
        IX activity = MockRepository.GenerateMock<IX>();

        // The stub:
        activity
            .Stub(x => x.Execute(
                ref Arg<object>.Ref(Is.Same(inputValue), outputValue).Dummy));

        activity.Execute(ref inputValue);

        activity
            .AssertWasCalled(x => x.Execute(
              ref Arg<object>.Ref(Is.Same(inputValue), null).Dummy));
    }

它可以被视为一个错误,但这是一个非常微妙的案例。如果真的有问题,你可以看看 Rhinos 的代码,看看这个 bug 是否容易修复。

【讨论】:

  • 感谢您抽出宝贵时间回答我的问题。但是,它没有帮助。第一个代码只是证实了我的观察。第二段代码其实和第一段一样,因为ReferenceEquals(inputValue, outputValue)在调用activity.Execute后会返回true,所以inputValue可以在assert中换成outputValue。我想说的是:您的第二个代码测试与第一个代码相同。但它不测试是否使用inputValue的原始值调用Execute...
  • @Daniel:我知道......这就是我回答的全部目的。我清理了测试以更容易地显示和证明效果。第二个测试只是为了显示到目前为止开发人员可能错过的效果。
  • 我不能多说。你会遇到这种效果,我只能通过使用最少的代码复制它来批准它。你的问题是:“我错过了什么吗?”我的回答是:“不,它存在并且可能被认为是一个错误。”
  • 啊,好吧,看来我误解了你回答的意图。你是对的,你回答了我的问题。我同意你的结论,这可能是开发人员错过它的原因......当我有时间时,我会挖掘源代码并检查是否可以轻松修复它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-03
  • 2016-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-03
  • 1970-01-01
相关资源
最近更新 更多