简体   繁体   English

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

[英]Verify method call with Lambda expression - Moq

I have a Unit of Work implementation with, among others, the following method:我有一个工作单元实施,其中包括以下方法:

T Single<T>(Expression<Func<T, bool>> expression) where T : class, new();

and I call it, for instance, like this:例如,我这样称呼它:

var person = _uow.Single<Person>(p => p.FirstName == "Sergi");

How can I verify that the Single method has been called with an argument of FirstName == "Sergi" ?如何验证Single方法是否已使用FirstName == "Sergi"的参数调用?

I've tried the following, but to no avail:我尝试了以下方法,但无济于事:

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

They all result in the folowing error:它们都导致以下错误:

Expected invocation on the mock at least once, but was never performed预期至少在模拟上调用一次,但从未执行过

Any ideas on how that can be done?关于如何做到这一点的任何想法? I'm using the latest Moq from NuGet, version 4.0.10827.0我正在使用NuGet的最新起订量,版本 4.0.10827.0

UPDATE: A Specific example更新:一个具体的例子

What I'm seeing is that whenever I use string literals inside the lambda, Verify works.我所看到的是,每当我在 lambda 中使用字符串文字时, Verify都有效。 As soon as I'm comparing variables it fails.一旦我比较变量,它就会失败。 Case in point:一个例子:

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

How come the Verify fails as soon as I use the method parameter in the lambda expression?为什么我一使用 lambda 表达式中的方法参数, Verify就会失败?

What would be the proper way to write this test?编写此测试的正确方法是什么?

The direct approach works just fine for me:直接方法对我来说很好:

// direct approach 
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));

The expression object doesn't return true for equivalent expressions so this will fail:对于等效表达式,表达式 object 不会返回 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));

To understand why, run the following NUnit test:要了解原因,请运行以下 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));
}

And as the test above indicates, comparing by the expression body will also fail, but string comparison works, so this works as well:正如上面的测试所示,通过表达式主体进行比较也会失败,但字符串比较有效,所以这也有效:

// even their string representations!
session.Verify(x => x
    .Single(It.Is<Expression<Func<Person, bool>>>(e => 
        e.ToString() == expression.ToString()));

And here's one more style of test you can add to the arsenal that also works:这里还有一种你可以添加到同样有效的测试方式:

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

As you have a number of test cases that fail that shouldn't, you likely have a different problem.由于您有许多不应该失败的测试用例,因此您可能会遇到不同的问题。

UPDATE : Per your update, consider the following setup and expressions:更新:根据您的更新,请考虑以下设置和表达式:

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;

One expression references an instance variable of the containing method.一个表达式引用包含方法的实例变量。 The other represents an expression that references a const member of a static class.另一个表示引用 static class 的 const 成员的表达式。 The two are different expressions, regardless of the values that may be assigned to the variables at runtime.无论在运行时可能分配给变量的值如何,两者都是不同的表达式。 If however, string normal_type is changed to const string normal_type then the expressions are again the same as each reference a const on the right hand side of the expression.但是,如果将string normal_type更改为const string normal_type ,则表达式再次与每个引用表达式右侧的const相同。

I would also like to share another approach to comparing the parameter expression to the expected expression.我还想分享另一种将参数表达式与预期表达式进行比较的方法。 I searched StackOverflow for "how to compare expressions," and I was led to these articles:我在 StackOverflow 上搜索“如何比较表达式”,然后我被引导到这些文章:

I was then led to this Subversion repository for db4o.net .然后我被带到了这个db4o.netSubversion 存储库 In one of their projects, namespace Db4objects.Db4o.Linq.Expressions , they include a class named ExpressionEqualityComparer .在他们的一个项目中,命名空间Db4objects.Db4o.Linq.Expressions包含一个名为ExpressionEqualityComparer的 class。 I was able to checkout this project from the repository, compile, build, and create a DLL to use in my own project.我能够从存储库中签出这个项目,编译、构建和创建 DLL 以在我自己的项目中使用。

With the ExpressionEqualityComparer , you can modify the Verify call to something like the following:使用ExpressionEqualityComparer ,您可以将Verify调用修改为如下所示:

session.Verify(x => x.Single(It.Is<Expression<Func<Person, bool>>>(e => new ExpressionEqualityComparer().Equals(e, expression))));

Ultimately, the ExpressionEqualityComparer and the ToString() techniques both return true in this case (with the ToString most likely being faster - speed not tested).最终, ExpressionEqualityComparerToString()技术在这种情况下都返回 true( ToString很可能更快 - 速度未经测试)。 Personally, I prefer the comparer approach since I feel it is more self-documenting and better reflects your design intent (comparing the expression objects rather a string comparison of their ToString outputs).就个人而言,我更喜欢比较器方法,因为我觉得它更能自我记录并更好地反映您的设计意图(比较表达式对象而不是其 ToString 输出的字符串比较)。

Note: I'm still looking for a db4o.net license file in this project, but I've not modified the code in anyway, included the copyright notice, and (since the page is publicly available) I'm assuming that's enough for now... ;-)注意:我仍在这个项目中寻找一个 db4o.net 许可证文件,但无论如何我都没有修改代码,包括版权声明,并且(因为该页面是公开可用的)我假设这已经足够了现在... ;-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM