gpt4 book ai didi

c# - 验证对字典中索引属性的调用

转载 作者:行者123 更新时间:2023-11-30 17:30:55 27 4
gpt4 key购买 nike

我正在尝试模拟一个包含字典的类,并验证对字典特定索引的调用。

类的接口(interface)如下:

public interface IClassWithADictionary
{
IDictionary<string, string> Dictionary { get; }
}

类看起来像:

public class ConcreteClassWithADictionary : IClassWithADictionary
{
public ConcreteClassWithADictionary(IDictionary<string, string> dictionary)
{
Dictionary = dictionary;
}

public IDictionary<string, string> Dictionary { get; }
}

现在这是我不明白的地方,我有一个测试用例,我试图验证字典中的特定键已经设置,稍后我试图验证是否已检索到此 key 。

[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]);
}

显然,Moq 框架可以验证字典本身的获取和设置,但不能验证特定的键或索引。我的问题是;在模拟类中验证字典中特定键的获取和设置的正确方法是什么?

最佳答案

有趣的是我之前没有注意到它,但那是深夜。基本上,这就是 Cameron 所说的:你应该 mock 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

Mock 之间不共享信息。
此外,模拟不是病毒。它们不会感染其他类来注入(inject)额外的功能。
此外,模拟不是分析器。它们不会改变运行时来注入(inject)额外的功能。

这使得 Mock 没有千里眼:他们只能看到对他们所做的事情,而不知道他们周围发生的事情。

现在我们可以进入正题:要使 Verify 正常工作,必须记录方法调用。方法调用发生在对象上。接到电话的对象知道它。运行时知道它。 Potential profiler 知道这一点。没有其他人。

首先,我们这里到底有什么调用和对象?

mConcrete.Dictionary[key] :
1) mock get_Dictionary (property getter)
2) dict get_Item (aka. this[])

接下来,什么执行记录?当然没有正常的典型类(class)。都是 Mock 做的录音。

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

现在,当您执行 Verify 时,verify 可以验证与“m”相关的任何内容,但是当您开始“向下钻取”到 m.Dict[] 时,它会碰壁,因为 Dictionary 是哑的,没有记录任何东西,更糟糕的是 - 它不是 Mock,所以它甚至不响应预期的与记录相关的接口(interface)。

如何解决?进行适当的设置并尽可能多地模拟。模拟 IDictionary 而不是使用普通 IDictionary。

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"]);

请注意,在最后一个中,我正在验证字典模拟。那是因为 mConcrete 仍然不知道除了它自己之外的任何其他对象发生了什么。

此处的最后一个兴趣点:Moq 实际上支持即时创建“模拟对象图”。来自 Moq Quickstart :

// 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);

如果您有“嵌套”接口(interface),就像这里 - IConcreteClass 返回 IDictionary(它确实返回 IDictionary 而不是 Dictionary,对吧?:))那么您可以使用此功能自动创建 IDictionary 模拟(和任何内部)最小起订量。

关于c# - 验证对字典中索引属性的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48875536/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com