2011-09-22 16 views
10

La siguiente línea de falla con una referencia nula, cuando la prueba:bocetos fuera NHibernate QueryOver con Moq

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

Mi prueba es de esta manera:

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

Para ser honesto, estoy agitándose en la oscuridad aquí: soy relativamente nuevo en nHibernate y Moq, por lo que no estoy muy seguro de qué buscar en google para obtener la información correcta.

Respuesta

1

No creo que el código anterior sea correcto. AFAIK QueryOver es un método de extensión en la interfaz ISession y no se puede simular el método de extensiones de esa manera (al menos no con las herramientas convencionales de Mocking como Moq o RhinoMocks).

+0

'QueryOver' no es un método de extensión. Estás pensando en 'Query' – Vadim

2

No intente burlarse de QueryOver. En su lugar, defina una interfaz de repositorio (que usa QueryOver internamente) y simule esa interfaz.

4

Esto no es una buena idea. No debe mock the types you don't own. En su lugar, debe introducir un Repository, basar su interfaz en el lenguaje de dominio/negocio e implementarlo utilizando NHibernate. La implementación puede usar ICriteria, HQL, QueryOver, Linq, etc. El punto es que esta decisión estará encapsulada y oculta del código que consume el repositorio.

Puede escribir una prueba de integración que probará la combinación de su interfaz + base de datos Real ORM + Real o Fake. Por favor, eche un vistazo a this y this respuestas sobre pruebas de repositorios y acceso a datos. Probar el código que usa Repository también es muy fácil porque puedes simular la interfaz de Repository.

¿Cuáles son las ventajas de este enfoque aparte de la capacidad de prueba? Están algo relacionados entre sí:

  • Separación de preocupaciones. Problemas de acceso a los datos resueltos en la capa de acceso a los datos (implementación del repositorio).
  • Loose Coupling. El resto del sistema no está acoplado a la herramienta de acceso a datos del día. Usted tiene el potencial para cambiar la implementación del repositorio de NHibernate a sql, azul, servicio web en bruto. Incluso si nunca necesita cambiar, la estratificación se aplica mejor si diseña 'como si' necesitara cambiar.
  • Legibilidad. Los objetos de dominio, incluida la definición de la interfaz del repositorio, se basan en el lenguaje de negocios/dominio.
+0

¿Le importaría a los downwalters comentar? – Dmitry

3

He utilizado varios enfoques en el pasado. Una forma es, como han sugerido otros, crear una clase de Repositorio que pueda simular/resguardar que encapsula sus consultas. Un problema con esto es que no es muy flexible y terminas teniendo un procedimiento almacenado como solución, excepto que este está en código en lugar de en la base de datos.

Una solución reciente que he intentado es crear un talón QueryOver que proporciono cuando corto el método QueryOver. Luego puedo proporcionar una lista de elementos que deben devolverse. Tenga en cuenta que si usa este enfoque, no solo debe escribir pruebas unitarias, sino una prueba de integración, que probará si la consulta realmente funciona o no.

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

Repositorio simulado y comprobable que encapsula el acceso a los datos es como un procedimiento almacenado? ¿Prefiere recomendar difundir QueryOver por todos lados? Eso destruiría la 'capa de dominio' y extendería sus responsabilidades entre la interfaz de usuario y la capa de acceso a datos. Eso sería súper flexible: http://en.wikipedia.org/wiki/Big_ball_of_mud – Dmitry

+2

@Dimitry, varios puntos. Sí, un repositorio donde guardas todas tus consultas es exactamente como tener un conjunto de procs almacenados. ¿Qué sucede si necesita consultar la misma entidad, pero usando 2 parámetros? ¿O tal vez solo quiere una proyección? ¿Cómo sería el uso de consultas según sea necesario 'destruir la capa de dominio'? Sus entidades de dominio aún contienen la lógica comercial. Además, NHibernate ya es una abstracción de acceso a datos. ¿Por qué sentirías que necesitas abstraerlo más? Hace las pruebas más fáciles, pero la refactorización es más difícil, yo también. – Vadim

+0

@Dimitry también echa un vistazo a estos artículos de 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

Últimamente he estado moviendo el código que llama .QueryOver() a un método virtual protegida lugar y la construcción de mi propia TestableXYZ que hereda de XYZ y reemplaza el método y devuelve una lista vacía o lo que sea. De esta forma, no necesito un repositorio solo para probar.

Cuestiones relacionadas