我很难提取任何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的静态方法。
问题在于,当我期望使用包含3个项目的列表调用它时,Moq说Produce()
被调用了空列表。在此示例中,xx
表示空列表。我不确定为什么我需要在我的Moq验证中使用参数匹配器。
我刚刚发现以下作品:
Expression<Func<IEnumerable<Report>, bool>> expression = x => reports.IsEqualTo(x);
calculatorMock
.Setup(x => x.Produce(It.Is(expression)))
.Returns(calculatorInputs);
是否有一个特定的原因导致无法像我上面尝试的那样提取It.Is<T>
?
以下是问题的工作副本:
使用System; 使用System.Linq.Expressions; 使用Moq; 使用Xunit;
命名空间MoqArgumentMatcher { 课程 { static void Main(string [] args) { 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; }
}
}
答案 0 :(得分:1)
Passes2
和Fails
测试之间的差异至少对我来说是最容易理解的,因为在测试失败的情况下,表达链中断了。
首先要注意的是It.Is
的签名:
TValue It.Is<TValue>(Expression<Func<TValue, bool>> match)
请特别注意,执行该实例时,它会返回TValue
的实例,而不是Expression
。接下来要注意的是Verify
的签名需要Expression
(类型为Action
或Func
),其中一个是调用所需方法。
当Moq执行Verify
方法时,它查看表达式并提取它正在验证的方法调用,然后提供正在调用的方法的值的表达式部分,在本例中为{{1 report
中的参数。然后,它编译这个小的参数表达式子树,以执行用于调用Produce(Report report)
方法的值来确定它是否匹配。
在Passes和Passes2的情况下,它能够提取Produce
。编译器知道应该将代码解析为表达式,因此为Expression<Func<Report, bool>>
调用创建表达式树。
在It.Is
的情况下,在这一行......
Fails
...编译器看到对var argumentMatcher = It.Is<Report>(xx => xx.Id == 1);
的调用,一旦代码运行就会对其进行评估。因此,它确定It.Is
的类型将是var
(返回类型),而不是任何TValue
。因此,当在Expression
调用中看到argumentMatcher
时,它现在是表达式树中的叶节点,一个简单的变量。
在运行时,Verify
可能会被评估为argumentMatcher
。 Moq,看到参数表达式子树是一个值而不是null
,执行与值Func
的比较,而不是执行与null
的比较为期望的。
(尽管OP对另一个问题的答案感到满意,但本着回答开放式问题的精神!)