Moq如何正确地模拟只有set的属性

14

如何使用Moq处理暴露了只读属性的接口?之前我添加了其他访问器,但是这导致我的代码过于混乱,到处都是随机的throw new NotImplementedException()语句。

我想要做一些简单的事情,比如:

mock.VerifySet(view => view.SetOnlyValue, Times.Never());

但是这会产生编译错误:The property 'SetOnlyValue' has no getter


1
@Igor Zevaka和其他人:view => view.SetOnlyValue'生成了The property 'SetOnlyValue' has no getter'。这是因为这段代码被解释为对getter的调用。在读写情况下,VerifySet遍历表达式并确定所需的是属性setter上的期望。在只写情况下,这会遇到编译器错误。我们需要假设OP在这里不是愚蠢的,并且不是想给自己增加工作量,而是在问“如何避免做额外的无用功”。 - Ruben Bartelink
2
哦,我明白了。所以这是关于在moq中设置验证,而不依赖于需要getter的属性,对吗? - Igor Zevaka
嗨。我已经查看了这个答案,只是想再确认一下。所以,如果我有一个仅可设置的属性,并且我不关心其值,我不能使用VerifySet(v => v.SetOnlyProp)。相反,我必须使用VerifySet(v => v.SetOnlyProp = It.IsAny<X>())。是这样吗? - superjos
@superjos 是的,完全正确!第一个 verifyset 是调用 SetOnlyProp 的 getter 的操作(Action),它将其转换为 setter 的表达式。但是由于该属性没有 getter,所以会导致构建失败。当您使用第二个 verifyset 时,您直接创建了该表达式。 - Chris Marisic
太好了(我明白了,但这个东西有点糟糕 :)) 感谢您的快速回复。 - superjos
显示剩余2条评论
2个回答

18
public class Xyz
{
    public virtual string AA { set{} }
}
public class VerifySyntax
{
    [Fact]
    public void ThisIsHow()
    {
        var xyz = new Mock<Xyz>();
        xyz.Object.AA = "bb";
        // Throws:
        xyz.VerifySet( s => s.AA = It.IsAny<string>(), Times.Never() );
    }
}
public class SetupSyntax
{
    [Fact]
    public void ThisIsHow()
    {
        var xyz = new Mock<Xyz>();
        xyz.SetupSet( s => s.AA = It.IsAny<string>() ).Throws( new InvalidOperationException(  ) );
        Assert.Throws<InvalidOperationException>( () => xyz.Object.AA = "bb" );
    }
}

+1 - 阅读了这篇文章后,我删除了我的回答,因为它似乎是OP想要的。 - Lee
@李:很酷,谢谢(我知道我读到这个问题时也犹豫了一下!) - Ruben Bartelink
谢谢Ruben,我永远不会随意决定将一个It.IsAny表达式放在Resharper说是编译错误的一行旁边! - Chris Marisic
我知道你可以专门测试设置,但我从未想过这会实际更改由SetupSet调用的方法重载以避免编译错误。 - Chris Marisic
1
@Chris Marisic:不确定我是否表达清楚。我的意思是,表达式view => view.SetOnlyValue调用getter,而像view => view.SetOnlyValue = 0这样的东西将调用setter。为了避免使用It之类的东西,在Setup/VerifySet的上下文中,第一种风格(getter)被解释为引用setter,即使底层代码是getter调用。没有我感觉你正在解释我之前的答案所暗示的那种魔法。 - Ruben Bartelink

3

感谢Ruben!

为了帮助有关VB.Net的一个小技巧,以下是相同的VB.Net代码:

Public Interface Xyz
    WriteOnly Property AA As String
End Interface
Public Class VerifySyntax
    <Fact()>
    Public Sub ThisIsHow()
        Dim xyz = New Mock(Of Xyz)
        xyz.Object.AA = "bb"
        ' Throws:
        xyz.VerifySet(Sub(s) s.AA = It.IsAny(Of String)(), Times.Never())
    End Sub
End Class
Public Class SetupSyntax
    <Fact()>
    Public Sub ThisIsHow()
        Dim xyz = New Mock(Of Xyz)
        xyz.SetupSet(Sub(s) s.AA = It.IsAny(Of String)()).Throws(New InvalidOperationException())
        Assert.Throws(Of InvalidOperationException)(Sub() xyz.Object.AA = "bb")
    End Sub
End Class

重要的是,您不能使用单行Function lambda,因为这将被解释为返回值的表达式,而不是您需要的赋值语句。这是因为VB.Net将单个等号不仅用于赋值,还用于相等比较,因此尝试执行以下操作:
        xyz.VerifySet(Function(s) s.AA = It.IsAny(Of String)(), Times.Never())

这段代码将被解释为 s.AA-value 和 It.IsAny(Of String)() 的布尔比较,从而调用 getter,这将导致编译错误。相反,你应该使用一个 Sub lambda(或可能是多行 Function lambda)。

但如果在属性上有一个getter,Function lambda仍然有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接