Re: [RhinoMocks] Problem mocking interface with generic method

81 views
Skip to first unread message
Message has been deleted

Ayende Rahien

unread,
Feb 27, 2008, 8:36:43 AM2/27/08
to Rhino...@googlegroups.com
This is a BCL bug.
You can use CreateRemotingMock to bypass it.

 
On 2/27/08, Paul Rendell <paul.r...@oxinst.com> wrote:

Hi,
If I try to create a mock or a stub that uses the following interface

public interface ITest
   {
       TView Show<TController, TView>(ViewInfo info, TController
controller)
           where TController : class, IController
           where TView : IView<TController>;
   }

then I get an exception when the test is run in debug mode. The call
stack associated with the exception is listed below. If the test is
run outside of the debugger or if the second constraint is removed,
the exception is not thrown. Can anyone help please?

Call stack :
failed: An attempt was made to load a program with an incorrect
format. (Exception from HRESULT: 0x8007000B)
      System.BadImageFormatException
      Message: An attempt was made to load a program with an
incorrect format. (Exception from HRESULT:           0x8007000B)
      Source: mscorlib
      StackTrace:
      at System.Reflection.Emit.TypeBuilder._TermCreateClass(Int32
handle, Module module)
      at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
      at System.Reflection.Em it.TypeBuilder.CreateType()
      at
Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
      at
Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
      at
Castle.DynamicProxy.Generators.InterfaceProxyWithTargetGenerator.GenerateCode(Type
proxyTargetType, Type[] interfaces, ProxyGenerationOptions options)
      at
Castle.DynamicProxy.DefaultProxyBuilder.CreateInterfaceProxyTypeWithoutTarget(Type
theInterface, Type[] interfaces, ProxyGenerationOptions options)
      at
Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyTypeWithoutTarget(Type
theInterface, Type[] interfaces, ProxyGenerationOptions options)
      at
Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type
theInterface, Type[] interfaces, ProxyGenerationOptions options,
IInterceptor[] interceptors)
      at
Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type
theInterface, Type[] interfaces, IInterceptor[] interceptors)
      at Rhino.Mocks.MockRepository.MockInterface(CreateMockState
mockStateFactory, Type type, Type[] extras)
      at Rhino.Mocks.MockRepository.CreateMockObject(Type type,
CreateMockState factory, Type[] extras, Object[]
argumentsForConstructor)
      at Rhino.Mocks.MockRepository.Stub(Type type, Object[]
argumentsForConstructor)
      at Rhino.Mocks.MockRepository.GenerateStub(Type type, Object[]
argumentsForConstructor)
      at Rhino.Mocks.MockRepository.GenerateStub[T](Object[]
argumentsForConstructor)



Peter Morris

unread,
Feb 27, 2008, 8:42:45 AM2/27/08
to Rhino...@googlegroups.com
Hi all

I still can't work out how to do this.....


IServiceProvider mockProvider =
Mocks.DoSomething<IServiceProvider>(originalServiceProvider);
mockProvider.GetService(typeof(IMyService)).Return(mockMyService);

Where a call to mockServiceProvider(typeof(IMyService)) will return a
specific value, but any other call will pass the request to
originalServiceProvider.


What do I have to do?

Thanks

Pete

Ayende Rahien

unread,
Feb 27, 2008, 8:52:52 AM2/27/08
to Rhino...@googlegroups.com
Um, can you should the entire test?

Peter Morris

unread,
Feb 27, 2008, 10:50:26 AM2/27/08
to Rhino...@googlegroups.com
Hi Ayende

The exact test mocks HttpContext etc and runs an action on a controller. It
checks that the changes are not saved to the DB if any constraints on the
object being updated are broken.


//First create a validation constraint that is always false
IConstraint constraint = new Constraint();
constraint.Expression = "false";
constraint.Name = "Cannot save";
IList<IConstraint> constraints = new List<IConstraint>();
constraints.Add(constraint);

