验证方法调用与Lambda表达式- Moq
本文关键字:表达式 Moq Lambda 方法 调用 验证 | 更新日期: 2023-09-27 18:01:41
我有一个工作单元实现,其中包括以下方法:
T Single<T>(Expression<Func<T, bool>> expression) where T : class, new();
,我把它命名为,例如,
var person = _uow.Single<Person>(p => p.FirstName == "Sergi");
我如何验证Single
方法是否以FirstName == "Sergi"
参数被调用?
我试了以下方法,但没有效果:
// direct approach
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));
// comparing expressions
Expression<Func<Person, bool>> expression = p => p.FirstName == "Sergi");
session.Verify(x => x
.Single(It.Is<Expression<Func<Person, bool>>>(e => e == expression));
它们都会导致以下错误:
预期对模拟调用至少一次,但从未执行
有什么好主意吗?我正在使用NuGet的最新Moq,版本4.0.10827.0
UPDATE:一个具体的例子
我看到的是,每当我在lambda中使用字符串字面量时,Verify
工作。当我比较变量时,它就失败了。例子:
// the verify
someService.GetFromType(QuestionnaireType.Objective)
session.Verify(x => x.Single<Questionnaire>(q =>
q.Type == QuestionnaireType.Objective));
// QuestionnaireType.Objective is just a constant:
const string Objective = "objective";
// the method where it's called (FAILS):
public Questionnaire GetFromType(string type)
{
// this will fail the Verify
var questionnaire = _session
.Single<Questionnaire>(q => q.Type == type);
}
// the method where it's called (PASSES):
public Questionnaire GetFromType(string type)
{
// this will pass the Verify
var questionnaire = _session
.Single<Questionnaire>(q => q.Type == QuestionnaireType.Objective);
}
当我在lambda表达式中使用方法参数时,Verify
如何失败?
写这个测试的正确方法是什么?
直接方法对我来说很好:
// direct approach
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));
对于等价表达式,表达式对象没有返回true,所以这将失败:
// comparing expressions
Expression<Func<Person, bool>> expression = p => p.FirstName == "Sergi");
session.Verify(x => x
.Single(It.Is<Expression<Func<Person, bool>>>(e => e == expression));
要了解原因,运行以下NUnit测试:
[Test]
public void OperatorEqualEqualVerification()
{
Expression<Func<Person, bool>> expr1 = p => p.FirstName == "Sergi";
Expression<Func<Person, bool>> expr2 = p => p.FirstName == "Sergi";
Assert.IsTrue(expr1.ToString() == expr2.ToString());
Assert.IsFalse(expr1.Equals(expr2));
Assert.IsFalse(expr1 == expr2);
Assert.IsFalse(expr1.Body == expr2.Body);
Assert.IsFalse(expr1.Body.Equals(expr2.Body));
}
正如上面的测试所表明的,通过表达式体进行比较也会失败,但是字符串比较可以工作,所以这个也可以工作:
// even their string representations!
session.Verify(x => x
.Single(It.Is<Expression<Func<Person, bool>>>(e =>
e.ToString() == expression.ToString()));
这里还有一种类型的测试,你可以添加到同样有效的武器库中:
[Test]
public void CallbackVerification()
{
Expression<Func<Person, bool>> actualExpression = null;
var mockUow = new Mock<IUnitOfWork>();
mockUow
.Setup(u => u.Single<Person>(It.IsAny<Expression<Func<Person, bool>>>()))
.Callback( (Expression<Func<Person,bool>> x) => actualExpression = x);
var uow = mockUow.Object;
uow.Single<Person>(p => p.FirstName == "Sergi");
Expression<Func<Person, bool>> expectedExpression = p => p.FirstName == "Sergi";
Assert.AreEqual(expectedExpression.ToString(), actualExpression.ToString());
}
当您有许多不应该失败的测试用例时,您可能会遇到不同的问题。
UPDATE:根据您的更新,考虑以下设置和表达式:
string normal_type = "NORMAL";
// PersonConstants is a static class with NORMAL_TYPE defined as follows:
// public const string NORMAL_TYPE = "NORMAL";
Expression<Func<Person, bool>> expr1 = p => p.Type == normal_type;
Expression<Func<Person, bool>> expr2 = p => p.Type == PersonConstants.NORMAL_TYPE;
一个表达式引用包含该方法的实例变量。另一个表示引用静态类的const成员的表达式。这两个表达式是不同的,不管在运行时赋给变量的值是什么。但是,如果将string normal_type
改为const string normal_type
,则表达式再次与每个引用表达式右侧的const
相同。
我还想分享另一种比较参数表达式和期望表达式的方法。我在StackOverflow上搜索"如何比较表达式",得到了以下文章:
- how-to-test-expressions-equality
- most-efficient-way-to-test-equality-of-lambda-expressions
Db4objects.Db4o.Linq.Expressions
中,他们包含了一个名为ExpressionEqualityComparer
的类。我能够从存储库中签出这个项目,编译、构建和创建一个DLL以在我自己的项目中使用。
使用ExpressionEqualityComparer
,您可以将Verify
调用修改为如下内容:
session.Verify(x => x
.Single(It.Is<Expression<Func<Person, bool>>>(e =>
new ExpressionEqualityComparer().Equals(e, expression))));
最终,在这种情况下,ExpressionEqualityComparer
和ToString()
技术都返回true (ToString
最有可能更快-速度未测试)。就我个人而言,我更喜欢比较方法,因为我觉得它更具有自我文档性,并且更好地反映了您的设计意图(比较表达式对象,而不是比较它们的ToString输出的字符串)。
注:我仍在寻找db40.net许可证文件在这个项目中,但我没有修改代码,包括版权声明,和(因为页面是公开可用的)我认为这是足够的现在…: -)