单一方法或泛型的模拟行为松散 It.IsAny

Mock behavior loose for single method or generic It.IsAny

我有一个摘要 class Shape 和几个继承自它的 classes (Square, Rectangle, Triangle , Circle, ...)。我还有一个 AreaCalculator class,其中包含一个名为 CalculateAreasByShape() 的方法。此方法为每个 Shape 类型调用一系列 CalculateShapeAreas<TShape>(List<TShape> shapes) 方法。

现在,在我的单元测试中,我有以下代码:

...
var areaMock = new Mock<AreaCalculator>(MockBehavior.Strict);
areaMock.Setup(m => m.CalculateAreasByShape()).Returns(new Dictionary<Type, decimal>());
var canvas = new DemoCanvas(shapes, perimeterMock.Object, areaMock.object);
var result = canvas.RunShapeCalculations();
...

此代码失败的原因是 MockBehavior 设置为 Strict 并且没有设置 CalculateShapeAreas<TShape>() 方法。现在,我知道我可以 运行 每个形状类型的设置,例如:

areaMock.Setup(m => m.CalculateShapeAreas<Triangle>(It.IsAny<List<Triangle>>())).Returns(default(decimal));

但这会产生很多重复代码。我想知道我们是否可以利用所有形状都继承自相同基数的事实 class。我尝试了以下方法,但它不起作用:

areaMock.Setup(m => m.CalculateShapeAreas<Shape>(It.IsAny<List<Shape>>())).Returns(default(decimal));

或者,是否可以在单个泛型方法上设置 MockBehavior.Loose(意思是,它不会检查实际类型)?

It.IsAny 开放通用支持尚未出现在 moq(版本 4.12.0)中。终于有计划介绍这个了,看看here


我认为您面临的问题是因为您使用 List<T> 作为输入参数。您可能会注意到 List<T> 不是协变的。

什么意思?

List<Shape> shapeList;
IEnumerable<Shape> shapeEnumerable;
var triag = new List<Triangle>();

shapeList = triag; // NOT ALLOWED
shapeEnumerable = triag; // ALLOWED

模拟时同样会产生问题。一种解决方案可能是更改 interface 而不是传递 List<T> 传递 IEnumerable<out T>,或者正如@JeppeStigNielsen 指出的那样 IReadOnlyList<out T>。事实上,可以使用由 List<T> 实现的 T 接口中的任何协变。

areaMock
    .Setup(m => m.CalculateShapeAreas<Shape>(It.IsAny<IEnumerable<Shape>>()))
    .Returns(default(decimal));