23

我有一个工作单元实施,其中包括以下方法:

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 的最新起订量,版本4.0.10827.0

更新:一个具体的例子

我所看到的是,每当我在 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);
}

Verify我在 lambda 表达式中使用方法参数怎么会失败?

编写此测试的正确方法是什么?

4

2 回答 2

15

直接方法对我来说很好:

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

由于您有许多不应该失败的测试用例,因此您可能会遇到不同的问题。

更新:根据您的更新,请考虑以下设置和表达式:

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每个引用 a 相同。const

于 2011-07-11T16:36:30.390 回答
1

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:

I was then led to this Subversion repository for db4o.net. In one of their projects, namespace Db4objects.Db4o.Linq.Expressions, they include a class named ExpressionEqualityComparer. I was able to checkout this project from the repository, compile, build, and create a DLL to use in my own project.

With the ExpressionEqualityComparer, you can modify the Verify call to something like the following:

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). 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).

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

于 2014-06-10T18:21:22.013 回答