63

I would like to set up a return value

_stubRepository.Stub(Contains(null)).IgnoreArguments().Return(true);

but then in a specific test, override that expectation to return false.

Something like:

_stubRepository.ClearExpectations();  //<- this does not exist, I'm just making something up
_stubRepository.Stub(Contains(null)).IgnoreArguments().Return(false);

Notice, I do not want the expectation to return false on the second call, I want to override the first expectation.

This would help simplify my testing scenario greatly.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
George Mauer
  • 117,483
  • 131
  • 382
  • 612

3 Answers3

79

There are three ways:

You can reset the expectations by using BackToRecord

I have to admit that I never really used it because it is awkward.

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

Edit: Now I use it sometimes, it is actually the cleanest way. There should be an extension method (like Stub) which does it - I think it just got forgotten. I would suggest to write your own.

You can use Repeat.Any()

It 'breaks' the order of the stubbed definition and "overrides" previous definitions. But it's somehow implicit. I use it sometimes because it is easy to write.

_stubRepository.Stub(x => x.Contains(null))
  .IgnoreArguments()
  .Return(false)
  .Repeat.Any();

You can create a new mock

Trivial, but explicit and easy to understand. It is only a problem if you want to keep plenty of definitions and only change one call.

_stubRepository = MockRepository.GenerateMock<IRepository>();
_stubRepository.Stub(x => x.Contains(null))
  .IgnoreArguments()
  .Return(false);
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • In internal Rhino Mock speak, using Repeat. Any creates a repeatable expectation, which trump normal expectations during playback. I recommend using BackToRecord, though. – Richard Szalay Apr 20 '09 at 21:15
  • Ah, figured it all out except for the Replay() call. – George Mauer Apr 20 '09 at 21:29
  • 1
    This is a thing only really known by people using RhinoMocks since 3.4 or older. RhinoMocks worked with Record-Replay, this means that a mock had to be set into replay mode explicitly. With 3.5, fortunately this has gone, mocks are always in replay mode (at least for the custom code). Until you put it back to record mode - except of clearing expectations I see no reason to do it. I already wanted to write a patch for this two lines to comfortably reset the expectations. – Stefan Steinegger Apr 20 '09 at 21:37
  • 5
    public static void ResetExpectations(this T mock) where T : class { mock.BackToRecord(BackToRecordOptions.Expectations); mock.Replay(); } I've got a bunch of handy rhino mocks extensions that I should put on google code actually.. – George Mauer Apr 20 '09 at 21:51
  • 4
    What if you've set several expectations on the mock object and you'd want to reset only one of them? Is the Repeat.Any option the only one? – Amittai Shapira Oct 06 '11 at 20:18
  • You could also avoid resetting expectations using `AssertWasCalled` instead of `Expect` and `WhenCalled` instead of `Return`. There is also a `TentativeReturn`, which I didn't use until now. I probably should write another paragraph about avoiding the problem ... – Stefan Steinegger Oct 07 '11 at 12:25
22

For these situations, I created a simple RinoMocks extention method to better show the intent of the stub and promote readability.

public static void OverridePrevious<T>(this IMethodOptions<T> options)
{
    options.Repeat.Any();
}

So instead of a cryptic call like the following that may require a comment:

[SetUp]
public void Setup()
{
    carStub.Stub(x => x.Model).Return("Model1");
    carStub.Stub(x => x.Model).Return("Model2");
}

[Test]
public void SomeTest()
{
    //Arrange
    //overrides previous stubs that were setup for the Model property
    carStub.Stub(x => x.Model).Return(null).Repeat.Any();

    //Act
    //Assert
}

You can get a more readable test that better shows the intent of the .Repeat.Any() calls:

carStub.Stub(x => x.Model).Return(null).OverridePrevious();
MoMo
  • 8,149
  • 3
  • 35
  • 31
  • 5
    In order to keep the option open to chain the stub configuration, shouldn't the extension method return an IMethodOptions? `public static IMethodOptions OverridePrevious(this IMethodOptions options) { return options.Repeat.Any(); }`. – PHeiberg Nov 22 '12 at 12:04
  • 2
    @PHeiberg - I haven't tried it or had the need to keep the 'chain open' but yes I suppose you are right. Good point. – MoMo Nov 26 '12 at 16:54
  • I like OverridePrevious as void - it forces it to the end. – user2864740 Dec 04 '16 at 22:16
8

For the sake of the community I'll add this to add to Stefan's list of options above:

If the return value needs to be changed frequently I find it clean and efficient to use a closure as follows.

bool returnValue = true;
_stubRepository.Stub(x => x.Contains(null)).IgnoreArguments().Do(new Func<bool>(() => {
    return returnValue;
}));

returnValue = false;
// Calls to Contains now return false;

returnValue = true;
// Calls to Contains now return true;

The lambda expression will be executed every time Contains is called and because we created a closure referencing returnValue, it will always look up the current value of returnValue.

Micah Hahn
  • 400
  • 2
  • 11