【问题标题】:Unit testing inheritance单元测试继承
【发布时间】:2009-02-12 12:45:13
【问题描述】:

我有一个关于单元测试的问题。假设我有几个类从父类继承行为。我不想测试所有子类的这种行为。相反,我会测试父类。但是,我还应该提供一个测试来证明该行为在子类中可用。你认为像 Assert.IsTrue(new ChildClass() is ParentClass) 这样的东西有意义吗?

【问题讨论】:

    标签: c# unit-testing tdd


    【解决方案1】:

    如果你使用的是最先进的单元测试框架,我不明白这个说法

    我不想测试所有子类的这种行为。

    如果您将子类的实例交给单元测试(针对父类的实例编写)应该不会发生变化,假设您的子类没有'不要以破坏继承方法契约的方式覆盖父行为的某些方面。这正是您需要测试的内容,不是吗?

    如果您担心运行测试所需的时间,我会仔细检查以确保您的测试已分区,以便您可以根据您正在积极处理的内容有选择地运行测试(但仍然定期运行完整的产品组合以捕获意外的依赖关系。)

    【讨论】:

    • 阿门。这是一个很好的理由来实际测试“继承有效”(即符合 Liskov 替换原则),而不是仅仅验证子类是父类的子类型。
    • 我不关心运行测试所需的时间。我只想编写尽可能少的测试,以便将来更轻松地进行分解。
    • @trendl - 如果您使用 NUnit,您可以为基类创建一个单元测试并使用 [TestCase] 属性来提供您拥有的所有子类 - 这可以按照建议的方式进行检查Joel 免费为您的所有子类提供服务。
    • 有一些帖子告诉你不要在测试类上使用继承,例如petrikainulainen.net/programming/unit-testing/…
    • 说我实际上同意你的观点,我认为这些帖子看到人们滥用继承,所以宣扬不要使用它,而不是说你应该正确使用它
    【解决方案2】:

    让您的测试从基类测试继承,[非常]大致是这样的。

    public class MyBaseClass
    {
        public virtual void DoStuff() { }
        public virtual void DoOtherStuff() { }
    }
    
    public class MySubClass : MyBaseClass
    {
        public override void DoOtherStuff()
        {
            // different to to base
        }
    }
    
    public abstract class TestOfBaseClass
    {
        protected abstract MyBaseClass classUnderTest { get; }
    
        [TestMethod]
        public void TestDoStuff()
        {
            classUnderTest.DoStuff();
            Assert.StuffWasDone();
        }
    }
    
    [TestClass]
    public class WhenSubclassDoesStuff : TestOfBaseClass
    {
        protected override MyBaseClass classUnderTest
        {
            get { return new MySubClass(); }
        }
    
        [TestMethod]
        public void ShoudDoOtherStuff()
        {
            classUnderTest.DoOtherStuff();
            Assert.OtherStuffDone();
        }
    }
    

    大多数流行的测试框架在运行子类测试时都会运行基础测试中的测试方法。

    或者看看https://github.com/gregoryyoung/grensesnitt之类的东西

    【讨论】:

    • 这正是我们想要的。谢谢。
    【解决方案3】:

    我认为编写继承有效的测试是浪费时间。如果您尝试使用基类方法,编译器将检查它们是否可用,假设您没有使用智能感知。我可能会在一个子类中测试行为,然后只在每个修改行为的子类(或行为所依赖的某些状态)中测试。

    【讨论】:

    • writing a test that inheritance works is a waste of time. The compiler will check... 单元测试捕获的问题和编译器捕获的问题是完全分开的。单元测试需要编译才能成功。 The compiler will check that the base class methods are available if you try to use them 问题不在于遵守基类契约,而是确认(正确继承的方法)内部的 行为 是一致的。
    【解决方案4】:

    您不想测试对象的类型,除非它来自无类型的工厂方法。否则,您正在针对 C# 编译器编写单元测试,这不是您想要做的。

    【讨论】:

    • 您可能会争辩说,继承链没有被破坏的测试是一个有效的测试。毕竟你可以拥有继承类 B 的类 A 继承类 C 并且在未来的某个时刻,开发人员决定 A 直接指向 C 会更好,而没有意识到 A 依赖于 B 执行的数据转换跨度>
    【解决方案5】:

    C# 编译器会为您处理此类检查。

    如果你喜欢,你可以写这样的东西:

    ParentClass childClass = new ChildClass()
    var testOutput = childClass.ParentMethod();
    Assert.IsNotNull(testOutput);
    

    【讨论】:

      【解决方案6】:

      我的观点是你的测试结构应该反映你的对象结构 所以如果你有继承自 Class Vehicle 的 Class Car

      那么你应该有继承自 Class TestVehicle 的 Class TestCar

      通过这样做,Test car 会自动继承所有 Vehicle 测试的正确测试,这意味着如果该函数在 Car 中被覆盖,它很可能会破坏测试,突出显示 Car 中需要 Overriden Test 以支持新行为,如果测试没有被破坏,那么覆盖可能首先是多余的

      例如

      class Vehicle
      {
          public virtual bool LicenceRequired
          {
              get{throw new NotImplmentedException()
          }
      }
      class Bicycle:Vehicle
      {
          public override bool LicenceRequired
          {
              get{return false;}
          }
      }
      class Car:Vehicle
      {
          public override bool LicenceRequired
          {
              get{return true;}
          }
      }
      class TestVehicle
      {
          public virtual Void LicenceRequiredTest()
          {
              Try
              {
                  LicenceRequired
                  Assert.Fail();
              }
              Catch(){}
          }
      }
      class TestBicycle:TestVehicle
      {
          public override void LicenceRequiredTest()
          {
              Assert.IsFalse(LicenceRequired);
          }
      }
      class TestCar:TestVehicle
      {
          public override void LicenceRequiredTest()
          {
              Assert.IsTrue(LicenceRequired);
          }
      }
      

      注意:仅应在测试继承对象时使用继承,而不仅仅是因为您想对 10 个不相关的对象进行相同的测试。如果您想这样做,我建议您使用静态助手类

      【讨论】:

        【解决方案7】:

        您可以使用两种方法来测试基类的行为

        1. 创建基类的存根实现,并对存根进行单元测试。只测试公共方法。测试您的私有和受保护方法是没有意义的,因为这些将被您应该在子类中测试的公共方法使用。这种方法不会强制您的基类实现没有错误地隐藏行为。
        2. 为执行基类方法的单元测试创​​建测试超类。每当您测试基类的子类时,让您的测试类继承自您的测试超类。这种方法可确保您不会无意中更改基类的行为,但会限制测试的灵活性。

        不要费心去验证继承是否真的有效(整个Assert.IsTrue(new ChildClass() is ParentClass) 事情)。你应该只测试行为。这是 .Net 框架的一个结构特征(继承),您应该相信它是有效的,否则您会发现自己在检查框架功能时会陷入螺旋式下降。

        【讨论】:

        • 单元测试的定义是你测试尽可能小的组件,所以单一的方法和属性意味着你应该总是单独测试你的子调用
        猜你喜欢
        • 2011-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-01
        • 2010-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多