【问题标题】:How to MOQ an Indexed property如何最小起订量索引属性
【发布时间】:2010-09-25 08:17:06
【问题描述】:

我正在尝试模拟对索引属性的调用。 IE。我想要起订量以下:

object result = myDictionaryCollection["SomeKeyValue"];

还有setter值

myDictionaryCollection["SomeKeyValue"] = myNewValue;

我这样做是因为我需要模拟我的应用使用的类的功能。

有人知道如何使用最小起订量来做到这一点吗?我尝试了以下变体:

Dictionary<string, object> MyContainer = new Dictionary<string, object>();
mock.ExpectGet<object>( p => p[It.IsAny<string>()]).Returns(MyContainer[(string s)]);

但这不会编译。

我想通过 MOQ 实现的目标是否可行,有没有人可以举例说明我如何做到这一点?

【问题讨论】:

  • 只使用存根对象不是更容易吗?设置所需的值并检查您需要的索引。

标签: c# tdd mocking moq


【解决方案1】:

不清楚您要做什么,因为您没有显示模拟的声明。你想模拟字典吗?

MyContainer[(string s)] 不是有效的 C#。

这样编译:

var mock = new Mock<IDictionary>();
mock.SetupGet( p => p[It.IsAny<string>()]).Returns("foo");

【讨论】:

  • 这也有效。除了新语法使用SetupSet 而不是ExpectGet。也适用于二传手。
  • IDictionary 来自 System.Collections 而不是来自 System.Collections.Generic
  • 如果我想根据键返回不同的值怎么办?
  • @Ahmad 然后使用 It.Is() 而不是 It.IsAny()。见stackoverflow.com/questions/9122070/…
【解决方案2】:

看来我试图用最小起订量做的事情是不可能的。

基本上我是在尝试最小化一个 HTTPSession 类型的对象,其中被设置为索引的项的键只能在运行时确定。访问索引属性需要返回先前设置的值。这适用于基于整数的索引,但基于字符串的索引不起作用。

【讨论】:

    【解决方案3】:

    Ash,如果你想模拟 HTTP Session,那么这段代码就可以了:

    /// <summary>
    /// HTTP session mockup.
    /// </summary>
    internal sealed class HttpSessionMock : HttpSessionStateBase
    {
        private readonly Dictionary<string, object> objects = new Dictionary<string, object>();
    
        public override object this[string name]
        {
            get { return (objects.ContainsKey(name)) ? objects[name] : null; }
            set { objects[name] = value; }
        }
    }
    
    /// <summary>
    /// Base class for all controller tests.
    /// </summary>
    public class ControllerTestSuiteBase : TestSuiteBase
    {
        private readonly HttpSessionMock sessionMock = new HttpSessionMock();
    
        protected readonly Mock<HttpContextBase> Context = new Mock<HttpContextBase>();
        protected readonly Mock<HttpSessionStateBase> Session = new Mock<HttpSessionStateBase>();
    
        public ControllerTestSuiteBase()
            : base()
        {
            Context.Expect(ctx => ctx.Session).Returns(sessionMock);
        }
    }
    

    【讨论】:

    • 不错 - 这正是我所需要的 +1
    • 这段代码的哪一部分使被测代码使用模拟会话? TestSuiteBase 是图书馆还是你自己的班级?
    • 这不是模拟,是假的。只是说。
    【解决方案4】:

    这并不难,但花了一点时间才找到它:)

    var request = new Moq.Mock<HttpRequestBase>();
    request.SetupGet(r => r["foo"]).Returns("bar");
    

    【讨论】:

      【解决方案5】:

      正如您正确发现的那样,SetupGetSetupSet 有不同的方法来分别初始化 getter 和 setter。尽管SetupGet 旨在用于属性,而不是索引器,并且不允许您处理传递给它的键。准确地说,对于索引器,SetupGet 无论如何都会调用Setup

      internal static MethodCallReturn<T, TProperty> SetupGet<T, TProperty>(Mock<T> mock, Expression<Func<T, TProperty>> expression, Condition condition) where T : class
      {
        return PexProtector.Invoke<MethodCallReturn<T, TProperty>>((Func<MethodCallReturn<T, TProperty>>) (() =>
        {
          if (ExpressionExtensions.IsPropertyIndexer((LambdaExpression) expression))
            return Mock.Setup<T, TProperty>(mock, expression, condition);
          ...
        }
        ...
      }
      

      为了回答您的问题,下面是使用底层 Dictionary 存储值的代码示例:

      var dictionary = new Dictionary<string, object>();
      
      var applicationSettingsBaseMock = new Mock<SettingsBase>();
      applicationSettingsBaseMock
          .Setup(sb => sb[It.IsAny<string>()])
          .Returns((string key) => dictionary[key]);
      applicationSettingsBaseMock
          .SetupSet(sb => sb["Expected Key"] = It.IsAny<object>())
          .Callback((string key, object value) => dictionary[key] = value);
      

      如您所见,您必须明确指定键来设置索引器设置器。详细信息在另一个 SO 问题中描述:Moq an indexed property and use the index value in the return/callback

      【讨论】:

      • 这就是我要找的!!其他示例均无效
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2018-09-06
      • 2011-01-25
      • 2021-08-22
      • 2013-05-31
      • 2011-06-15
      • 1970-01-01
      相关资源
      最近更新 更多