2017-05-19 5 views
1

J'ai un gestionnaire (couche de gestion) qui appelle un référentiel (couche d'accès aux données avec EF). La logique du gestionnaire appellera deux fois une méthode du référentiel avec deux expressions lambda différentes en paramètre.Comment se moquer d'une méthode deux fois avec une expression lambda différente?

Ma question est comment moquer mon dépôt pour retourner une réponse donnée pour un premier lambda, mais retourner une autre réponse pour un deuxième lambda?

Par exemple:

public class Person 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class Relation 
{ 
    public int GiverId { get; set; } 
    public int ReceiverId { get; set; } 
} 

public interface IRelationRepository 
{ 
    bool Loves(Expression<Func<Relation, bool>> predicate); 
} 

public class RelationRepository : IRelationRepository 
{ 
    public bool Loves(Expression<Func<Relation, bool>> predicate) 
    { 
     // Some logic... 
     return true; 
    } 
} 

public class KissManager 
{ 
    private readonly IRelationRepository repository; 

    public KissManager(IRelationRepository repository) 
    { 
     this.repository = repository; 
    } 

    public bool Kiss(Person p1, Person p2) 
    { 
     var result = this.repository.Loves(r => r.GiverId == p1.Id && r.ReceiverId == p2.Id) 
      && this.repository.Loves(r => r.GiverId == p2.Id && r.ReceiverId == p1.Id); 
     return result; 
    } 
} 

[TestMethod] 
public void KissWithReceiverNotInLove() 
{ 
    // Arange. 
    var p1 = new Person { Id = 5, Name = "M. Love" }; 
    var p2 = new Person { Id = 17, Name = "Paul Atreid" }; 

    var kissRepositoryMock = new Mock<IRelationRepository>(); 
    kissRepositoryMock 
     .Setup(m => m.Loves(r => r.GiverId == p1.Id && r.ReceiverId == p2.Id)) 
     .Returns(true); 
    kissRepositoryMock 
     .Setup(m => m.Loves(r => r.GiverId == p2.Id && r.ReceiverId == p1.Id)) 
     .Returns(false); 

    var kissManager = new KissManager(kissRepositoryMock.Object); 

    // Act. 
    var result = kissManager.Kiss(p1, p2); 

    // Assert. 
    Assert.IsFalse(result); 
} 
+1

[SetupSequence] (https://codecontracts.info/2011/07/28/moq-setupsequence-is-great-for-mocking/) au lieu de 'Setup'. Assurez-vous de les configurer dans le bon ordre, cependant. –

+0

Merci beaucoup, ça marche! Mais si je change l'ordre de mes appels (dans le manager), cela ne change rien sur le plan fonctionnel mais mon test échouera. Connaissez-vous un autre moyen? – C0b0ll

+1

L'ordre de SetupSequence doit correspondre à celui du SUT. Si vous changez l'ordre des appels dans votre SUT, alors votre test devra être mis à jour. Les tests seront fragiles à cet égard. –

Répondre

2

Une option serait d'installer la méthode d'accepter toute expression/prédicat via It.IsAny<Expression<Func<Relation, bool>>>() et utiliser une source de fausses données qui permet le comportement désiré avec un prédicat/expression donnée.

[TestMethod] 
public void _KissWithReceiverNotInLove() { 
    // Arange. 
    var p1 = new Person { Id = 5, Name = "M. Love" }; 
    var p2 = new Person { Id = 17, Name = "Paul Atreid" }; 
    var relations = new List<Relation>() 
    { 
     new Relation{ GiverId = p1.Id, ReceiverId = p2.Id } 
     //The above should satisfy the first expected predicate. 
     //Note there are no other relations in this list. 
     //That is by design to make the second predicate return false. 
    }; 

    var kissRepositoryMock = new Mock<IRelationRepository>(); 

    kissRepositoryMock 
     .Setup(m => m.Loves(It.IsAny<Expression<Func<Relation, bool>>>())) 
     .Returns((Expression<Func<Relation, bool>> predicate) => relations.Any(predicate.Compile())); 

    var kissManager = new KissManager(kissRepositoryMock.Object); 

    // Act. 
    var result = kissManager.Kiss(p1, p2); 

    // Assert. 
    Assert.IsFalse(result); 
} 
+0

C'est un très bon moyen de simuler l'ensemble de données sur le référentiel. Merci pour votre réponse ! – C0b0ll

2

Comme il est dit dans le commentaire du premier poste, il est également possible d'utiliser SetupSequence, mais je préfère la solution @Nkosi car il ne dépend pas de la mise en œuvre.

Je poste un exemple d'utilisation de SetupSequence dans notre exemple. Cela peut être utile pour quelqu'un.

[TestMethod] 
public void KissWithReceiverNotInLove() 
{ 
    // Arange. 
    var p1 = new Person { Id = 5, Name = "M. Love" }; 
    var p2 = new Person { Id = 17, Name = "Paul Atreid" }; 

    var kissRepositoryMock = new Mock<IRelationRepository>(); 
    kissRepositoryMock 
     .SetupSequence(m => m.Loves(It.IsAny<Expression<Func<Relation, bool>>>())) 
     .Returns(true) 
     .Returns(false); 

    var kissManager = new KissManager(kissRepositoryMock.Object); 

    // Act. 
    var result = kissManager.Kiss(p1, p2); 

    // Assert. 
    Assert.IsFalse(result); 
}