2011-09-22 13 views
10

La seguente linea non riesce con un riferimento null, quando il test:beffardo fuori NHibernate QueryOver con Moq

var awards = _session.QueryOver<Body>().Where(x => x.BusinessId == (int)business).List(); 

La mia prova è in questo modo:

var mockQueryOver = new Mock<IQueryOver<Body, Body>>(); 
mockQueryOver.Setup(q => q.List()).Returns(new List<Body> {_awardingBody}); 
_mockSession.Setup(c => c.QueryOver<Body>()).Returns((mockQueryOver.Object)); 
_mockCommandRunner = new Mock<ICommandRunner>(); 
_generator = new CertificateGeneratorForOpenSSLCommandLine(_mockSession.Object, _mockCommandRunner.Object, _mockDirectory.Object, _mockFile.Object, _mockConfig.Object); 

Per essere onesti sto agitando in giro il buio qui - Sono relativamente nuovo a nHibernate e Moq, quindi non sono molto sicuro di cosa sia il google per ottenere le informazioni giuste.

risposta

1

Non penso che il codice sopra sia corretto. AFAIK QueryOver è un metodo di estensione sull'interfaccia ISession e non puoi simulare un metodo di estensione come quello (almeno non con strumenti di simulazione convenzionale come Moq o RhinoMocks).

+0

'QueryOver' non è un metodo di estensione. Stai pensando a 'Query' – Vadim

2

Non provare a prendere in giro QueryOver. Invece, definire un'interfaccia repository (che utilizza internamente QueryOver) e deridere quella interfaccia.

4

Questa non è una buona idea. Non dovresti mock the types you don't own. Invece dovresti introdurre uno Repository, basare la sua interfaccia su dominio/linguaggio aziendale e implementarlo usando NHibernate. L'implementazione può usare ICriteria, HQL, QueryOver, Linq ecc. Il punto è che questa decisione sarà incapsulata e nascosta dal codice che consuma il repository.

È possibile scrivere un test di integrazione che verificherà la combinazione dell'interfaccia + Real ORM + database Real o Fake. Si prega di dare un'occhiata a this e this risposte su test di repository e accesso ai dati. Anche testare il codice che usa il repository è semplicissimo, perché puoi prendere in giro l'interfaccia del repository.

Quali sono i vantaggi di questo approccio oltre alla testabilità? Sono in qualche modo collegati tra loro:

  • Separazione di preoccupazioni. Problemi di accesso ai dati risolti nel livello di accesso ai dati (implementazione del repository).
  • Accoppiamento lento. Il resto del sistema non è accoppiato allo strumento di accesso ai dati del giorno. Hai il potenziale per cambiare l'implementazione del repository da NHibernate a raw sql, azzurro, servizio web. Anche se non hai mai bisogno di cambiare, la stratificazione viene applicata meglio se progetti 'come se' devi cambiare.
  • Leggibilità. Gli oggetti di dominio, inclusa la definizione dell'interfaccia del repository, sono basati sul linguaggio aziendale/di dominio.
+0

Faresti meglio a downtoter per commentare? – Dmitry

3

Ho usato diversi approcci in passato. Un modo è, come altri hanno suggerito, di creare una classe di repository che puoi simulare/stub che incapsula le tue query. Un problema con questo è che non è molto flessibile e si finisce per avere una procedura memorizzata come la soluzione, tranne che questa è in codice piuttosto che nel database.

Una soluzione recente che ho provato è la creazione di uno stub QueryOver che fornisco quando stubo il metodo QueryOver. Posso quindi fornire un elenco di elementi che devono essere restituiti. Tieni presente che se utilizzi questo approccio non dovresti scrivere solo test unitari, ma un test di integrazione, che testerà se la query funziona effettivamente.

public class QueryOverStub<TRoot, TSub> : IQueryOver<TRoot, TSub> 
{ 
    private readonly TRoot _singleOrDefault; 
    private readonly IList<TRoot> _list; 
    private readonly ICriteria _root = MockRepository.GenerateStub<ICriteria>(); 

    public QueryOverStub(IList<TRoot> list) 
    { 
     _list = list; 
    } 

    public QueryOverStub(TRoot singleOrDefault) 
    { 
     _singleOrDefault = singleOrDefault; 
    } 

    public ICriteria UnderlyingCriteria 
    { 
     get { return _root; } 
    } 

