Ayende @ Rahien

It's a girl

Rhino Mocks Challenge: Implement This Feature

Okay, let us see if this approach works...

Here is a description of a feature that I would like to have in Rhino Mocks (modeled after a new feature in Type Mock). I don't consider this a complicated feature, and I would like to get more involvement from the community in building Rhino Mocks (see the list of all the people that helped get Rhino Mocks 3.5 out the door).

The feature is fluent mocks. The idea is that this code should work:

var mockService = MockRespository.GenerateMock<IMyService>();
Expect.Call( mockService.Identity.Name ).Return("foo");

Assert.AreEqual("foo", mockService.Identity.Name);

Where identity is an interface.

The best place to capture such semantics is in the RecordMockState.

Have fun, and send me the patch :-)

Comments

Steven Harman
10/08/2008 12:19 AM by
Steven Harman

Is this really something you __want in Rhino.Mocks?

I know what you're getting at, and I realize that TypeMock has it... but recursive mocks like this don't cause you to feel the pain of the Demeter violation in the code under test. What happened to the idea (or was it just an ideal) of reinforcing good practices by making it painful to... well... not such good things - like breaking LoD?

I'm just curious why you've decided on this particular feature? (and its OK if your answer was just that you threw a dart at the dartboard to pick one.) :)

pb
10/08/2008 02:42 AM by
pb

I find it interesting that calls to action have so few responses while the philsophical debates over buses and what not have so many... I'll wait a few days and if no one answers then I'll take a crack at it.

pb
10/08/2008 03:42 AM by
pb

What is Identity.Name? Can you provide a sample interface implementation to go with this? I'm unsure if this is a method call or what exactly .Identity is.

pb
10/08/2008 03:59 AM by
pb

Er, guess I should have re-read it a few times, this?

public interface ISomeInterace {

public string Name { get; set; }

}

public interface IMyService {

public class ISomeInterace Identity { get; set; }

}

Dennis
10/08/2008 04:46 AM by
Dennis
var mockService = MockRespository.GenerateMock

<imyservice();

Expect.Call( mockService.Identity.Name ).Return("foo");

This is way too subtle for me :S Why should interfaces in a mock have different semantics from strings and ints?

I could understand if I asked specifically for a MockRespository.GenerateDeepMock <imyservice(); and it was documented that this would mock all non-sealed classes that was not set.

Jeremy Gray
10/08/2008 04:49 AM by
Jeremy Gray

I think I'm with Steven on this one. Historically, we've had Type Mock over on the most-effective-for-legacy-code side of the fence and everyone else over on the more-strongly-encouraging-good-practices side of the fence and a feature like this seems at first like a step towards, if not down, a slippery-slope.

meowth
10/08/2008 09:51 AM by
meowth

Do you want a feature such an Isolator's recursive mocking?

Ryan Gray
10/08/2008 03:18 PM by
Ryan Gray

In response to Steve, Jeremy, et. al., the utility of this feature is in the ability to mock fluent interfaces. The Law of Demeter goes out the window when people are using fluent interfaces; we expect the client to make deep calls.

D. P. Bullington
10/08/2008 08:07 PM by
D. P. Bullington

"I would like to get more involvement from the community in building Rhino Mocks "

No, you are just getting lazy. I am not your helpdesk. :P

Jeremy Gray
10/08/2008 09:11 PM by
Jeremy Gray

@Ryan - True enough. The point once missed is now taken and mine conceded. :)

pb
10/09/2008 01:49 AM by
pb

Simon, don't you think the test code in that thread is much more verbose and more difficult to read than what is asked for here?

Simone Busoli
10/09/2008 07:26 AM by
Simone Busoli

I just pointed out that it was already discussed, and solved something more complex than the scenario proposed here, which it solves as well. If you want code trivial to read, you should stay away from RhinoMocks.

Jeff Brown
10/22/2008 07:44 AM by
Jeff Brown

So you just want the Record / Replay switch to occur implicitly where actions inside of Expect should be performed in record mode and actions outside of it should not?

The delegate-based syntax is easy of course:

Expect.Call(() => Foo.DoSomething()).Return("Blah");

The Call method can just enter record mode, evaluate the delegate (or disassemble the lambda expression) and return to replay mode.

But if you really want:

Expect.Call(Foo.DoSomething()).Return("Blah");

Then we have to find another way to run code before the mocked expression to enter record mode.

Here's one possible hack:

public static class Expect

{

public static Action

<object Call

{

    get

    {

        BeforeCall();

        return AfterCall;

    }

}


private static void BeforeCall()

{

     // enter record mode

}


private static void AfterCall(object dummyResult)

{

     // return to playback mode

}

}

So the "Call" method can be represented as a property that returns a delegate with a side-effect of doing something before the call runs.

Ugh. A property with a side-effect. Maybe we're thinking about this wrong. Let's try something else.

Consider the behavior of a stubbed method. (Without loss of generality, properties, events can follow the same logic.)

  1. Initially the stubbed method should have no real behavior. When you call it, it should do nothing and just return default values to the caller. So that means that as long as we have not assigned any behavior to the stubbed method, calling it ought to be completely harmless.

  2. If we've already set behaviors for a stubbed method (like throwing an exception), then we usually will not try to set additional behaviors. I know Rhino.Mocks allows the user to record multiple ordered or unordered expectations, but we could actually live without that feature...

So consider this code:

Expect.Call(Foo.DoSomething()).Return("bar");

Assert.AreEqual("bar", Foo.DoSomething());

Here's one way to make the fluent syntax work given the C# order of evaluation:

  1. Foo.DoSomething() gets evaluated. Well, it's just a stubbed method so it does nothing of its own, but it does remember that it was called.

  2. The surrounding fluent expression involving Expect.Call(...) or LastCall gets evaluated. It knows that the previously evaluated stubbed method was Foo.DoSomething(). So it sets its behavior.

  3. Foo.DoSomething() runs again. This time we have some behavior associated with it, which we perform for real.

Jeff Brown
10/22/2008 07:46 AM by
Jeff Brown

Oops, I think I missed the point. You're actually talking about implementing recursive mocks, not fussing over the syntax.

Ahh well.

Ayende Rahien
10/22/2008 10:31 AM by
Ayende Rahien

Jeff,

The property that return a delegate with a side effect is evil, but exactly what I need in this scenario, thanks

Comments have been closed on this topic.