2011-09-22 4 views
10

다음 줄을 Null 참조 실패로 nHibernate 수 QueryOver을 비웃음 여기에 어둠이 있습니다. 저는 nHibernate와 Moq에 비교적 익숙하지 않아, 올바른 정보를 얻으려면 무엇을해야할지 잘 모릅니다.는 MOQ

답변

1

나는 위의 코드가 옳다고 생각하지 않습니다. AFAIK QueryOver는 ISession 인터페이스의 확장 메소드이며, Moock이나 RhinoMocks와 같은 기존의 Mocking 툴과 같은 확장 메소드를 모의 할 수는 없습니다.

+0

'QueryOver'는 확장 메소드가 아닙니다. 당신은'Query'에 대해 생각하고 있어요 – Vadim

2

QueryOver를 조롱하지 마십시오. 대신, 내부적으로 QueryOver를 사용하는 저장소 인터페이스를 정의하고 해당 인터페이스를 조롱하십시오.

4

이것은 좋지 않습니다. mock the types you don't own하지 않아야합니다. 대신 Repository을 소개하고 도메인/비즈니스 언어에 기반을두고 NHibernate를 사용하여 구현해야합니다. 구현은 ICriteria, HQL, QueryOver, Linq 등을 사용할 수 있습니다. 요점은이 결정이 저장소를 사용하는 코드에서 캡슐화되고 숨겨지는 것입니다.

인터페이스 + Real ORM + Real 또는 Fake 데이터베이스의 조합을 테스트하는통합 테스트를 작성할 수 있습니다. 리포지토리 및 데이터 액세스 테스트에 대한 답변은 thisthis입니다. 저장소 인터페이스를 조롱 할 수 있기 때문에 저장소를 사용하는 코드를 테스트하는 것도 매우 쉽습니다.

테스트 가능성 외에도이 접근법의 장점은 무엇입니까? 서로 다소 관련이 있습니다.

  • Separation of Concerns. 데이터 액세스 계층에서 해결 된 데이터 액세스 문제 (저장소 구현).
  • 루스 커플 링. 나머지 시스템은 데이터 액세스 도구와 결합되지 않습니다. Hibernate에서 raw sql, azure, web service로 리포지토리 구현을 전환 할 수 있습니다. 전환 할 필요가없는 경우에도 전환 할 필요가있는 것처럼 디자인하면 레이어가 더 잘 적용됩니다.
  • 가독성. 리포지토리 인터페이스 정의를 포함한 도메인 개체는 비즈니스/도메인 언어를 기반으로합니다.
+0

downvoter는 관심이 있으십니까? – Dmitry

3

저는 과거에 여러 접근법을 사용했습니다. 한 가지 방법은, 다른 사람들이 제안한 것처럼 당신의 쿼리를 캡슐화하는 mock/stub 할 수있는 Repository 클래스를 만드는 것입니다. 이것에 대한 한가지 문제점은 매우 유연하지 않고 솔루션과 같은 스토어드 프로 시저가 필요하다는 것입니다. 단, 데이터베이스가 아닌 코드에 있습니다.

내가 최근에 시도한 솔루션은 QueryOver 메서드를 스텁 할 때 제공하는 QueryOver 스텁을 만드는 것입니다. 그런 다음 반환해야하는 항목의 목록을 제공 할 수 있습니다. 이 접근 방식을 사용하는 경우 단위 테스트 만 작성하면 안되며 쿼리가 실제로 작동하는지 테스트하는 통합 테스트를 사용해야합니다.

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

모의하고 테스트 가능한 데이터 액세스를 캡슐화하는 저장소는 저장 프로 시저와 비슷합니까? 오히려 그 장소 전체에 QueryOver를 퍼뜨리는 것이 좋습니다. 그러면 '도메인 계층'이 파괴되고 UI 및 데이터 액세스 계층간에 책임이 분산됩니다. 그러면 매우 유연합니다. http://en.wikipedia.org/wiki/Big_ball_of_mud – Dmitry

+2

@ Dimitry, 몇 가지 점. 예, 모든 쿼리를 보관하는 리포지토리는 저장된 procs 집합과 똑같습니다. 동일한 엔터티를 쿼리해야하지만 2 개의 매개 변수를 사용해야하는 경우에는 어떻게됩니까? 아니면 방금 프로젝션을 원해? 어떻게하면 '도메인 계층을 파괴하라'는 질문에 따라 쿼리를 사용할 수 있습니까? 도메인 엔티티에는 여전히 비즈니스 로직이 포함되어 있습니다. 게다가 Hibernate는 이미 데이터 접근의 추상화이다. 왜 더 추상적으로 그릴 필요가 있다고 생각하니? 테스트가 더 쉬워 지지만 리팩토링이 더 어렵습니다. – Vadim

+0

@Dimitry 또한 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

최근에 내가 대신 보호 된 가상 메서드에 .QueryOver를 호출하는 코드를() 이동 XYZ에서 상속과 방법을 무시하고 빈 목록 또는 무엇이든을 반환 내 자신의 TestableXYZ를 구축했습니다. 이렇게하면 테스트 용 저장소가 필요 없다.