【问题标题】:Best way to override a class's dependency for a unit test?覆盖单元测试的类依赖项的最佳方法?
【发布时间】:2021-08-17 04:13:23
【问题描述】:

tl;dr 试图了解测试类的正确方法,但需要覆盖依赖项的某些成员。下面我描述了我尝试子类化此依赖项的思考过程,但也许我应该只重新实现依赖项的接口并跳过尝试完全覆盖和子类化依赖项?


好的,我正在尝试对依赖于调用对象的类进行单元测试。我想让该对象的属性可变,这样我就可以在测试期间更改它们,而不是让它调用网络。我认为这很棘手,因为父类在相关属性上只有一个 getter。我对 C# 有点陌生,希望能确认/纠正我目前的推理。

我有一个界面:

public interface ISettings
{
    bool IsFooEnabled { get; }
}

还有一个班级

public class Settings : ISettings
{
    public bool IsFooEnabled => bool.Parse(DoNetworkStuff());
}

我在测试中做了一个此类: 图一:

public class TestSettings : Settings
{
    public bool IsFooEnabled = true;
}

我希望将被覆盖的对象传递给对象的构造函数,因为通常SomeObject 需要一个ISettings 对象:

public void Test()
{
    TestSettings s = new TestSettings();
    s.IsFooEnabled = true;
    SomeObject s = new SomeObject(s);
}

所以测试失败是因为DoNetworkStuff() 正在返回null: 图 1.1:

System.ArgumentNullException : Value cannot be null. (Parameter 'value')
  Stack Trace: 
    Boolean.Parse(String value)
    Settings.get_IsFooEnabled() line 113

我认为是因为

同样的错误: 图2:

public class TestSettings : Settings
{
    public bool IsFooEnabled { get; set; }
}

和 图3:

public class TestSettings : Settings
{
        public bool IsFooEnabled => false;
}

和 图4:

public class TestSettings : Settings
{
        public bool IsFooEnabled = false;
}

全部返回图1.1的错误。这意味着Settings.IsFooEnabled 没有被覆盖,它仍在尝试调用并获得空值。我对此感到困惑,因为我认为在子类中,子类的定义优先。但是预期的类型是Settings,所以我认为TestSettings IsFooEnabled 被忽略了。

但这有效: 图4:

public class TestSettings : Settings
{
    public override bool IsFooEnabled => false;
}

但是我必须将设置更改为虚拟,否则它会抱怨,这是有道理的,因为在父类中没有虚拟就无法覆盖: 图5:

public class Settings : ISettings
{
    public virtual bool IsFooEnabled => bool.Parse(DoNetworkStuff());
}

我也尝试自行覆盖该属性: 图6:

public class TestSettings : Settings
{
    public override bool IsFooEnabled = false;
}

但是得到了这个错误:

Severity    Code    Description Project File    Line    Suppression State
Error   CS0106  The modifier 'override' is not valid for this item  Foo.Unit.Tests  Foo.cs  33  N/A

这是有道理的,因为您不能覆盖属性。 我认为总的来说我有点困惑为什么属性声明不会覆盖父类的属性表达式。我认为父母有一个事实;意味着它必须是另一个覆盖它的 getter,它不能只是一个属性?

最后的问题,这是处理这种情况的正确方法吗?我是否从根本上误解了 C# 继承的某些部分? 抱歉啰嗦了,但我真的很想了解这里发生了什么。

【问题讨论】:

  • 这似乎是一个XY problem,最终是一个设计问题,因为目标/主题类,如果它遵循 SOLID 原则,将依赖于抽象 ISettings 而不是具体化,即:Settings,这将导致尝试覆盖成员静音的整个问题,因为它不再与实现细节紧密耦合。
  • 好的,这是我没有写的另一个选项,但是是的,只是实现接口及其所有成员似乎是一种更简单的方法,尽管如果有很多我必须实现类的所有成员.
  • @Nkosi - “没有实际意义”,而不是“静音”。

标签: c# unit-testing inheritance polymorphism overriding


【解决方案1】:

显然,您不希望 TestSettings 直接实现 ISettings,因为您还需要实现大量其他成员。

解决这个问题的一种方法是“部分模拟”。许多模拟库都提供了这一点。使用模拟库通常比“手动”模拟更好。

除此之外:您可以覆盖属性。不起作用的情况不是一个属性,而只是一个字段。以下变体应该可以正常工作(它们只是定义属性的不同方式):

public class TestSettings : Settings
{
    public override bool IsFooEnabled
    {
        get;
    }
}

public class TestSettings : Settings
{
    public override bool IsFooEnabled => true;
}

这不起作用,因为它是一个字段,而不是 C# 中不同的属性。

public class TestSettings : Settings
{
    public override bool IsFooEnabled = true; // syntax error
}

【讨论】:

  • 哦,谢谢!今天我学习了字段、属性和方法之间的区别!你上面在 TestSettings 中描述的 IsFooEnabled 属性也可以有一个设置器吗?即{get;set;}
  • 是的。这正是您定义它的方式。
猜你喜欢
  • 2019-04-07
  • 1970-01-01
  • 2014-03-18
  • 1970-01-01
  • 2011-10-14
  • 2020-09-03
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
相关资源
最近更新 更多