Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 18 | Comments: 87

filter by tags archive

Rhino Mocks 3.5A feature to be proud of - seamless Do()

time to read 2 min | 384 words

image One of the major pain points for me in Rhino Mocks has always been the Callback() and Do() delegates. I designed them at the 1.1 days, when we didn't have such things as anonymous delegates or lambda. As a result, they required you to explicitly pass a delegate type, where a pain to use and major eye sore.

For a long time, I accepted that there is nothing to do. The fault was with the annoying compiler, and like many other language limitations, you just have to live with it.

I was writing something today and I really want to be able to use Do(), but I had zero intention of getting into that mess again. And suddenly it hit me.

I was doing it all wrong. I should be trying to get the user to do the work, instead of the compiler. Here is the deal, the original design of Do() include matching the called method signature. But most often, you don't care about that.

Once I realized that, I realized that my issue all along was the insistence that it should be strongly typed. There isn't any really reason why this should be so. And once you let go of that, you can get this really cool syntax:

[Test]
public void Can_use_when_called_to_exceute_code_when_exceptation_is_matched_ \
                 without_stupid_delegate_sig_overhead()
{
	var wasCalled = true;
	var stub = MockRepository.GenerateStub<IDemo>();
	stub.Stub(x => x.StringArgString(Arg.Is("")))
		.Return("blah")
		.Do(delegate { wasCalled = true; });
	Assert.AreEqual("blah", stub.StringArgString(""));
	Assert.IsTrue(wasCalled);
}

Oh, and it even allows you to directly modify the method return value:

[Test]
public void Can_modify_return_value()
{
	var wasCalled = true;
	var stub = MockRepository.GenerateStub<IDemo>();
	stub.Stub(x => x.StringArgString(Arg.Is("")))
		.Return("blah")
		.Do(invocation => invocation.ReturnValue = "arg");
	Assert.AreEqual("arg", stub.StringArgString(""));
	Assert.IsTrue(wasCalled);
}

You can also inspect the method arguments and make decisions based on that:

[Test]
public void Can_inspect_method_arguments()
{
	var wasCalled = true;
	var stub = MockRepository.GenerateStub<IDemo>();
	stub.Stub(x => x.StringArgString(null))
		.IgnoreArguments()
		.Return("blah")
		.Do(invocation => Assert.AreEqual("foo", invocation.Arguments[0]));
	Assert.AreEqual("blah", stub.StringArgString("foo"));
	Assert.IsTrue(wasCalled);
}

Overall, this just equaled to the entire AAA syntax in my level of pain reduction.

More posts in "Rhino Mocks 3.5" series:

  1. (30 Jun 2008) Getting closer to conclusion
  2. (29 Jun 2008) The role of Stub vs. Mock
  3. (29 Jun 2008) To be strict or not?

Comments

Chris Ortman

What is invocation? IInvocation or is it wrapped up?

Eddie Garmon

Is there any reason that you start with wasCalled as true? Validating it is true at the end is rather pointless given the initial truthfullness.

Chris Canal

I like the new syntax, it almost stopped me moving to Moq, however, is there no way to centralise verifying all the expectations?

With Moq you get the MockFactory to pull mocks out and allows you to call VerifyAll. This seems to be missing with Rhino 3.5? Or am I being stupid?

I'm not a huge fan of using a callback, it adds noise IMO....

Ayende Rahien

Chris,

Wrapped up, I don't want to expose the underlying implementation to the user

Ayende Rahien

Chris.

Use MockRepository instance methods instead. Then you can call mocks.VerifyAll();

This feature has been in Rhino Mocks since 2.0

Shawn Neal

This is great especially the returnValue you've added to the Do syntax. I can finally reproduce all of the Moq QuickStart examples using RhinoMocks, the Expect Call With Reference Lazy Evaluate was the last one I was having problems reproducing using RhinoMocks. This now works:

[Test]

public void ShouldExpectCallWithReferenceLazyEvaluate()

{

int a = 25;

var mock = MockRepository.GenerateMock<IFoo>();


mock.Expect(x => x.DoArgument(Arg<string>.Matches(arg => arg == a.ToString())))

    .Return(a)

    .Do(invocation => invocation.ReturnValue = a);


a = 10;


Assert.AreEqual(10, mock.DoArgument("10"));

}

Ayende Rahien

You could always do that (as far back as 2.0).

The problem was the syntax was lousy.

Shawn Neal

How would you go about lazily returning a value in an older version of RhinoMocks? Moq's "Returns" statement takes a value or a delegate (iirc), but RhinoMocks "Return" takes only a value (int in my example). Is there another way in RhinoMocks to set the return value that I'm not aware of?

Ayende Rahien

Do( (Func<int, string>) (x) = > a);

Daniel Cazzulino

Wouldn't it be even better if Do had generics overloads to receive the argument types directly too?

Like:

.Do<int, string>(x => a);

I think that makes the lambda syntax cleaner.

It also converges with Moq Callback syntax :)

Ayende Rahien

That is already there.

It is ugly and unseemly

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

  1. Buffer allocation strategies: A possible solution - 2 days from now
  2. Buffer allocation strategies: Explaining the solution - 3 days from now
  3. Buffer allocation strategies: Bad usage patterns - 4 days from now
  4. The useless text book algorithms - 5 days from now
  5. Find the bug: The concurrent memory buster - 6 days from now

There are posts all the way to Sep 11, 2015

RECENT SERIES

  1. Find the bug (5):
    20 Apr 2011 - Why do I get a Null Reference Exception?
  2. Production postmortem (10):
    03 Sep 2015 - The industry at large
  3. What is new in RavenDB 3.5 (7):
    12 Aug 2015 - Monitoring support
  4. Career planning (6):
    24 Jul 2015 - The immortal choices aren't
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats