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

时间:2016-06-12 21:18:36

标签: c# unit-testing testing moq

我是一个界面,它有:

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>()]"}

这究竟是什么意思?我在嘲笑界面所以不应该这不是问题吗?

由于

1 个答案:

答案 0 :(得分:7)

我建议反对它的原因有很多。首先,正如评论中所提到的,没有理由说真正的字典在这里不起作用。 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");行试图模仿Get Dictionary<T, T> {@ 1}},因为@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