如何使用不同的lambda表达式两次模拟一个方法?

时间:2017-05-19 20:05:24

标签: c# lambda mocking repository moq

我有一个调用存储库的管理器(业务层)(带有EF的数据访问层)。 管理器的逻辑将使用两个不同的lambda表达式作为参数调用两次存储库的方法。

我的问题是如何模拟我的存储库以返回第一个lambda的给定响应,但是为第二个lambda返回另一个响应?

例如:

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

2 个答案:

答案 0 :(得分:1)

一种选择是将方法设置为通过It.IsAny<Expression<Func<Relation, bool>>>()接受任何表达式/谓词,并使用假数据源,该数据源允许使用给定谓词/表达式的所需行为。

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

答案 1 :(得分:1)

正如在第一篇文章的评论中所说,也可以使用SetupSequence,但我更喜欢@Nkosi解决方案,因为它不依赖于实现。

我在我们的示例中发布了SetupSequence用法的示例。它对某些人来说很有用。

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