验证方法调用与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如何失败?

写这个测试的正确方法是什么?

验证方法调用与Lambda表达式- Moq

直接方法对我来说很好:

// 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
然后,我找到了db40.net的Subversion存储库。在他们的一个项目命名空间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))));

最终,在这种情况下,ExpressionEqualityComparerToString()技术都返回true (ToString最有可能更快-速度未测试)。就我个人而言,我更喜欢比较方法,因为我觉得它更具有自我文档性,并且更好地反映了您的设计意图(比较表达式对象,而不是比较它们的ToString输出的字符串)。

注:我仍在寻找db40.net许可证文件在这个项目中,但我没有修改代码,包括版权声明,和(因为页面是公开可用的)我认为这是足够的现在…: -)