2

See this code:

TicketStoreService fakeTicketStoreService = 
                                        MockRepository.GenerateMock<TicketStoreService>(); 

fakeTicketStoreService.Expect(service => service.DoSomething(Arg.Is(new Guid()))
                      .Return(new Guid());

fakeTicketStoreService.DoSomething(Arg.Is(new Guid()));
fakeTicketStoreService.VerifyAllExpectations();

Note that DoSomething is a non-virtual methodcall in an autogenerated class which inherits from NO interface. So it shouldn't work, according to common knowledge. But it does.

Problem is that it's the only (non commercial) framework that can do this:

  • Rhino.Mocks works, and verification works too
  • FakeItEasy says it doesn't find a default constructor (probably just wrong exception message): No default constructor was found on the type SomeNamespace.TicketStoreService
  • Moq gives something sane and understandable: Invalid setup on a non-virtual (overridable in VB) member: service=> service.DoSomething
  • Nsubstitute gives a message System.NotSupportedException: Cannot serialize member System.ComponentModel.Component.Site of type System.ComponentModel.ISite because it is an interface.

I'm really wondering what's going on here with the frameworks, except Moq. The "fancy new" frameworks seem to have an initial perf hit too, probably preparing some Type cache and serializing stuff, whilst RhinoMocks somehow manages to create a very "slim" mock without recursion. I have to admit I didn't like RhinoMocks very well, but here it shines.. unfortunately.

So, is there a way to get that to work with newer (non-commercial!) mocking frameworks, or somehow get a sane error message (describing which of the 6 parameters I actually use was different in which way) out of Rhino.Mocks? And why can Rhino.Mocks achieve this, when clearly every Mocking framework states it can only work with virtual methods when given a concrete class?

*Let's not derail the discussion by talking about alternative approaches like Extract&Override or runtime-proxy Mocking frameworks like JustMock/TypeMock/Moles or the new Fakes framework, I know these, but that would be less ideal solutions, for reasons beyond this topic.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
hko
  • 923
  • 9
  • 17
  • MockRepository returns Mock? in case of Moq? – Anton Jun 26 '12 at 11:08
  • @Anton no, Mock, because TicketStoreService is a generated service without deriving from an interface.. the behavior of mock is understandable, problem is: why is RhinoMocks working and why can't the other frameworks do it? – hko Jun 26 '12 at 13:20

1 Answers1

4

You could try the Fody plugin Virtuosity which makes all members virtual using il weaving, so any dynamic proxy-based library will be able to work with it. Check the Fody example for what's involved hooking this up.

As to why Rhino Mocks works, I couldn't get it to:

[Test]
public void FieldTest() {
    var fakeTicketStoreService = MockRepository.GenerateMock<TicketStoreService>(); 
    fakeTicketStoreService.Expect(service => service.DoSomething(Arg.Is(new Guid()))).Return(new Guid());
    fakeTicketStoreService.DoSomething(Arg.Is(new Guid()));
    fakeTicketStoreService.VerifyAllExpectations();
}

public class TicketStoreService {
    public Guid DoSomething(Guid guid) { return guid; }
}

It fails with:

System.InvalidOperationException : Invalid call, the last call has been used or no call has been made (make sure that you are calling a virtual (C#) / Overridable (VB) method).
   at Rhino.Mocks.LastCall.GetOptions()
   at Rhino.Mocks.RhinoMocksExtensions.Expect(T mock, Function`2 action)

It looks like there is something else happening here. Are you able to post a stand-alone, minimal case that reproduces this?

Lastly, all the libraries you mentioned are open source, so it would be great to pick the one you like most and see if you can improve the error messages or performance. If you haven't contributed to open source before it can seem a bit daunting, but people on the projects are normally only too happy to try and help out anyone interesting in contributing. :)

Simon
  • 33,714
  • 21
  • 133
  • 202
David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • good answer, I unfortunately can't mark it as correct right now because it doesn't answer the question why RhinoMocks works there, but Fody seems so good, I didn't think I find something new in here. I worked around the issue with a wrapper that encapsulated the service calls, cleaner anyway (NSubstitute error messages >> RhinoMocks error messages), but of course +1 for that, and accepted answer once I'm sure noone else jumps in ;) – hko Jul 25 '12 at 20:05
  • I'm happy to take a crack at why Rhino Mocks works for your case, but you'll need to post an runnable example because I'm unable to reproduce it. :) – David Tchepak Jul 26 '12 at 00:00
  • I'll try to do that when I get the time, the service class itself is unfortunately a very big, partial class, so I had to strip it down incrementally.. again, will do it once I find the time. :) – hko Jul 26 '12 at 07:02