Ayende @ Rahien

It's a girl

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

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.

Comments

Chris Ortman
07/02/2008 12:44 PM by
Chris Ortman

What is invocation? IInvocation or is it wrapped up?

Eddie Garmon
07/02/2008 02:40 PM by
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
07/02/2008 03:18 PM by
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
07/02/2008 06:00 PM by
Ayende Rahien

Chris,

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

Ayende Rahien
07/02/2008 06:03 PM by
Ayende Rahien

Eddie ,

No, that is a bug.

Ayende Rahien
07/02/2008 06:05 PM by
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
07/02/2008 11:51 PM by
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
07/02/2008 11:55 PM by
Ayende Rahien

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

The problem was the syntax was lousy.

Shawn Neal
07/03/2008 12:12 AM by
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
07/03/2008 12:39 AM by
Ayende Rahien

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

Daniel Cazzulino
07/05/2008 06:06 PM by
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
07/09/2008 06:43 AM by
Ayende Rahien

That is already there.

It is ugly and unseemly

Comments have been closed on this topic.