C# 使用MOQ在非虚拟成员上的设置无效

C# 使用MOQ在非虚拟成员上的设置无效,c#,unit-testing,testing,moq,C#,Unit Testing,Testing,Moq,我是莫清,一个界面,它有: 字典实例变量{get;set;} 我创建了一个新的接口模拟,并尝试将其设置为只返回一个随机字符串,如下所示: \u mockContext.SetupGet(m=>m.InstanceVariables[It.IsAny()])。返回(@“c:\users\randomplace”) 但我似乎犯了一个错误: {“非虚拟(在VB中可重写)成员上的设置无效:m=>m.InstanceVariables[It.IsAny()]”} 这到底是什么意思?我在模仿界面,所以这不是

我是莫清,一个界面,它有:

字典实例变量{get;set;}

我创建了一个新的接口模拟,并尝试将其设置为只返回一个随机字符串,如下所示:

\u mockContext.SetupGet(m=>m.InstanceVariables[It.IsAny()])。返回(@“c:\users\randomplace”)

但我似乎犯了一个错误:

{“非虚拟(在VB中可重写)成员上的设置无效:m=>m.InstanceVariables[It.IsAny()]”}

这到底是什么意思?我在模仿界面,所以这不是一个问题吗


谢谢

我有很多理由反对它。首先,正如评论中提到的,没有理由认为真正的字典在这里不起作用。和
\u mockContext.SetupGet(m=>m.InstanceVariables[It.IsAny()])。返回(@“c:\users\randomplace”)
试图模拟
字典
上的
获取
,而@RB notes不是
虚拟的
,因此您的错误。您可能在模拟您的接口,但设置在.NET字典上

其次,IMO,
It.IsAny()
会导致非常弱的测试,因为它会响应任何字符串。结构类似于:

const string MyKey = "someKey";

var dictionary =  new Dictionary<string, object>();
dictionary.Add(MyKey, @"c:\users\randomplace");
_mockContext.Setup(m => m.InstanceVariables).Returns(dictionary);

var sut = new SomeObject(_mockContext.Object());
var result = sut.Act(MyKey);

// Verify
然后,您可以通过以下方式创建字典模拟:

var dictionary = new Mock<IDictionary<string, object>>();
dictionary.SetupGet(d => d[It.IsAny<string>()]).Returns(@"c:\users\randomplace");

为什么它需要是虚拟的?

Moq和其他类似的模拟框架只能模拟接口, 抽象方法/属性(在抽象类上)或虚拟 具体类的方法/属性

这是因为它生成了一个将实现接口的代理 或者创建一个派生类,该派生类重写中的那些可重写方法 命令拦截呼叫。


我反对它有很多理由。首先,正如评论中提到的,没有理由认为真正的字典在这里不起作用。和
\u mockContext.SetupGet(m=>m.InstanceVariables[It.IsAny()])。返回(@“c:\users\randomplace”)
试图模拟
字典
上的
获取
,而@RB notes不是
虚拟的
,因此您的错误。您可能在模拟您的接口,但设置在.NET字典上

其次,IMO,
It.IsAny()
会导致非常弱的测试,因为它会响应任何字符串。结构类似于:

const string MyKey = "someKey";

var dictionary =  new Dictionary<string, object>();
dictionary.Add(MyKey, @"c:\users\randomplace");
_mockContext.Setup(m => m.InstanceVariables).Returns(dictionary);

var sut = new SomeObject(_mockContext.Object());
var result = sut.Act(MyKey);

// Verify
然后,您可以通过以下方式创建字典模拟:

var dictionary = new Mock<IDictionary<string, object>>();
dictionary.SetupGet(d => d[It.IsAny<string>()]).Returns(@"c:\users\randomplace");

为什么它需要是虚拟的?

Moq和其他类似的模拟框架只能模拟接口, 抽象方法/属性(在抽象类上)或虚拟 具体类的方法/属性

这是因为它生成了一个将实现接口的代理 或者创建一个派生类,该派生类重写中的那些可重写方法 命令拦截呼叫。


如果可以创建一个字典,那么为什么需要模拟一个
字典
呢?因为当我执行在mockContext对象中传递给属性的方法时,我想更改InstanceVariables产生的内容。您的属性是
字典
,而不是
IDictionary
。因此,要使其编译,您必须模拟具体的实现,而不是您所说的接口(尽管我不能确定,因为您没有发布该代码)。因此,错误是正确的-您不能覆盖非虚拟成员(并且字典索引器不是虚拟的)。然而,@Valentin的问题仍然存在——为什么不创建一个字典,其中的值按照您对单元测试的期望设置——字典是一个愚蠢的数据存储——这里没有可模仿的功能!如果可以创建一个字典,那么为什么需要模拟一个
字典
呢?因为当我执行在mockContext对象中传递给属性的方法时,我想更改InstanceVariables产生的内容。您的属性是
字典
,而不是
IDictionary
。因此,要使其编译,您必须模拟具体的实现,而不是您所说的接口(尽管我不能确定,因为您没有发布该代码)。因此,错误是正确的-您不能覆盖非虚拟成员(并且字典索引器不是虚拟的)。然而,@Valentin的问题仍然存在——为什么不创建一个字典,其中的值按照您对单元测试的期望设置——字典是一个愚蠢的数据存储——这里没有可模仿的功能!回答得好。要查看具体类
字典
的索引器确实是非虚拟的,请参阅。要查看接口
IDictionary
是否包含成员(索引器),请参阅。我完全同意,最好在测试中创建一个标准的
字典
,并从模拟中返回。但是,在第二种方法中,您不必设置
It.IsAny()
。如果需要,您只能设置
d=>d[“someKey”]
。回答得好。要查看具体类
字典
的索引器确实是非虚拟的,请参阅。要查看接口
IDictionary
是否包含成员(索引器),请参阅。我完全同意,最好在测试中创建一个标准的
字典
,并从模拟中返回。但是,在第二种方法中,您不必设置
It.IsAny()
。如果需要,您只能设置
d=>d[“someKey”]