    public ICriteria RootCriteria 
    { 
     get { return _root; } 
    } 

    public IList<TRoot> List() 
    { 
     return _list; 
    } 

    public IList<U> List<U>() 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOver<TRoot, TRoot> ToRowCountQuery() 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOver<TRoot, TRoot> ToRowCountInt64Query() 
    { 
     throw new NotImplementedException(); 
    } 

    public int RowCount() 
    { 
     return _list.Count; 
    } 

    public long RowCountInt64() 
    { 
     throw new NotImplementedException(); 
    } 

    public TRoot SingleOrDefault() 
    { 
     return _singleOrDefault; 
    } 

    public U SingleOrDefault<U>() 
    { 
     throw new NotImplementedException(); 
    } 

    public IEnumerable<TRoot> Future() 
    { 
     return _list; 
    } 

    public IEnumerable<U> Future<U>() 
    { 
     throw new NotImplementedException(); 
    } 

    public IFutureValue<TRoot> FutureValue() 
    { 
     throw new NotImplementedException(); 
    } 

    public IFutureValue<U> FutureValue<U>() 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOver<TRoot, TRoot> Clone() 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOver<TRoot> ClearOrders() 
    { 
     return this; 
    } 

    public IQueryOver<TRoot> Skip(int firstResult) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot> Take(int maxResults) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot> Cacheable() 
    { 
     return this; 
    } 

    public IQueryOver<TRoot> CacheMode(CacheMode cacheMode) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot> CacheRegion(string cacheRegion) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> And(Expression<Func<TSub, bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> And(Expression<Func<bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> And(ICriterion expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> AndNot(Expression<Func<TSub, bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> AndNot(Expression<Func<bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOverRestrictionBuilder<TRoot, TSub> AndRestrictionOn(Expression<Func<TSub, object>> expression) 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOverRestrictionBuilder<TRoot, TSub> AndRestrictionOn(Expression<Func<object>> expression) 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOver<TRoot, TSub> Where(Expression<Func<TSub, bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> Where(Expression<Func<bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> Where(ICriterion expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> WhereNot(Expression<Func<TSub, bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> WhereNot(Expression<Func<bool>> expression) 
    { 
     return this; 
    } 

    public IQueryOverRestrictionBuilder<TRoot, TSub> WhereRestrictionOn(Expression<Func<TSub, object>> expression) 
    { 
     return new IQueryOverRestrictionBuilder<TRoot, TSub>(this, "prop"); 
    } 

    public IQueryOverRestrictionBuilder<TRoot, TSub> WhereRestrictionOn(Expression<Func<object>> expression) 
    { 
     return new IQueryOverRestrictionBuilder<TRoot, TSub>(this, "prop"); 
    } 

    public IQueryOver<TRoot, TSub> Select(params Expression<Func<TRoot, object>>[] projections) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> Select(params IProjection[] projections) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> SelectList(Func<QueryOverProjectionBuilder<TRoot>, QueryOverProjectionBuilder<TRoot>> list) 
    { 
     return this; 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> OrderBy(Expression<Func<TSub, object>> path) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, path); 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> OrderBy(Expression<Func<object>> path) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, path, false); 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> OrderBy(IProjection projection) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, projection); 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> OrderByAlias(Expression<Func<object>> path) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, path, true); 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> ThenBy(Expression<Func<TSub, object>> path) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, path); 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> ThenBy(Expression<Func<object>> path) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, path, false); 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> ThenBy(IProjection projection) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, projection); 
    } 

    public IQueryOverOrderBuilder<TRoot, TSub> ThenByAlias(Expression<Func<object>> path) 
    { 
     return new IQueryOverOrderBuilder<TRoot, TSub>(this, path, true); 
    } 

    public IQueryOver<TRoot, TSub> TransformUsing(IResultTransformer resultTransformer) 
    { 
     return this; 
    } 

    public IQueryOverFetchBuilder<TRoot, TSub> Fetch(Expression<Func<TRoot, object>> path) 
    { 
     return new IQueryOverFetchBuilder<TRoot, TSub>(this, path); 
    } 

    public IQueryOverLockBuilder<TRoot, TSub> Lock() 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOverLockBuilder<TRoot, TSub> Lock(Expression<Func<object>> alias) 
    { 
     throw new NotImplementedException(); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, U>> path) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<U>> path) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, U>> path, Expression<Func<U>> alias) 
    { 
     return new QueryOverStub<TRoot, U>(_list); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<U>> path, Expression<Func<U>> alias) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, U>> path, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<U>> path, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, U>> path, Expression<Func<U>> alias, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<U>> path, Expression<Func<U>> alias, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, IEnumerable<U>>> path) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<IEnumerable<U>>> path) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, IEnumerable<U>>> path, Expression<Func<U>> alias) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<IEnumerable<U>>> path, Expression<Func<U>> alias) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, IEnumerable<U>>> path, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<IEnumerable<U>>> path, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<TSub, IEnumerable<U>>> path, Expression<Func<U>> alias, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, U> JoinQueryOver<U>(Expression<Func<IEnumerable<U>>> path, Expression<Func<U>> alias, JoinType joinType) 
    { 
     return new QueryOverStub<TRoot, U>(new List<TRoot>()); 
    } 

    public IQueryOver<TRoot, TSub> JoinAlias(Expression<Func<TSub, object>> path, Expression<Func<object>> alias) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> JoinAlias(Expression<Func<object>> path, Expression<Func<object>> alias) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> JoinAlias(Expression<Func<TSub, object>> path, Expression<Func<object>> alias, JoinType joinType) 
    { 
     return this; 
    } 

    public IQueryOver<TRoot, TSub> JoinAlias(Expression<Func<object>> path, Expression<Func<object>> alias, JoinType joinType) 
    { 
     return this; 
    } 

    public IQueryOverSubqueryBuilder<TRoot, TSub> WithSubquery 
    { 
     get { return new IQueryOverSubqueryBuilder<TRoot, TSub>(this); } 
    } 

    public IQueryOverJoinBuilder<TRoot, TSub> Inner 
    { 
     get { return new IQueryOverJoinBuilder<TRoot, TSub>(this, JoinType.InnerJoin); } 
    } 

    public IQueryOverJoinBuilder<TRoot, TSub> Left 
    { 
     get { return new IQueryOverJoinBuilder<TRoot, TSub>(this, JoinType.LeftOuterJoin); } 
    } 

    public IQueryOverJoinBuilder<TRoot, TSub> Right 
    { 
     get { return new IQueryOverJoinBuilder<TRoot, TSub>(this, JoinType.RightOuterJoin); } 
    } 

    public IQueryOverJoinBuilder<TRoot, TSub> Full 
    { 
     get { return new IQueryOverJoinBuilder<TRoot, TSub>(this, JoinType.FullJoin); } 
    } 
} 
+0

