如何用不同的 lambda 表达式模拟一个方法两次?
How mock a method two times with different lambda expression?
我有一个调用存储库(使用 EF 的数据访问层)的管理器(业务层)。
管理器的逻辑将使用两个不同的 lambda 表达式作为参数调用存储库的方法两次。
我的问题是如何将我的存储库模拟为 return 第一个 lambda 的给定响应,但 return 第二个 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);
}
一个选项是设置方法以通过 It.IsAny<Expression<Func<Relation, bool>>>()
接受任何 expression/predicate 并使用允许给定 predicate/expression.[=12 的所需行为的假数据源=]
[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);
}
正如第一个 post 的评论中所说,也可以使用 SetupSequence,但我更喜欢@Nkosi 解决方案,因为它不依赖于实现。
我 post 在我们的示例中使用 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);
}
我有一个调用存储库(使用 EF 的数据访问层)的管理器(业务层)。 管理器的逻辑将使用两个不同的 lambda 表达式作为参数调用存储库的方法两次。
我的问题是如何将我的存储库模拟为 return 第一个 lambda 的给定响应,但 return 第二个 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);
}
一个选项是设置方法以通过 It.IsAny<Expression<Func<Relation, bool>>>()
接受任何 expression/predicate 并使用允许给定 predicate/expression.[=12 的所需行为的假数据源=]
[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);
}
正如第一个 post 的评论中所说,也可以使用 SetupSequence,但我更喜欢@Nkosi 解决方案,因为它不依赖于实现。
我 post 在我们的示例中使用 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);
}