当我尝试使用 Moq 解决不同的情况时,我尝试使用 SetupSet 来解决。这揭示了另一个潜在的问题。
当我在属性上使用 SetupSet 以及方法上的设置时,Moq 似乎“忘记”了方法上的设置已经完成。
下面是示例代码,非常简单:
public class Prancer
{
public Prancer(bool pIsMale)
{
IsMale = pIsMale;
ExecuteMe();
}
private bool _IsMale;
public virtual bool IsMale
{
get { return this._IsMale; }
private set { this._IsMale = value; }
}
private bool _Antlers;
public virtual bool Antlers
{
get { return this._Antlers; }
set
{
this._Antlers = value;
}
}
public virtual void ExecuteMe()
{
throw new Exception("Why am I here?");
}
}
单元测试如下:
public class PrancerTests
{
[Fact]
public void Antlers_NoSetup()
{
// Arrange
// create mock of class under test
var sut = new Mock<Prancer>(true) { CallBase = true };
sut.Setup(x => x.ExecuteMe()); // nullify
// Act
sut.Object.Antlers = true;
// Assert
sut.VerifySet(x => x.Antlers = true);
}
[Fact]
public void Antlers_SetupProperty()
{
// Arrange
// create mock of class under test
var sut = new Mock<Prancer>(true) { CallBase = true };
sut.SetupProperty(x => x.Antlers, false);
sut.Setup(x => x.ExecuteMe()); // nullify
// Act
sut.Object.Antlers = true;
// Assert
sut.VerifySet(x => x.Antlers = true);
}
[Fact]
public void Antlers_SetupSet()
{
// Arrange
// create mock of class under test
var sut = new Mock<Prancer>(true) { CallBase = true };
sut.SetupSet(x => x.Antlers = true);
sut.Setup(x => x.ExecuteMe()); // nullify
// Act
sut.Object.Antlers = true;
// Assert
sut.VerifySet(x => x.Antlers = true);
}
}
我使用 SetupSet 的单元测试报告在方法 ExecuteMe() 中抛出异常(“我为什么在这里?”),这证明即使有 Setup(x => x.ExecuteMe) 方法 ExecuteMe() 也会执行()) 来阻止它。其他两个单元测试通过(显然不执行 ExecuteMe())。
我什至尝试在 ExecuteMe() 的设置上放置一个回调,但结果相同。我还颠倒了 Setup 和 SetupSet 的顺序(在代码中),但无济于事。
有没有想过为什么 SetupSet 会影响方法 Setup?
最佳答案
Any thoughts why the SetupSet could have affected the method Setup?
我认为这是最小起订量中的错误。请你这么客气file an issue at Moq's GitHub repository moq/moq4 ? (只需包括您在此处发布的代码,或链接到此 SO 问题。)
我将尝试解释这里发生了什么。 (这对您来说听起来很熟悉,因为您已经在 GitHub 上报告了类似的问题;为了 SO 访问者,我在这里重复解释。)让我们先看看您对 SetupSet
的调用。 :
sut.SetupSet(x => x.Antlers = true);
Moq 在其设置和验证方法中大量使用 LINQ 表达式树(Expression<Action<TMock,…>>
或 Expression<Func<TMock,…>>
)。表达式只是“作为数据的代码”,Moq 可以对其进行分析以确定您希望您的 mock 做什么(在设置期间),或者您的 mock 应该发生什么(在验证期间)。
但是,由于 C# 编译器的限制(即它无法将包含赋值的 lambda 转换为表达式树),Moq 的 SetupSet
不能使用表达式树;相反,它接受普通的 Action<TMock>
,即一段无法直接分析的代码。然而,Moq 需要根据这段代码执行设置。在这种情况下发生的是 Moq 在类似记录器的“试运行”模式(内部称为 FluentMockContext
)中调用此 lambda。然后,它会观察该“试运行”造成的影响,并根据这些影响执行设置操作。
现在我们到了@Kritner 在 comment above 中提到的一点:
SetupSet
goes through the constructor, where none of your other setup/verifies do.
调用委托(delegate)意味着它必须实际实例化模拟对象,以便它可以将它作为参数传递给您的设置 lambda。这意味着您的模拟类型的构造函数将运行。因为你指定了 CallBase = true
, 在那次试运行期间,最小起订量会调用你的 ExecuteMe
基础实现。这就是为什么我们最终采用您抛出的方法。
这里的错误是 CallBase
并不真正适用于“预演”原则,因为 CallBase
的全部目的是在模拟类型中执行用户代码,它位于 Moq 的控制之外,因此不知道(而且理应永远不必知道)它应该以“试运行”模式执行。
整个“试运行”模拟模式适用于许多常见的使用场景,但存在根本性缺陷。我已经确定 quite a few problems with Moq that are caused by it我一直在寻找方法(method decompilation 等)来替换用于它的内部组件(FluentMockContext
)。
请将此作为错误提交到 Moq 的 GitHub 存储库中,我会将其添加到问题列表中。
关于c# - SetupSet 'forgets'方法设置的使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45375841/