Se moquant de nHibernate QueryOver avec le Moq
la ligne suivante échoue avec une référence nulle, lors des tests:
var awards = _session.QueryOver<Body>().Where(x => x.BusinessId == (int)business).List();
Mon test est comme suit:
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);
pour être honnête, je me balade dans le noir ici - je suis relativement nouveau à nHibernate et Moq, donc je ne suis pas très sûr de ce qu'il faut google pour obtenir la bonne information.
5 réponses
Je ne pense pas que le code ci-dessus soit correct. AFAIK QueryOver est une méthode d'extension sur l'interface ISession et vous ne pouvez pas vous moquer de la méthode des extensions comme ça (du moins pas avec les outils de moquerie traditionnels comme Moq ou RhinoMocks).
Ce n'est pas une bonne idée. Vous ne devriez pas moquer des types que vous ne possédez pas. Au lieu de cela, vous devriez introduire un Dépôt, basent son interface sur le langage domaine / business et l'implémentent en utilisant NHibernate. La mise en œuvre peut utiliser ICriteria, HQL, QueryOver, Linq etc. Le point est que cette décision sera encapsulée et cachée du code qui consomme le dépôt.
Vous pouvez écrire un test d'intégration qui permettra de tester la combinaison de votre interface + vrai ORM + vrai ou faux base de données. Veuillez prendre un coup d'oeil à et réponses au sujet de la mise à l'essai des dépôts et de l'accès aux données. Tester le code qui utilise le dépôt est également très facile parce que vous pouvez simuler l'interface du dépôt.
Quels sont les avantages de cette approche autre que la testabilité? Ils sont en quelque sorte liés les uns aux autres:
- séparation des préoccupations. L'accès aux données des problèmes résolus dans la couche d'accès aux données (référentiel de mise en œuvre).
- Accouplement Lâche. Le reste du système n'est pas relié à l'outil d'accès aux données du jour. Vous avez la possibilité de changer la mise en œuvre du dépôt de NHibernate à sql brut, azure, service web. Même si vous n'avez jamais besoin de le changer, la superposition est appliquée mieux si vous conception du "comme si" vous devez passer.
- la Lisibilité. Les objets de domaine, y compris la définition de l'interface du dépôt, sont basés sur le langage business/domain.
j'ai utilisé plusieurs approches dans le passé. Une façon est, comme d'autres l'ont suggéré, de créer une classe de dépôt que vous pouvez simuler/stub qui encapsule vos requêtes. Un problème avec cela est qu'il n'est pas très flexible et vous vous retrouvez avec une procédure stockée comme solution, sauf que celle-ci est en code plutôt que dans la base de données.
une solution récente que j'ai essayée est de créer un talon QueryOver que je fournis quand je stab la méthode QueryOver. Je peux fournir une liste d'éléments qui doit être retourné. Gardez à l'esprit si vous utilisez cette approche, vous ne devriez pas seulement écrire des tests unitaires, mais un test d'intégration, qui permettra de tester si la requête fonctionne ou non.
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); }
}
}
N'essayez pas de vous moquer de QueryOver. À la place, définissez une interface de dépôt (qui utilise QueryOver en interne) et simulez cette interface.
dernièrement j'ai déplacé le code qui appelle .QueryOver () à une méthode virtuelle protégée à la place et en construisant mon propre TestableXYZ qui hérite de XYZ et remplace la méthode et retourne une liste vide ou autre. De cette façon, je n'ai pas besoin d'un dépôt juste pour les tests.