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

Ser*_*eit 18 c# lambda moq mocking verify

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

T Single<T>(Expression<Func<T, bool>> expression) where T : class, new();
Run Code Online (Sandbox Code Playgroud)

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

var person = _uow.Single<Person>(p => p.FirstName == "Sergi");
Run Code Online (Sandbox Code Playgroud)

如何验证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));
Run Code Online (Sandbox Code Playgroud)

它们都会导致以下错误:

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

关于如何做到这一点的任何想法?我正在使用NuGet 的最新Moq,版本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);
}
Run Code Online (Sandbox Code Playgroud)

Verify一旦我在lambda表达式中使用method参数,怎么会失败?

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

Kal*_*son 12

直接的方法对我来说很好:

// direct approach 
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));
Run Code Online (Sandbox Code Playgroud)

对于等效表达式,表达式对象不返回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));
Run Code Online (Sandbox Code Playgroud)

要了解原因,请运行以下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));
}
Run Code Online (Sandbox Code Playgroud)

并且如上面的测试所示,通过表达式体的比较也会失败,但是字符串比较也起作用,所以这也有效:

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

这里还有一种可以添加到工具库中的测试方式:

[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());
}
Run Code Online (Sandbox Code Playgroud)

由于您有许多不应该失败的测试用例,您可能会遇到其他问题.

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

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;
Run Code Online (Sandbox Code Playgroud)

一个表达式引用包含方法的实例变量.另一个表示引用静态类的const成员的表达式.无论在运行时可能分配给变量的值如何,这两者都是不同的表达式.但是,如果string normal_type更改为,const string normal_type则表达式再次与表达式const右侧的每个引用a相同.