当我尝试解决与起订量不同的情况下,我试图使用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的单元测试报告异常(&#34;为什么我在这里?&#34;)抛出方法ExecuteMe(),这证明即使有安装程序也执行ExecuteMe()方法( x =&gt; x.ExecuteMe())来防止它。其他两个单元测试通过(显然不执行ExecuteMe())。
我甚至尝试在ExecuteMe()的Setup上设置Callback,但结果相同。我也颠倒了Setup和SetupSet的顺序(在代码中),但没有用。
为什么SetupSet可能影响方法设置?
答案 0 :(得分:1)
为什么SetupSet可能影响方法设置?
我相信这是Moq的一个错误。请你这么善良,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可以分析这些数据以确定您希望模拟执行的操作(在安装过程中),或模拟应该发生的事情(在验证期间)。
但是,由于C#编译器的限制(即它不能将包含赋值的lambda转换为表达式树),Moq的SetupSet
不能使用表达式树;相反,它接受普通的Action<TMock>
,即一段无法直接分析的代码。然而,Moq需要根据这段代码执行设置。在这种情况下发生的是Moq以类似记录器的“干运行”模式(内部称为FluentMockContext
)调用此lambda。然后它观察“干运行”造成的影响,并将其设置动作建立在它们上面。
现在我们在comment above中找到@Kritner提到的一点:
SetupSet
遍历构造函数,其他任何设置/验证都没有。
调用委托意味着它必须实际实例化mock对象,以便它可以将它作为参数传递给你的setup lambda。这意味着您的模拟类型的构造函数将运行。而且因为你已经指定了CallBase = true
,所以在那次干旱运行中,Moq会调用你的ExecuteMe
基础实现。这就是为什么我们最终会抛出你的方法。
这里的错误是CallBase
并不真正适用于“干运行”原则,因为CallBase
的整个目的是在模拟类型中执行用户代码,这就是谎言在Moq的控制范围之外,因此不知道(并且正确地不应该知道)它应该以“干运行”模式执行。
整个“干运行”模拟模式适用于许多常见的使用场景,但基本上存在缺陷。我已经确定了quite a few problems with Moq that are caused by it,并且我一直在寻找替换用于它的内部组件(FluentMockContext
)的方法(method decompilation等)。
请将此文件作为Moq GitHub存储库的错误提交,我会将其添加到问题列表中。