2010-12-07 3 views
5

Ainsi, en utilisant NUnit et RhinoMocks:Impossible d'obtenir RhinoMocks pour émettre une maquette qui suit la restriction de type générique règles

//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>(); 
... 

L'erreur que je reçois est:

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 

Lors d'une tentative de générer le mock contre la classe concrète, j'obtiens une erreur semblable, cette fois sur la méthode QueryFor(). Si je tente de redéfinir les méthodes qui utilisent TRest dans l'interface ISecurityRepository, j'obtiens un "System.BadImageFormatException: Une tentative de chargement d'un programme avec un format incorrect a été effectuée. (Exception de HRESULT: 0x8007000B)" qui ressemble à un retour en arrière .

Je pense que le problème principal est que RhinoMocks est perturbé par les paramètres génériques utilisés comme restrictions de type génériques. Je n'ai aucune idée de l'endroit où il est confus et donc je ne sais pas comment ou si je peux le déconcentrer. J'ai une couverture adéquate des tests d'intégration que je pourrais ignorer ces tests unitaires défaillants si je dois absolument, mais évidemment je préfère les réparer si je le peux. Tes pensées?

Répondre

6

Il ressemble à ceci est un problème connu causé par Castle.DynamicProxy qui est fixé dans le dernier tronc de ce projet, mais toujours cassé dans la dernière version Rhino Mocks:

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

Si vous vous sentez aventureux, vous pouvez construire votre propre Rhino Mocks avec le dernier DynamicProxy et il devrait être corrigé.

+0

Merci. Je pense que je vais simplement ignorer le test pour le moment, et garder un œil sur la prochaine version de Rhino Mocks. Tant que cela sera corrigé à la prochaine sortie, je ne me sens pas si mal de le faire. – KeithS

+0

Juste pour confirmer la suggestion d'Ergwun, j'ai eu le même problème. J'ai vérifié la source de Rhino et mis à jour tout à la version de tronc Castle et le bug est en effet corrigé. – FinnNk

1

On dirait que Castle Dynamic Proxy (que Rhino Mocks utilise pour la génération de proxy) ne génère pas la classe de proxy correctement compte tenu de la façon dont vous avez défini vos arguments génériques. Vous pouvez générer un proxy (et donc une maquette) si vous définissez votre IRepository comme ceci:

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); 
} 

Si vous avez vraiment besoin défini dans l'autre sens, vous devez produire un bug avec Rhino Mocks.

+0

J'en ai vraiment besoin, j'ai défini la façon dont je l'ai. J'ai plusieurs implémentations de IRepository qui travaillent chacune contre un DB différent (je n'ai pas inventé le schéma, je viens juste de l'hériter) et chaque implémentation fonctionne avec un sous-ensemble différent du domaine. La définition que je possède garantit à la compilation que le code ne peut pas tenter de demander à un Repository de travailler avec un objet qui ne fait pas partie de son schéma. Ce que vous avez là-bas nécessiterait essentiellement un référentiel par objet de domaine, car T ne peut pas être défini comme ISecurityDomainObject car Save et QueryFor ne peuvent pas connaître le type concret (dont ils ont besoin). – KeithS

+0

Assez juste. Le problème est que l'exception ne se produit pas dans Rhino Mocks, mais dans Castle DynamicProxy. J'ai vérifié Moq (qui utilise une version plus récente de Castle DynamicProxy que Rhino MOCKS) et souffre lui aussi de cette limitation. Pour l'instant, cette interface n'est pas simulable avec Rhino Mocks ou Moq. Pour que cela soit corrigé, Castle DynamicProxy devra corriger le problème sous-jacent. J'aimerais avoir une meilleure réponse pour vous. –

Questions connexes