Ordering method return values with Rhino-Mock stub

2019-02-12 19:24发布

I've started experimenting with Rhino-Mocks (3.6) while reading Roy Osherove's The Art of Unit Testing. He has an example that demonstrates that a mocked method can be scripted to return different results when called twice with the same parameter:

   [Test]
    public void ReturnResultsFromMock()
    {
        MockRepository repository = new MockRepository();
        IGetRestuls resultGetter = repository.DynamicMock<IGetRestuls>();
        using(repository.Record())
        {
            resultGetter.GetSomeNumber("a");
            LastCall.Return(1);

            resultGetter.GetSomeNumber("a");
            LastCall.Return(2);

            resultGetter.GetSomeNumber("b");
            LastCall.Return(3);

        }

        int result = resultGetter.GetSomeNumber("b");
        Assert.AreEqual(3, result);

        int result2 = resultGetter.GetSomeNumber("a");
        Assert.AreEqual(1, result2);

        int result3 = resultGetter.GetSomeNumber("a");
        Assert.AreEqual(2, result3);
    }

This works fine. But when I try the same thing with a Stub, and a method that accepts and returns a string, I am not able to generate the second return value:

    [Test]
    public void StubMethodWithStringParameter_ScriptTwoResponses_SameResponseReceived()
    {
        MockRepository mocks = new MockRepository();
        IMessageProvider stub = mocks.Stub<IMessageProvider>();

        using (mocks.Record())
        {
            stub.GetMessageForValue("a");
            LastCall.Return("First call");
            stub.GetMessageForValue("a");
            LastCall.Return("Second call");
        }

        Assert.AreEqual("First call", stub.GetMessageForValue("a"));
        Assert.AreEqual("Second call", stub.GetMessageForValue("a"));
    }
}

public interface IMessageProvider
{
    string GetMessage();
    string GetMessageForValue(string value);
}

This test is failing, because "First Call" is received for both calls. I've tried several wrinkles of syntax (Using mocks.Ordered(), SetResult, Expect etc.), but am still unable to get the second result to appear.

Am I doing something wrong, or is this a limitation with Rhino-Mocks? I've checked this blog post, but the suggested syntax did not resolve my issue.

2条回答
等我变得足够好
2楼-- · 2019-02-12 19:39

I think if you are working with stubs, using Expect does not fit as you do not want an expectation but a replacement for your dependency.

So I believe if you use the stub syntax it makes more sense:

stub.Stub.(s=>s.GetMessageForValue("a"))
                            .Return("First call").Repeat.Once(); 

stub.Stub.(s=>s.GetMessageForValue("a"))
                            .Return("Second call").Repeat.Any;
查看更多
不美不萌又怎样
3楼-- · 2019-02-12 19:57

The bit you're missing is to tell the stub that the first value should only be returned once:

...
using (mocks.Record())
{
    stub.GetMessageForValue("a");
    LastCall.Return("First call").Repeat.Once();
    stub.GetMessageForValue("a");
    LastCall.Return("Second call");
}

Of course your "Second call" really means "Second-or-subsequent call" unless you impose other restrictions with Repeat.

You might also consider using the newer Arrange, Act, Assert (AAA) syntax RhinoMocks now offers:

[Test]
public void StubMethodWithStringParameter_ScriptTwoResponses_SameResponseReceived()
{
    IMessageProvider stub = MockRepository.GenerateStub<IMessageProvider>();

    stub.Expect(mp => mp.GetMessageForValue("a"))
        .Return("First call")
        .Repeat.Once();
    stub.Expect(mp => mp.GetMessageForValue("a"))
        .Return("Second call");

    Assert.AreEqual("First call", stub.GetMessageForValue("a"));
    Assert.AreEqual("Second call", stub.GetMessageForValue("a"));
}

It's a little more concise and generally saves you from having to worry about the record-playback-assert state of the stub. Derick Bailey wrote an article about using Repeat on Los Techies. It also happens to use the AAA syntax).

查看更多
登录 后发表回答