在C#中,即使使用SetupProperty或SetupSet,当setter非常重要时,Moq VerifySet也会抛出Expression is not a property setter invocation.
。
这是一个简单的例子。请注意,Antlers setter是微不足道的,而Antlers2 setter并不简单。:
public class Dancer
{
public Dancer(bool pIsMale)
{
IsMale = pIsMale;
}
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 bool Antlers2
{
get { return this._Antlers; }
set
{
// females cannot have antlers
if (IsMale)
this._Antlers = value;
else
this._Antlers = false;
}
}
}
以下是单元测试。第二组三个(使用Antlers2)在其他方面与第一组三个相同(使用Antlers)。使用Antler的所有单元测试都通过了测试。所有使用Antlers2的单元测试抛出Expression is not a property setter invocation.
,即使使用SetupProperty,我认为整个属性的实现完全被忽略并被Moq取代。
public class DancerTests
{
[Fact]
public void Antlers_NoSetup()
{
// Arrange
// create mock of class under test
var sut = new Mock<Dancer>(true) { CallBase = true };
// 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<Dancer>(true) { CallBase = true };
sut.SetupProperty(x => x.Antlers, false);
// 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<Dancer>(true) { CallBase = true };
sut.SetupSet(x => x.Antlers = true);
// Act
sut.Object.Antlers = true;
// Assert
sut.VerifySet(x => x.Antlers = true);
}
[Fact]
public void Antlers2_NoSetup()
{
// Arrange
// create mock of class under test
var sut = new Mock<Dancer>(true) { CallBase = true };
// Act
sut.Object.Antlers2 = true;
// Assert
sut.VerifySet(x => x.Antlers2 = true);
}
[Fact]
public void Antlers2_SetupProperty()
{
// Arrange
// create mock of class under test
var sut = new Mock<Dancer>(true) { CallBase = true };
sut.SetupProperty(x => x.Antlers2, false);
// Act
sut.Object.Antlers2 = true;
// Assert
sut.VerifySet(x => x.Antlers2 = true);
}
[Fact]
public void Antlers2_SetupSet()
{
// Arrange
// create mock of class under test
var sut = new Mock<Dancer>(true) { CallBase = true };
sut.SetupSet(x => x.Antlers2 = true);
// Act
sut.Object.Antlers2 = true;
// Assert
sut.VerifySet(x => x.Antlers2 = true);
}
}
什么是关于混淆Moq的VerifySet的非平凡的基类属性设置器?我正在使用Moq 4.7.99,Visual Studio 2015,目标是.Net Framework 4.5.2。
感谢您的帮助!我从StackOverflow中受益匪浅!
答案 0 :(得分:0)
所以看起来你有很多条件正在创造这个奇怪的场景。这可能是一个值reporting的错误,我不确定。无论如何,你头晕目眩似乎来自于设置Callbase = true
和/或从IsMale
属性设置器中调用Antler2
。
设置Callbase = true
会为您班级中的每个虚拟方法创建一个MockInvocation
- 包括IsMale
(稍后这将很重要)。从表面上看,这看起来并不是什么大不了的事,但事实证明确实如此。在SetupSetImpl
方法中,Moq会抛出异常。此方法验证 Moq调用的最后一个MockInvocation
是否为setter - 它通过检查上一个调用方法的名称以'set_'开头来执行此操作。
使用普通的“普通的setter”,最后一次调用将是Antlers2
的设置器,但是你引入了一个扭曲。在你的二传手中,你打电话给IsMale
,因为你在创建模拟时也设置了Callbase = true
,你的MockInvocation
属性就有一个IsMale
。那么当您设置Antler2
属性时会发生什么''set_Antler2'被添加到调用列表中,然后在IsMale
属性setter中调用Antler2
时,'get_IsMale'被添加到调用列表。因此,当SetupSetImpl
检查上次调用是否是一个setter时,它会找到一个getter和panics。
我已经测试并确认至少有两种方法可以防止此异常。首先是不设置Callbase = true
。但这是一个问题,因为您需要模拟对象将Antler2
属性设置器传递给基础对象 - 这是测试的整个点。另一个更简单的解决方案是完全绕过IsMale
属性,只需直接从Antler2
属性设置器调用支持字段,如下所示:
if (this._IsMale == true) // Notice I'm calling _IsMale
this._Antlers2 = value;
else
this._Antlers2 = false;
即使设置了Callbase = true
,这仍然有效,因为您没有调用IsMale
属性,因此不会将get_IsMale
添加到调用列表中。相反,您只需检查IsMale
属性使用的支持字段。