//Mock the IConstraintProvider service to return this list
IConstraintProvider mockConstraintProvider =
Mocks.Create<IConstraintProvider>();
SetupResult.For(mockConstraintProvider.GetConstraints(null)).IgnoreParameters().Return(constraints);

//Mock the service provider to always use the mock constraint provider
IEcoServiceProvider mockServiceProvider =
Mocks.Create<IEcoServiceProvider>();
SetupResult.For(mockServiceProvider.GetEcoService<IConstraintProvider>()).Return(mockConstraintProvider);


Then I create my controller + mock the http stuff.....

AccountController controller =
AccountController.CreateTestController(mockServiceProvider);

(mock http)

Mocks.ReplayAll();
controller.SaveChanges(1, "Mr", "Peter", "Morris");

(asserts here).

The thing is that the controller action does something like this

01: Get the object instance from the DB
02: Apply the changes to the object
03: Use IConstraintProvider to get constraints and check the values are
valid
04: Save changes or undo changes.


The persistence framework I use depends on IEcoServiceProvider for creating
objects, reading cache values, plus all sorts of other stuff. So instead of
having to do a SetupResult.For<> on every possible combination I would just
like my mockServiceProvider to pass the request onto the RealServiceProvider
unless I explicitly set a result, e.g.


IEcoServiceProvider mockServiceProvider =
Mocks.SomethingHere<IEcoServiceProvider>(RealServiceProvider);


I hope that's clear :-)


Pete

Ayende Rahien

unread,
Feb 27, 2008, 10:54:23 AM2/27/08
to Rhino...@googlegroups.com

Peter Morris

unread,
Feb 28, 2008, 4:40:39 AM2/28/08
to Rhino...@googlegroups.com
Hi Ayende

> http://www.ayende.com/Wiki/(S(xy3xruulb1ifjlnfmzhwghvy))/Rhino+Mocks+Partial+Mocks.ashx

I have seen this before. It seems to mock a class type, I need to partially
mock a specific instance implementing an interface.

It needs to be a specific instance because there are
IObjectFactory : Creates objects
ICacheService : Holds property values of the objects
IOclService : Performs OCL evaluation on the cache

and so on. The IEcoServiceProvider is always passed to me, I never create
it myself. As a consequence I need to partially mock an instance of (some
unknown object type) which implements IEcoServiceProvider and by pass only
some of the calls to GetEcoService<T>().

At the moment I am having to set up mocks for every service and pass them on
to the original service, and that's too much work :-)

Pete

Ayende Rahien

unread,
Feb 28, 2008, 10:12:42 AM2/28/08
to Rhino...@googlegroups.com
Expect.Call( foo.Bar() ).Do( (MyDelegateType) fooService.Bar );

Peter Morris

unread,
Feb 28, 2008, 10:35:01 AM2/28/08
to Rhino...@googlegroups.com
> Expect.Call( foo.Bar() ).Do( (MyDelegateType) fooService.Bar );

My point is that there are so many services to pass through, not only that
but I would also need to be aware of any additional services being added
etc. All I want to do is to say "Operate as normal, but when
IConstraintProvider is retrieved return this mock object instead".

If this is not already possible I hope you will consider it as a feature,
e.g.


IEcoServiceProvider mockProvider =
mocks.CreatePassThrough<IEcoServiceProvider>(RealProvider);
Expect.Call(mockProvider.GetEcoService<IConstraintProvider>()).Return(mockConstraintProvider);

2 lines of code, does everything required. To mock the object factory +
cache + persistence + transactionservice etc would all be too much work, and
adding passthroughs manually would not only be a lot of work but also prone
to mistakes whenever a new service is added (which I might not even be aware
of).

Pete

Ayende Rahien

unread,
Feb 28, 2008, 10:55:17 AM2/28/08
to Rhino...@googlegroups.com
Peter,
Feel free to submit a patch.

 
On 2/28/08, Peter Morris <mrpm...@gmail.com> wrote:

