我想在我的一个存储库中模拟一个特定的表达式,但我遇到了一些麻烦。
我目前有:
Mock<Container> returnContainer = new Mock<Container>();
Mock<IRepository<Container>> CntnrRepository =
new Mock<IRepository<Container>>();
CntnrRepository.Setup<Container>(repo => repo
.Find(x => x.Name == "foo")
.Returns(returnContainer.Object);
每当下面的代码运行时,它返回null而不是上面的Mock<Container>
。
Container found =
containerRepository.Find(x => x.Name == cntnrName);
我在这里做错了什么?
以下是使用注入存储库的代码:
public int Foo(Guid id, string name)
{
Container found =
containerRepository.Find(x => x.Name == name);
if (found != null)
return CONTAINER_NOT_FREE;
Container cntnrToAssociate =
containerRepository.Find(x => x.Id == cntnrId);
if (cntnrToAssociate == null)
return CONTAINER_NOT_FOUND;
return OK;
}
在我的一个测试的上面代码中,我只需要在第一个查询(Find
)中将值返回到containerRepository
答案 0 :(得分:4)
编辑:我更新了解决方案,这适用于Expression-arguments Edit2:我添加了一个使用IQToolkit中的ExpressionComparer的更通用的解决方案(查看最后一个测试)。这应该满足设置
中的任何通用表达式如果按如下所示进行设置,则只能为某些输入参数返回
[Test]
public void SetupFunc_TestWithExpectedArgument_ReturnsNotNull()
{
// Arrange
var repository = new Mock<IRepository<Container>>();
repository.Setup(r => r.Find(It.Is<Expression<Func<Container, bool>>>(x => NameIsFoo(x)))).Returns(new Container());
// Act
Container container = repository.Object.Find(x => x.Name == "foo");
// Assert
Assert.That(container, Is.Not.Null);
}
[Test]
public void SetupFunc_TestWithOtherargument_ReturnsNull()
{
// arrange
var repository = new Mock<IRepository<Container>>();
repository.Setup(r => r.Find(It.Is<Expression<Func<Container, bool>>>(x => NameIsFoo(x)))).Returns(new Container());
// Act
Container container = repository.Object.Find(x => x.Name == "bar");
// Assert
Assert.That(container, Is.Null);
}
private static bool NameIsFoo(Expression<Func<Container, bool>> expression)
{
if (expression == null)
return false;
var mExpr = expression.Body as BinaryExpression;
if (mExpr == null)
return false;
var constantExpression = mExpr.Right as ConstantExpression;
if (constantExpression == null)
return false;
return Equals(constantExpression.Value, "foo");
}
[Test]
public void SetupFunc_TestWithExpectedArgumentUsingExpressionComparer_ReturnsNotNull()
{
// Arrange
var repository = new Mock<IRepository<Container>>();
Expression<Func<Container, bool>> expectedArgument = x => x.Name == "foo";
repository.Setup(r => r.Find(It.Is<Expression<Func<Container, bool>>>(x => ExpressionComparer.AreEqual(x, expectedArgument)))).Returns(new Container());
// Act
Container container = repository.Object.Find(x => x.Name == "foo");
// Assert
Assert.That(container, Is.Not.Null);
}
答案 1 :(得分:0)
以下似乎返回非null对象:
static void Main(string[] args)
{
Mock<Container> returnContainer = new Mock<Container>();
var CntnrRepository = new Mock<IRepository<Container>>();
CntnrRepository.Setup<Container>(repo => repo.Find(x => x.Name == "foo")).Returns(returnContainer.Object);
var found = CntnrRepository.Object.Find(x => x.Name == "foo");
// Or if you want to pass the mock repository to a method
var container = GetContainer(CntnrRepository.Object);
}
public static Container GetContainer(IRepository<Container> container)
{
return container.Find(x => x.Name == "foo");
}
您必须调用containerRepository.Object.Find,而不是调用containerRepository.Find。如果删除.Object部分,我甚至无法编译代码。
编辑:我添加了如何将IRepository传递给方法
的示例答案 2 :(得分:0)
似乎答案是“无法完成。”
请参阅Jason Punyon's answer to Moq.Mock - how to setup a method that takes an expression。
根据他的回答,你可以使用
CntnrRepository.Setup(repo => repo.FindAll(
It.IsAny<Expression<Func<Container, bool>>>()))
.Returns(returnContainer.Object);
但你不能对表达式施加任何约束。我想你可以使用It.Is解析表达式,但这可能比模拟它的价值更麻烦。
这是我参加“don't expose IQueryable on repositories”阵营的几个原因之一。虽然你只是暴露了IList,但是使用Expression会下滑到相同的滑坡IMHO。
答案 3 :(得分:0)
到目前为止,我还没有找到我正在寻找的解决方案,但我找到了一个不错的解决方法。我最终使用Moq的Callback
方法来做我想做的事。请参阅以下内容:
int numCalls = 0;
List<Container> expectedContainers = new List<Container>();
//Add list of expected containers here. Since I want the first call to return null
//and the 2nd call to return a valid container object I will fill the array with null
//in the 1st entry and a valid container in the 2nd
expectedContainers.Add(null);
expectedContainers.Add(new Container());
CntnrRepository.Setup<Container>(
repo => repo.Find(It.IsAny<Expression<Func<Container, bool>>>()))
.Returns(() => returnContainers[numCalls])
.Callback(() => numCalls++);
上面的代码允许我在同一个存储库中的不同时间返回不同的结果。
因此,我没有寻找特定的linq表达式,而是根据函数对存储库的调用次数返回正确的对象。