【问题标题】:Can I use Moq to set values for fields?我可以使用 Moq 为字段设置值吗?
【发布时间】:2013-04-09 19:16:40
【问题描述】:

我有一个要模拟的依赖项。

public abstract class MyDependency
{
    protected Dictionary<string, object> _outputs = new Dictionary<string, object>();
    public Dictionary<string, object> Outputs
    {
        get
        {
            return _outputs;
        }
    }
}

我需要公共属性Outputs 来返回一个已知值以用于我的单元测试。我知道我们不能模拟字段或非虚拟成员。因此,我可以创建自己的模拟 MyDependency 来设置支持字段 _outputs

public class MockMyDependency : MyDependency
{
    public MockMyDependency()
    {
        _outputs = new Dictionary<string, object>
        {
            { "key", "value" }
        };
    }
}

但是,是否可以在不显式创建自己的派生模拟类的情况下使用 Moq 来执行此操作?

【问题讨论】:

  • SetupGet() 是否适用于抽象类?
  • @48klocs 是的,但要正确模拟一个属性(或方法),该属性必须标记为abstractvirtual。不幸的是,我无法修改 MyDependency 类。
  • 建议编辑:标题中的“属性”,而不是字段,因为问题和答案关注。

标签: c# .net mocking moq


【解决方案1】:

如果您不能将属性更改为虚拟属性或定义接口(我假设如果您不能执行第一个,您也将无法执行第二个),那么我只看到两个可能的选项:

  • 使用支持模拟非虚拟成员的模拟框架(例如 TypeMock)
  • 创建一个从 MyDependency 继承的特定模拟实现,就像您已经做的那样。

【讨论】:

    【解决方案2】:

    如果您自己不从该类派生,则很难指定protected 字段_outputs,因为它不在范围内,并且它的名称不会编译。

    但你当然可以用讨厌的反射来做到这一点:

    var mock = new Mock<MyDependency>();
    
    var field = typeof(MyDependency)
        .GetField("_outputs", BindingFlags.Instance | BindingFlags.NonPublic);
    field.SetValue(mock.Object,
        new Dictionary<string, object> { { "key", "value" } });
    

    如果你确实MyDependency派生,那么在派生自它的类中,你可以直接设置字段,如下所示:

    // this code is inside some class which derives from MyDependency
    mock.Object._outputs = new Dictionary<string, object> { { "key", "value" } };
    

    【讨论】:

      【解决方案3】:

      认为我明白你在问什么,我认为你正在寻找这样的东西:

      public class Test
      {
          public void MockObject()
          {
              var mocked = new Moq.Mock<MyDependency>();
              mocked.CallBase = true;
              mocked.Setup(m => m.Outputs).Returns(new Dictionary<string, object>() { { "key", "value" } });
      
              mocked.Object.{do something with mocked object};
          }
      }
      

      ...我认为您仍然需要在 Outputs 属性上使用 virtual

      【讨论】:

      • 是的,Outputs 属性上必须有 virtual 关键字才能使 Setup 起作用。不幸的是,修改依赖项对我来说并不是一个真正的选择,因为它属于不同的组。
      【解决方案4】:

      声明一个接口,从 MyDependency 类中公开您想要的成员,然后您可以将它与 Moq 一起使用来创建您的测试所需的行为。

      public interface IMyDependency {
          Dictionary<string, object> Outputs { get; }
      }
      
      // test...
      var dictionary = new Dictionary<string, string>{{"key", "value"}};
      var dependency = new Mock<IMyDependency>();
      dependency.SetupGet(d => d.Outputs).Returns(dictionary);
      

      当您在生产代码中使用它时,您可以使用 DuckTyping(或类似名称)将 MyDependency 实例“投射”到您的界面后面。

      【讨论】:

        猜你喜欢
        • 2011-10-24
        • 2013-02-26
        • 1970-01-01
        • 1970-01-01
        • 2018-02-22
        • 1970-01-01
        • 2023-01-26
        • 1970-01-01
        相关资源
        最近更新 更多