Il repository mockable e testabile che incapsula l'accesso ai dati è come una stored procedure? Preferisci consigliare la diffusione di QueryOver dappertutto? Ciò distruggerebbe il "dominio layer" e diffonderebbe le sue responsabilità tra interfaccia utente e livello di accesso ai dati. Questo sarebbe sicuramente super flessibile: http://en.wikipedia.org/wiki/Big_ball_of_mud – Dmitry

+2

@Dimitry, diversi punti. Sì, un repository in cui mantieni tutte le tue query è esattamente come avere una serie di proc memorizzati. Cosa succede se ha bisogno di interrogare la stessa entità, ma usando 2 parametri? O forse vuole solo una proiezione? In che modo utilizzare le query in base alle necessità "distrugge il livello dominio"? Le tue entità di dominio contengono ancora la logica aziendale. Inoltre, NHibernate è già un'astrazione di accesso ai dati. Perché ti sentiresti come se avessi bisogno di astrarre ulteriormente? Rende più facile il test, ma il refactoring è più difficile, imo. – Vadim

+0

@Dimitry Verificate anche questi articoli da Oren http://ayende.com/blog/4784/architecting-in-the-pit-of-doom-the-evils-of-the-repository-abstraction-layer http://ayende.com/blog/4783/reviewing-oss-project-whiteboard-chatndash-the-select-n-1-issue – Vadim

1

Ultimamente sto spostando il codice che chiama .QueryOver() per un metodo virtuale protetto, invece, e costruire il mio TestableXYZ che eredita da XYZ e l'override del metodo e restituisce una lista vuota o qualsiasi altra cosa. In questo modo non ho bisogno di un repository solo per il test.

Problemi correlati