【问题标题】:Why does my argument matcher fail when I extract it into a variable? [duplicate]当我将它提取到变量中时,为什么我的参数匹配器会失败? [复制]
【发布时间】:2013-03-22 17:20:17
【问题描述】:

我很难提取任何It.Is<T> 参数匹配器变量。每当我这样做时,测试都会失败。

这行得通:

calculatorMock
    .Setup(x => x.Produce(It.Is<IEnumerable<Report>>(xx => reports.IsEqualTo(xx))))
    .Returns(calculatorInputs);

但是,这失败了:

var argumentMatcher = It.Is<IEnumerable<Report>>(xx => reports.IsEqualTo(xx));
calculatorMock
    .Setup(x => x.Produce(argumentMatcher))
    .Returns(calculatorInputs);

IsEqualTo 是一个返回 bool 的静态方法。

问题是 Moq 说 Produce() 是用一个空列表调用的,而我希望它被一个包含 3 个项目的列表调用。在此示例中,xx 表示空列表。我不知道为什么我的起订量验证需要内联参数匹配器。

我刚刚发现以下工作:

Expression<Func<IEnumerable<Report>, bool>> expression = x => reports.IsEqualTo(x);
calculatorMock
    .Setup(x => x.Produce(It.Is(expression)))
    .Returns(calculatorInputs);

It.Is&lt;T&gt; 无法像我上面尝试的那样被提取,是否有特定原因?

这是问题的工作副本:

使用系统; 使用 System.Linq.Expressions; 使用最小起订量; 使用 Xunit;

命名空间 MoqArgumentMatcher { 课堂节目 { 静态无效主要(字符串 [] 参数) { var testRunner = new TestRunner();

        testRunner.Passes();
        testRunner.Fails();

        Console.ReadKey();
    }
}

public class TestRunner
{
    [Fact]
    public void Passes()
    {
        // Arrange
        var calculatorMock = new Mock<ICalculator>();
        var consumer = new CalculatorConsumer(calculatorMock.Object);
        var report = new Report {Id = 1};

        // Act
        consumer.Consume(report);

        // Assert
        calculatorMock.Verify(x => x.Produce(
            It.Is<Report>(xx => xx.Id == 1)), Times.Once());
    }

    [Fact]
    public void Passes2()
    {
        // Arrange
        var calculatorMock = new Mock<ICalculator>();
        var consumer = new CalculatorConsumer(calculatorMock.Object);
        var report = new Report { Id = 1 };

        // Act
        consumer.Consume(report);

        // Assert
        Expression<Func<Report, bool>> expression = x => x.Id == 1;
        calculatorMock.Verify(x => x.Produce(It.Is(expression)), Times.Once());
    }

    [Fact]
    public void Fails()
    {
        // Arrange
        var calculatorMock = new Mock<ICalculator>();
        var consumer = new CalculatorConsumer(calculatorMock.Object);
        var report = new Report {Id = 1};

        // Act
        consumer.Consume(report);

        // Assert
        var argumentMatcher = It.Is<Report>(xx => xx.Id == 1);
        calculatorMock.Verify(x => x.Produce(argumentMatcher), Times.Once());
    }
}

public class CalculatorConsumer
{
    private readonly ICalculator _calculator;

    public CalculatorConsumer(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public void Consume(Report report)
    {
        _calculator.Produce(report);
    }
}

public interface ICalculator
{
    void Produce(Report report);
}

public class Report
{
    public int Id { get; set; }
}

}

【问题讨论】:

  • “这失败了”的描述太模糊了。 如何失败了?
  • @JonSkeet:你完全正确。我已经添加了我能想到的所有内容。
  • 你能在一个简短但完整的程序中重现这个吗?这将使测试更容易。我怀疑这与类型推断有关,但很难确定。 (Moq 文档链接在项目页面上不起作用也无济于事!)
  • @JustinPihony:感谢您提出的建议。这似乎正是我所遇到的。

标签: c# moq


【解决方案1】:

Passes2Fails 测试之间的区别是最容易理解的,至少在我看来,在测试失败的情况下,表达式链中断。

首先要注意的是It.Is的签名:

TValue It.Is<TValue>(Expression<Func<TValue, bool>> match)

特别注意,当它被执行时,它返回一个TValue 的实例,而不是一个Expression。接下来要注意的是Verify 的签名需要Expression(类型为ActionFunc),其中一个是调用所需的方法。

当 Moq 执行 Verify 方法时,它会查看表达式并提取它正在验证的方法调用,然后提取为被调用方法提供值的表达式部分,在本例中为 report 参数Produce(Report report)。然后它编译这个小参数表达式子树,以针对用于调用Produce 方法的值来执行以确定它是否匹配。

在 Passes 和 Passes2 的情况下,它能够提取 Expression&lt;Func&lt;Report, bool&gt;&gt;。编译器知道它应该将代码解析为表达式,因此为It.Is 调用创建一个表达式树。

如果是Fails,就在这一行……​​

var argumentMatcher = It.Is<Report>(xx => xx.Id == 1);

...编译器看到对It.Is 的调用,该调用将在代码运行后立即进行评估。因此,它确定var 的类型将是TValue(返回类型),而不是任何Expression。因此,当在Verify 调用中看到argumentMatcher 时,它现在是表达式树中的一个叶节点,一个简单的变量。

在运行时,argumentMatcher 可能被评估为null。 Moq 看到 parameter-expression-sub-tree 是一个值而不是 Func,执行与该值的比较,即 null,而不是根据需要执行与 1 的比较。

(尽管 OP 对另一个问题的答案感到满意,但这是本着回答开放性问题的精神!)

【讨论】:

  • 这是一个很棒的答案!非常感谢您提供了令人难以置信的深刻答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-12
  • 2016-10-21
相关资源
最近更新 更多