Peter Morris

unread,
Feb 28, 2008, 11:42:27 AM2/28/08
to Rhino...@googlegroups.com
> Peter,
> Feel free to submit a patch.

Feeling free I have no problem. Feeling able.....hmm :-)

Seriously though I have only been using your mocking stuff for a short
period, I think trying to alter the code at this stage would be a bit
premature.


Pete

Ayende Rahien

unread,
Feb 28, 2008, 12:00:33 PM2/28/08
to Rhino...@googlegroups.com
try it, you'll be surprised.

On 2/28/08, Peter Morris <mrpm...@gmail.com> wrote:

Peter Morris

unread,
Feb 28, 2008, 12:43:16 PM2/28/08
to Rhino...@googlegroups.com
Okay, taking a look wont do any harm I suppose :-)

I can't see info on your homepage regarding your trunk url + the procedure
for submitting patches. I presume you have it documented somewhere, would
you mind sending the URL please?


Thanks

Pete

Ayende Rahien

unread,
Feb 28, 2008, 12:46:55 PM2/28/08
to Rhino...@googlegroups.com

Peter Morris

unread,
Feb 29, 2008, 3:47:10 PM2/29/08
to Rhino...@googlegroups.com
This stuff is totally alien to me :-)

I overloaded MockInterface() to allow an additional parameter "object
target". The

//Provide original behaviour
private object MockInterface(CreateMockState mockStateFactory, Type type,
Type[] extras)
{
return MockInterface(mockStateFactory, type, extras, null);
}

private object MockInterface(CreateMockState mockStateFactory, Type type,
Type[] extras, object target)
{
object proxy;
List<Type> implementedTypesForGenericInvocationDiscoverability = new
List<Type>(extras);
implementedTypesForGenericInvocationDiscoverability.Add(type);
RhinoInterceptor interceptor = new RhinoInterceptor(this, new
ProxyInstance(this,
implementedTypesForGenericInvocationDiscoverability.ToArray()));

ArrayList types = new ArrayList();
types.AddRange(extras);
types.Add(typeof(IMockedObject));


if (target == null)
//Original behaviour
proxy =
generator.CreateInterfaceProxyWithoutTarget(type,
(Type[])types.ToArray(typeof(Type)), interceptor);
else
//New behaviour
proxy =
generator.CreateInterfaceProxyWithTargetInterface(type, target, new
IInterceptor[] { interceptor });


IMockState value = mockStateFactory((IMockedObject)proxy);
proxies.Add(proxy, value);
return proxy;
}


I have no idea if this is the correct approach or not but it's my first
attempt :-)

Anyway, my problem is that CreateInterfaceProxyWithTargetInterface returns
an object that cannot be cast to IMockedObject. I also tried

proxy =
generator.CreateInterfaceProxyWithTarget(type, extras, target, new
IInterceptor[] { interceptor } );


and that does exactly the same thing.

Some pointers would be appreciated :-)

Otter

unread,
Mar 27, 2008, 10:25:39 AM3/27/08
to Rhino.Mocks
Hello Ayende,

Can you explain or provide a link to any existing documentary on why
CreateMockWithRemoting is able to get around the bug that prevents one
from mocking interfaces like the one mentioned here?

Everything I've found on the subject says that Remoting mocks are
intended to mock objects that derive from MarshalByRefObject, but none
mentions the fact that it can be used to get around that BCL bug.

Thanks!

On Feb 27, 9:36 am, "Ayende Rahien" <aye...@ayende.com> wrote:
> This is a BCL bug.
> You can use CreateRemotingMock to bypass it.
>

Ayende Rahien

unread,
Mar 27, 2008, 10:32:30 AM3/27/08
to Rhino...@googlegroups.com
Remoting mocks can also mock interfaces.
That works around the BCL bug in generating derived classes for this issue.
Reply all
Reply to author
Forward
0 new messages