【问题标题】:Invalid setup on a non virtual member using MOQ使用 MOQ 的非虚拟成员上的设置无效
【发布时间】:2016-06-12 21:18:36
【问题描述】:

我正在moqing一个界面,它有:

Dictionary<string, object> InstanceVariables { get; set; }

我创建了一个新的界面模拟并尝试对其进行设置,使其仅返回一个随机字符串,如下所示:

_mockContext.SetupGet(m => m.InstanceVariables[It.IsAny<string>()]).Returns(@"c:\users\randomplace");

但我似乎得到了一个错误:

{"Invalid setup on a non-virtual (overridable in VB) member: m => m.InstanceVariables[It.IsAny<String>()]"}

这到底是什么意思?我在嘲笑界面,所以这不是问题吗?

谢谢

【问题讨论】:

  • 如果可以创建字典,为什么还需要模拟Dictionary 将所有必填字段?
  • 因为我想在执行我在 mockContext 对象中传递的方法时更改 InstanceVariables 产生的内容
  • 您的财产是Dictionary,而不是IDictionary。因此,要使其编译,您必须模拟具体的实现,不是您所说的接口(尽管我不能确定,因为您没有发布该代码)。因此,错误是正确的 - 您不能覆盖非虚拟成员(并且字典索引器不是虚拟的)。但是,@Valentin 的问题仍然存在 - 为什么不直接创建一个字典,其值设置为单元测试所需的值 - 字典是一个愚蠢的数据存储 - 没有可以模拟的功能!

标签: c# unit-testing testing moq


【解决方案1】:

我不建议这样做的原因有很多。首先,正如 cmets 中所提到的,没有理由说真正的字典在这里不起作用。 We shouldn't mock types that what we don't ownonly mock types we do own
_mockContext.SetupGet(m => m.InstanceVariables[It.IsAny<string>()]).Returns(@"c:\users\randomplace"); 行试图模拟Dictionary<T, T> 上的Get,@RB 注释不是virtual,因此你的错误。您可能在模拟 您的 界面,但那里的设置在 .NET 字典中。

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

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

将更强大,因为只有在正确的密钥被提供给/或由您的被测系统 (sut) 生成时,字典才能响应路径。


也就是说,如果你绝对必须模拟一个字典,原因在问题上并不明显......那么你接口上的属性需要是字典的接口,IDictionary,而不是具体的类:

IDictionary<string, object> InstanceVariables { get; set; }

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

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

然后在上下文模拟上:

_mockContext.SetupGet(d => d.InstanceVariables).Returns(dictionary.Object);

为什么需要是虚拟的?

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

这是因为它会生成一个代理来实现接口 或创建一个派生类来覆盖那些可覆盖的方法 为了拦截电话。
Credit to @aqwert

【讨论】:

  • 好答案。要查看具体类Dictionary&lt;,&gt; 的索引器确实是非虚拟的,请参见its documentation。要查看接口IDictionary&lt;,&gt; 是否包含成员(索引器),请参阅its page。我绝对同意最好在测试中创建一个标准的填充Dictionary&lt;,&gt;,然后从模拟中返回它。但是,在第二种方法中,您不必设置It.IsAny&lt;string&gt;()。如果需要,您只能设置 d =&gt; d["someKey"]
猜你喜欢
  • 2017-03-29
  • 2014-07-19
  • 1970-01-01
  • 2015-09-05
  • 1970-01-01
  • 2017-05-12
  • 1970-01-01
  • 2012-09-27
  • 1970-01-01
相关资源
最近更新 更多