C# 验证对字典中索引属性的调用
我试图模拟一个包含字典的类,并验证对字典特定索引的调用 类的接口如下所示:C# 验证对字典中索引属性的调用,c#,dictionary,dependency-injection,mocking,moq,C#,Dictionary,Dependency Injection,Mocking,Moq,我试图模拟一个包含字典的类,并验证对字典特定索引的调用 类的接口如下所示: public interface IClassWithADictionary { IDictionary<string, string> Dictionary { get; } } 带有词典的公共接口IClass { IDictionary字典{get;} } 这门课看起来像: public class ConcreteClassWithADictionary : IClassWithADicti
public interface IClassWithADictionary
{
IDictionary<string, string> Dictionary { get; }
}
带有词典的公共接口IClass
{
IDictionary字典{get;}
}
这门课看起来像:
public class ConcreteClassWithADictionary : IClassWithADictionary
{
public ConcreteClassWithADictionary(IDictionary<string, string> dictionary)
{
Dictionary = dictionary;
}
public IDictionary<string, string> Dictionary { get; }
}
public class-ConcreteClassWithADictionary:IClassWithADictionary
{
带有词典(IDictionary dictionary)的公共具体类
{
字典=字典;
}
公共IDictionary字典{get;}
}
现在这里是我不明白的地方,我有一个测试用例,我试图验证字典中的特定键是否已设置,然后我试图验证是否已检索到该键
[Test]
public void MyTest()
{
var concreteClassWithADictionaryMock = new Mock<IClassWithADictionary>();
var dictionary = new Dictionary<string, string>();
concreteClassWithADictionaryMock
.Setup(m => m.Dictionary)
.Returns(dictionary); // Setting up mock to return a concrete dictionary
var key = "Key";
var value = "Value";
concreteClassWithADictionaryMock.Object.Dictionary[key] = value; // Setting the value
var test = concreteClassWithADictionaryMock.Object.Dictionary[key]; // Getting the value
// Passes here - no index specified
concreteClassWithADictionaryMock.Verify(m => m.Dictionary);
// Passes here - with VerifyGet() too
concreteClassWithADictionaryMock.VerifyGet(m => m.Dictionary);
// Fails here - throws exception, "Expression is not a property access: m => m.Dictionary[.key]"
concreteClassWithADictionaryMock.VerifyGet(m => m.Dictionary[key]);
//Fails here - no invocation performed, doesn't seem to like the set or the key indexer
concreteClassWithADictionaryMock.VerifySet(m => m.Dictionary[key] = value);
// Fails here - no invocation performed, even with verifying index access of some kind
concreteClassWithADictionaryMock.Verify(m => m.Dictionary[key]);
}
[测试]
公共无效MyTest()
{
var concreteClassWithADictionaryMock=new Mock();
var dictionary=newdictionary();
带adictionarymock的混凝土类
.Setup(m=>m.Dictionary)
.Returns(dictionary);//设置mock以返回具体的dictionary
var key=“key”;
var value=“value”;
concreteClassWithADictionaryMock.Object.Dictionary[key]=value;//设置值
var test=concreteClassWithADictionaryMock.Object.Dictionary[key];//获取值
//在此处传递-未指定索引
concreteClassWithADictionaryMock.Verify(m=>m.Dictionary);
//在此传递-也使用VerifyGet()传递
concreteClassWithADictionaryMock.VerifyGet(m=>m.Dictionary);
//此处失败-引发异常,“表达式不是属性访问:m=>m.Dictionary[.key]”
带有adictionarymock.VerifyGet(m=>m.Dictionary[key])的具体类;
//此处失败-未执行任何调用,似乎不喜欢集合或键索引器
concreteClassWithADictionaryMock.VerifySet(m=>m.Dictionary[key]=value);
//此处失败-即使验证某种索引访问,也不会执行任何调用
验证(m=>m.Dictionary[key]);
}
显然,Moq框架可以验证字典本身的获取和设置,但不能验证特定的键或索引。我的问题是,;在模拟类中验证字典中特定键的获取和设置的正确方法是什么?答案有些琐碎
您应该模拟mock类返回的字典,并对其调用verify方法。有趣的是,我之前没有注意到它,但那已经是深夜了。基本上,卡梅隆是这么说的:你应该嘲笑IDictionary。原因如下:
var dictionary = new Dictionary<string, string>(); // <---- HEEERE
concreteClassWithADictionaryMock
.Setup(m => m.Dictionary)
.Returns(dictionary); // Setting up mock to return a concrete dictionary
// ...
concreteClassWithADictionaryMock.Verify(m => m.Dictionary); // A
concreteClassWithADictionaryMock.VerifyGet(m => m.Dictionary[key]); // B
接下来,是什么执行录制?不,当然是正常的典型课程。所有的模拟都是用来录音的
mConcrete.Dictionary[key] :
1) mock get_Dictionary (property getter) <-- 'm' records the call
and returns dict
according to setup
注意,在最后一个示例中,我正在对dictionary mock进行验证。这是因为mConcrete仍然不知道除了它自己以外的任何其他对象上发生了什么
最后一点:Moq实际上支持动态创建“模拟对象图”。发件人:
//创建一个自动递归模拟:一个模拟,它将为每个没有期望且其返回值可以模拟的成员返回一个新的模拟(即,它不是值类型)
//默认DefaultValue是DefaultValue.Empty,即ref类型为NULL
//让我们改变这一点
var mock=new mock{DefaultValue=DefaultValue.mock};
//此属性访问通常会返回NULL
//但现在返回一个模拟酒吧
Bar值=mock.Object.Bar;
//返回的mock被重用,因此可以进一步访问返回的属性
//相同的模拟实例。这允许我们也使用这个实例来
//如果我们愿意,就对它设定进一步的期望
var barMock=Mock.Get(值);
Setup(b=>b.Submit()).Returns(true);
如果您有“嵌套”接口,比如这里的-IConcreteClass返回IDictionary(它确实返回IDictionary而不是Dictionary,对吗?:),那么您可以使用此功能使IDictionary模拟(以及任何内部)由Moq自动创建。您使用的是实际的词典,而不是模拟的词典,因此Moq不知道已访问的词典的成员。我假设提供的示例代码片段仅用于演示目的,因为不应模拟被测主题。当然,抱歉,我应该澄清一下,测试用例并不能完全代表我的场景,这只是一个解释我误解的例子:)
mConcrete.Dictionary[key] :
1) mock get_Dictionary (property getter) <-- 'm' records the call
and returns dict
according to setup
mConcrete.Dictionary[key] :
1) ...
2) dict get_Item (aka. this[]) <-- plain Dict returns item
no recording happens
var mDictionary = new Mock<IDictionary<string, string>>();
mDictionary.Setup(d => d["key"]).Returns("value");
var mConcrete = new Mock<IClassWithADictionary>();
mConcrete
.Setup(m => m.Dictionary)
.Returns(mDictionary.Object); // Setting up mock to return a mock
var test = mConcrete.Object.Dictionary[key]; // Getting the value
// verify if the Dictionary-Itself was read
mConcrete.Verify(m => m.Dictionary);
// verify if the Dictionary-Contents was read
mDictionary.Verify(m => m.Dictionary["key"]);
// Make an automatic recursive mock: a mock that will return a new mock for every member that doesn't have an expectation and whose return value can be mocked (i.e. it is not a value type)
// default DefaultValue is DefaultValue.Empty, i.e. NULL of ref types
// let's change that
var mock = new Mock<IFoo> { DefaultValue = DefaultValue.Mock };
// this property access would normally return a NULL
// but now returns a mock of Bar
Bar value = mock.Object.Bar;
// the returned mock is reused, so further accesses to the property return
// the same mock instance. this allows us to also use this instance to
// set further expectations on it if we want
var barMock = Mock.Get(value);
barMock.Setup(b => b.Submit()).Returns(true);