5

So, using NUnit and RhinoMocks:

//Defines basic behavior of all persistable domain objects
public interface IDomainObject {...}

//defines domain objects specific to the Security DB
public interface ISecurityDomainObject : IDomainObject {...}

//Defines a basic transactional data Repository; there are multiple implementors
//which each close TRest to the interface that defines their DB's domain classes
public interface IRepository<TRest> : IDisposable where TRest:IDomainObject
{
    IUnitOfWork BeginUnitOfWork();
    void CommitUnitOfWork(IUnitOfWork unitOfWork);
    void RollBackUnitOfWork(IUnitOfWork unitOfWork);        
    void Save<T>(T domainObject, IUnitOfWork unitOfWork) where T : class, TRest;        
    IQueryable<T> QueryFor<T>(IUnitOfWork unitOfWork) where T :class, TRest;
}

public interface ISecurityRepository:IRepository<ISecurityDomainObject> {}

public class SecurityRepository:ISecurityRepository

...

//This line breaks when run in an NUnit test
var securityRepository = MockRepository.GenerateMock<ISecurityRepository>();
...

The error I get is:

System.TypeLoadException : Method 'Save' on type 'ISecurityRepositoryProxyb8e21deb3cb04067a01ac5b63f7045af' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' tried to implicitly implement an interface method with weaker type parameter constraints.
at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
at System.Reflection.Emit.TypeBuilder.CreateType()
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 interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, 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.DynamicMock(Object[] argumentsForConstructor)
at Rhino.Mocks.MockRepository.<>c__DisplayClass7`1.<GenerateMock>b__6(MockRepository r)
at Rhino.Mocks.MockRepository.CreateMockInReplay(Func`2 createMock)
at Rhino.Mocks.MockRepository.GenerateMock(Object[] argumentsForConstructor)
at CSHD.Tests.Unit.Presentation.LoginTests.TestAuthenticationFails() in LoginTests.cs: line 138 

When attempting to generate the mock against the concrete class, I get a similar error, this time on the QueryFor() method. If I attempt to redefine the methods that use TRest in the ISecurityRepository interface, I get a "System.BadImageFormatException : An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)" which looks like a step backwards.

I think the core problem is that RhinoMocks is getting confused by the generic parameters being used as generic type restrictions. I have no clue exactly where it's being confused and therefore I don't know how or if I can unconfuse it. I have adequate integration test coverage that I could ignore these failing unit tests if I absolutely have to, but obviously I'd rather fix them if I can. Your thoughts?

KeithS
  • 70,210
  • 21
  • 112
  • 164

2 Answers2

6

It looks like this is a known issue caused by Castle.DynamicProxy that is fixed in the latest trunk of that project, but still broken in the latest Rhino Mocks release:

http://groups.google.com/group/rhinomocks/browse_thread/thread/2c1b53bf66b77b8e/ad09a6cd1e304a93

If you're feeling adventurous you can build your own Rhino Mocks with the latest DynamicProxy and it should be fixed.

Ergwun
  • 12,579
  • 7
  • 56
  • 83
  • Thanks. I think I'll just ignore the test for now, and keep an eye out for the next Rhino Mocks build. As long as it will be fixed as of the next release I don't feel so bad about doing so. – KeithS Dec 08 '10 at 00:27
  • Just to confirm Ergwun's suggestion, I had the same issue. I checked out the Rhino source and updated everything to the trunk version Castle and the bug is indeed fixed. – FinnNk Jan 19 '11 at 09:34
1

Looks like Castle Dynamic Proxy (which Rhino Mocks uses for proxy generation) is not generating the proxy class correctly given the way that you've defined your generic arguments. You can generate a proxy (and hence a mock) if you define your IRepository like this instead:

public interface IRepository<T> : IDisposable where T : class, IDomainObject
{
    IUnitOfWork BeginUnitOfWork();
    void CommitUnitOfWork(IUnitOfWork unitOfWork);
    void RollBackUnitOfWork(IUnitOfWork unitOfWork);        
    void Save(T domainObject, IUnitOfWork unitOfWork);        
    IQueryable<T> QueryFor(IUnitOfWork unitOfWork);
}

If you really need it defined the other way, you'll have to file a bug with Rhino Mocks.

James Kovacs
  • 11,549
  • 40
  • 44
  • I kinda really need it defined the way I have it. I have several implementations of IRepository that each work against a different DB (I didn't make up the schema, I just inherited it) and each implementation works with a different subset of the domain. The definition I have ensures at compile-time that code cannot attempt to ask a Repository to work with an object that isn't part of its schema. What you have there would basically require a repository per domain object, as T cannot be defined as ISecurityDomainObject because Save and QueryFor couldn't know the concrete type (which they need). – KeithS Dec 07 '10 at 23:47
  • Fair enough. The problem is that the exception isn't happening in Rhino Mocks, but in Castle DynamicProxy. I checked Moq (which uses a more recent version of Castle DynamicProxy than Rhino MOCKS) and it too suffers from this limitation. At the moment, this interface isn't mockable using Rhino Mocks or Moq. For this to be fixed, Castle DynamicProxy will have to fix the underlying issue. Wish I had a better answer for you. – James Kovacs Dec 08 '10 at 00:48