Rhino Mocks 3.5A feature to be proud of - seamless Do()
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:
- (30 Jun 2008) Getting closer to conclusion
- (29 Jun 2008) The role of Stub vs. Mock
- (29 Jun 2008) To be strict or not?
Comments
What is invocation? IInvocation or is it wrapped up?
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.
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....
Chris,
Wrapped up, I don't want to expose the underlying implementation to the user
Eddie ,
No, that is a bug.
Chris.
Use MockRepository instance methods instead. Then you can call mocks.VerifyAll();
This feature has been in Rhino Mocks since 2.0
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()
{
}
You could always do that (as far back as 2.0).
The problem was the syntax was lousy.
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?
Do( (Func<int, string>) (x) = > a);
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 :)
That is already there.
It is ugly and unseemly
Comment preview