2

I have problems with stubbing a method with a ref parameter.
I want to stub that method for a certain input value and check that it was called.
My attempt:

// 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));

BTW: I know, that this test doesn't make any sense, because it doesn't test any real classes. It's a condensed version of my real test to illustrate the problem.

As you can see, there is something strange going on:

The stub of the execute method is correct and it is called, because executeCalled is 1 and the tmp parameter has been changed from inputValue to outputValue.
BUT:

  • The first check with AssertWasCalled passes, although it checks, whether Execute was called with outputValue, which it wasn't.
  • The second check with AssertWasCalled failes, although it checks, whether Execute was called with inputValue, which it was.

Furthermore, when I check i.Arguments[1] inside WhenCalled of the stub, it is outputValue, not inputValue... It looks like Rhino Mocks is changing the input value to the specified return value, before even calling the stub...

Is this a bug in Rhino Mocks? Or am I missing something? If it is a bug, are there any workarounds, beside the executeCalled counter?

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443

1 Answers1

2

The same test, a bit cleaned up:

    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));
    }

It passes. It clearly shows that Rhino records the output value, not the original input value. It is rarely recognized, because you need this temp variable to test this effect.

The following test also passes:

    [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));
    }

It can be seen as a bug, but is quite a subtle case. If it is really a problem, you may take a look into Rhinos code to see if the bug is easy to fix.

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • Thanks for taking the time to answer my question. However, it doesn't help. The first code just confirms my observation. The second code is actually the same as the first, because `ReferenceEquals(inputValue, outputValue)` will return `true` after the call to `activity.Execute`, so `inputValue` can be exchanged for `outputValue` in the assert. What I am trying to say is: Your second code tests the same as the first code. But it does not test whether `Execute` was called with the original value of `inputValue`... – Daniel Hilgarth Apr 19 '11 at 17:08
  • @Daniel: I know ... all that is the whole purpose of my answer. I cleaned up the test to show and prove the effect more easily. The second test is to only to show how the effect may have been missed by the developers so far. – Stefan Steinegger Apr 19 '11 at 18:00
  • I can't say much more. You encounter the effect and I can only approve it by reproducing it using minimal code. Your question was: "did I miss anything?" My answer is: "No, it's there and may be considered a bug." – Stefan Steinegger Apr 19 '11 at 18:07
  • Ah, ok, it looks like I misunderstood the intent of your answer. You are right, you answer my question. And I agree about your conclusion that it might be the reason why the developers missed it... When I have the time, I will dig into the source and check whether I can fix it easily. – Daniel Hilgarth Apr 20 '11 at 10:02