为什么我收到消息"非虚拟(在VB中可覆盖)成员上的无效设置..."?

Rah*_*dha 163 c# unit-testing moq

我有一个单元测试,我必须模拟一个返回bool类型的非虚方法

public class XmlCupboardAccess
{
    public bool IsDataEntityInXmlCupboard(string dataId,
                                          out string nameInCupboard,
                                          out string refTypeInCupboard,
                                          string nameTemplate = null)
    {
        return IsDataEntityInXmlCupboard(_theDb, dataId, out nameInCupboard, out refTypeInCupboard, nameTemplate);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我有一个XmlCupboardAccess类的模拟对象,我试图在我的测试用例中为这个方法设置mock,如下所示

[TestMethod]
Public void Test()
{
    private string temp1;
    private string temp2;
    private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();
    _xmlCupboardAccess.Setup(x => x.IsDataEntityInXmlCupboard(It.IsAny<string>(), out temp1, out temp2, It.IsAny<string>())).Returns(false); 
    //exception is thrown by this line of code
}
Run Code Online (Sandbox Code Playgroud)

但是这条线引发了异常

Invalid setup on a non-virtual (overridable in VB) member: 
x => x.IsDataEntityInXmlCupboard(It.IsAny<String>(), .temp1, .temp2, 
It.IsAny<String>())
Run Code Online (Sandbox Code Playgroud)

有什么建议如何绕过这个例外?

Amo*_*mol 253

Moq无法模拟非虚方法和密封类.在使用模拟对象运行测试时,MOQ实际上创建了一个内存代理类型,它继承自"XmlCupboardAccess"并覆盖您在"SetUp"方法中设置的行为.正如您在C#中所知,只有在标记为虚拟的情况下才能覆盖某些内容,而Java则不然.Java假定默认情况下每个非静态方法都是虚拟的.

我认为你应该考虑的另一件事是为你的"CupboardAccess"引入一个界面,然后开始嘲笑界面.它可以帮助您解耦代码并从长远来看有益处.

最后,有一些框架如:TypeMockJustMock直接与IL一起工作,因此可以模拟非虚方法.然而,两者都是商业产品.

  • 关于你应该只模拟接口这一事实的+1.这个问题解决了我遇到的问题,因为我不小心嘲笑了类而不是底层接口. (57认同)

Ral*_*alt 27

作为任何与我有同样问题的人的帮助,我不小心错误地输入了实现类型而不是接口,例如

var mockFileBrowser = new Mock<FileBrowser>();
Run Code Online (Sandbox Code Playgroud)

代替

var mockFileBrowser = new Mock<IFileBrowser>();
Run Code Online (Sandbox Code Playgroud)


Bry*_*ida 5

请参阅 为什么我要模拟的属性需要是虚拟的?

您可能必须编写一个包装器接口或将该属性标记为virtual / abstract,因为Moq创建了一个代理类,该类用于拦截呼叫并返回您放入.Returns(x)呼叫中的自定义值。


小智 5

您应该模拟该类接口,而不是模拟具体类。从 XmlCupboardAccess 类中提取接口

public interface IXmlCupboardAccess
{
    bool IsDataEntityInXmlCupboard(string dataId, out string nameInCupboard, out string refTypeInCupboard, string nameTemplate = null);
}
Run Code Online (Sandbox Code Playgroud)

而不是

private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();
Run Code Online (Sandbox Code Playgroud)

改成

private Mock<IXmlCupboardAccess> _xmlCupboardAccess = new Mock<IXmlCupboardAccess>();
Run Code Online (Sandbox Code Playgroud)