views:

275

answers:

5

I have this problem, which may be a bug in Rhino Mocks 3.5:

stubObj = MockRepository.GenerateStub(IObject);

stubObj.Stub(a=>a.Get()).Return (Guid.Empty); //1.stub
stubObj.Stub(a=>a.Get()).Return (Guid.NewGuid()); //2.stub, should overwrite the first one?

this:

var value = stubObj.Get(); 

returns Guid.Empty, is this the correct behavior?

A: 

The following tests should pass:

// arrange
var stubObj = MockRepository.GenerateStub<IObject>();
stubObj.Stub(a => a.Get())
       .Return(Guid.Empty);
// act
var value = stubObj.Get();
// assert
Assert.AreEqual(Guid.Empty, value);

And if you want to return a new guid:

// arrange
var stubObj = MockRepository.GenerateStub<IObject>();
stubObj.Stub(a => a.Get())
       .Return(Guid.NewGuid());
// act
var value = stubObj.Get();
// assert
Assert.AreNotEqual(Guid.Empty, value);
Darin Dimitrov
thanks, but I dont want to arrange it once again just to change stubbed return value. Is there a kind of replay() method that replays just the specified stub object?
David
A: 

You have just programmed the Stub object for two separate calls. If you call stubObj.Get again, you should get what Guid.NewGuid generated. You can prepare your fake object for any number of invocations of different kinds. For this reason, it doesnt make sense to expect the last .Stub call for a given invocation to replace previous .Stubbings of that call.

In your test code, which should be short and neat, there should never be a case where you need to 'undo' such programming of the mock in the way you seem to want to do.

If what needs to be returned is a conditional thing which varies depending on other bits of your test code across multiple calls to this block of code, the last thing you want is magic happening to make readers have to figure out what you meant. If it's conditional, you should make it clear.

And then, when you've made it clear, refactor it out as you should not have Conditional Logic in Tests (see xUnit Test Patterns)

Ruben Bartelink
a nitpick: `Stub` only sets behavior, not expectations. It's unfortunate that RhinoMocks supports both record-replay-verify (where you would record expectations) and arrange-act-assert (where you would use stubs and separate assertions), it makes things very complicated to explain.
Wim Coenen
@wcoenen: I dont use RhinoMocks, and the cruft and confusion caused by the multiple models and other legacy cruft is the primary reason (I choose Moq as it's got one opinion). The main thing I wanted to get across in my response was that in general you're preparing for multiple calls and hence it would be surprising in most cases for the framework to go collapsing all of them down to one in the way that the questioner seems to be suggesting would make sense) . I'm happy for anyone to go in and edit my terminology to make it less confusing for RM users.
Ruben Bartelink
@wcoenen: I've tried to remove abuse of RM terminology by replacing it with generic terminology and would appreciate any additional edits to make things less confusing. (Was reading chapter 6 or PragProg's Unit Testing book [published 2007] in recent days which relegates RhinoMocks to a sidebar [and doesnt mention Moq] which won't help with the termninology. (I still stand over my main point which is that one shouldn't be modifying a stub/mock setup as part of a test or otherwise engaging in any such Conditional Logic In Test antipatterns.)
Ruben Bartelink
+1  A: 

If you want to return the empty guid a known number of times, the only thing you have to do is to tell RhinoMocks how many times to repeat it, e.g. the following test passes:

[Test]
    public void MultipleStubsTest()
    {
        var testMock = MockRepository.GenerateMock<ITest>();
        testMock.Stub(x => x.Get()).Return(Guid.Empty).Repeat.Once();
        testMock.Stub(x => x.Get()).Return(Guid.NewGuid());

        Assert.AreEqual(Guid.Empty, testMock.Get());
        Assert.AreNotEqual(Guid.Empty, testMock.Get());
    }

if you don't know how many times Get() will be called before the guid should change, than you can always use .Do() and code it there (please let me know if you need more details).

Grzenio
A: 

You seem to be after a mock, not a stub. Martin Fowler has an article explaining the difference.

A mock is an object where you first record a number of expectations and behaviors. After the record phase, you go to the replay phase which is the actual test where the mock will respond as recorded. Finally you can verify that the mock was used as expected. In this case you need this:

// record
var mock = MockRepository.GenerateMock<IObject>();
Expect.Call(mock.Get()).Return(Guid.Empty());
Expect.Call(mock.Get()).Return(Guid.NewGuid());

// replay (normally you would pass the mock to some object under test here)
mock.Replay();
var firstResult = mock.Get();
var secondResult = mock.Get();

// verify that the mock was used as expected by the object under test
mock.Verify();

A stub on the other hand, has no notion of a "replay script". It is simply set up with canned responses. The stub does remember what happened to is so that you can make assertions later with AssertWasCalled. This way, assertions can be kept separate from behavior. (In practice, RhinoMocks blurs the mock/stub distinction a bit because it allows you to do more with stubs than just canned responses).

When possible, you should avoid the use of mocks and the record-replay-verify approach. Tests with stubs and the arrange-act-assert approach are less brittle, and more clear because they keep a distinction between setting up behavior and assertions.

edit: since the confusion between stubs and mocks is a common one, I've blogged about the difference.

Wim Coenen
A: 

That's how you would do it. It passes on my machine.

    [TestMethod]
    public void Test()
    {
        stubObj = MockRepository.GenerateMock<IGuidTest>();

        stubObj.Stub(a => a.Get()).Repeat.Times(1).Return(Guid.Empty);
        stubObj.Stub(a => a.Get()).Repeat.Times(1).Return(Guid.NewGuid()); 

        Assert.AreEqual(Guid.Empty, stubObj.Get());
        Assert.AreNotEqual(Guid.Empty, stubObj.Get());

    }
    public IGuidTest stubObj;
    public interface IGuidTest
    {
        Guid Get();
    }
Usman