Search code examples
c#unit-testingmockingnunitrhino-mocks

Rhino mocks stubbing twice the same function doesn't work as expected


I've started to work with Rhino recently and encountered a very unexpected behavior that I couldn't overcome.

The issue is that I have an infrastructure of my stubs and in one of my tests I needed to change one of the predefined stubs (in my infrastructure) to return a different value then the default one.

I've reproduce the issue in the following code:

[TestFixture]
public class UnitTest1
{
    private IWorker _worker;

    [SetUp]
    void Setup()
    {
        _worker = MockRepository.GenerateStub<IWorker>();
        _worker.Stub(w=>w.DoWork()).Return(0);
    }

    [Test]
    public void DoWork_StubbingFunctionTwice_CallingTheLastStub()
    {
        int expected = 1;
        _worker.Stub(w => w.DoWork()).Return(expected);
        int actual =_worker.DoWork();
        Assert.AreEqual(expected, actual);
    }

}

public interface IWorker
{
    int DoWork();
}

someone knows why the Rhino stubs are behaving this way and more important how can I solve it in the cleanest way?


Solution

  • When you specify a behavior on a fake object in RhinoMocks without any constraint(RepeatOnce and etc..) the behavior will stay and you won't be able to override it.(actually option 1 shows how you can...)

    In your case you specify a specific behavior in the Setup method:

    _worker.Stub(w=>w.DoWork()).Return(0);
    

    The above line will execute before _worker.Stub(w => w.DoWork()).Return(expected);.

    The best practice for SetUp / TestInitialize attributes is to group inside them only settings which you want to apply in all test methods.

    By default any method with a return value returns the default(T) so you can remove _worker.Stub(w=>w.DoWork()).Return(0); and then any thing will work.

    If your real case is more complex:

    Option1: clear the mock

    // clear expectations, an enum defines which
    _worker.BackToRecord(BackToRecordOptions.All);
    
    // go to replay again.
    _worker.Replay();
    

    Option2: limit the behavior

    _worker.Stub(w=>w.DoWork()).Return(0).Repeat.Once(); // or the max time you need...
    

    and then add a loop inside the test method:

    for(...; i < num of time; ...)
        _worker.DoWork();
    

    Option3: create a new fake and CUT(class under test)