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

时间:2011-07-11 15:36:38

标签: c# lambda mocking moq verify

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

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

我打电话给它,例如,像这样:

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

如何验证Single方法是否已使用[{1}}参数调用?

我尝试了以下内容,但无济于事:

FirstName == "Sergi"

它们都会导致以下错误:

  

模拟上的预期调用至少一次,但从未执行过

关于如何做到这一点的任何想法? 我正在使用NuGet的最新版Moq,版本4.0.10827.0

更新:具体示例

我所看到的是,每当我在lambda中使用字符串文字时,// 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)); 都能正常工作。一旦我比较变量就失败了。一个很好的例子:

Verify

在lambda表达式中使用method参数后,// 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); } 怎么会失败?

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

2 个答案:

答案 0 :(得分:11)

直接方法对我来说很合适:

// 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,则表达式再次与表达式右侧的每个引用const相同。

答案 1 :(得分:1)

我还想分享另一种方法来将参数表达式与预期表达式进行比较。我在StackOverflow中搜索了#34;如何比较表达式,&#34;我被引导到这些文章:

然后我被引导到this Subversion repository db4o.net。在其中一个项目名称空间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输出的字符串比较)。

注意:我仍然在这个项目中寻找db4o.net许可文件,但我还没有修改代码,包括版权声明,以及(由于页面是公开的,我现在假设已经足够......; - )