C# 儿童班级的单元测试

C# 儿童班级的单元测试,c#,unit-testing,class-hierarchy,C#,Unit Testing,Class Hierarchy,假设我们有一个超简单的类层次结构: public class SomeMath { public int Add(int x, int y) { return x + y; } } public class MoreMath : SomeMath { public int Subtract(int x, int y) { return x - y; } } 当我为MoreMath类编写测试时,是否应该为Add方法

假设我们有一个超简单的类层次结构:

public class SomeMath
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}

public class MoreMath : SomeMath
{
    public int Subtract(int x, int y)
    {
        return x - y;
    }
}
当我为
MoreMath
类编写测试时,是否应该为
Add
方法编写测试?或者,当我测试
SomeMath
类时,我应该只关心该方法吗?更一般地说:我应该测试一个类的所有方法,还是只测试“新”方法


我可以为双方提出一些理由。例如,当测试所有方法时,您将不止一次地测试同一事物,这不是很好,而且可能会变得单调乏味。但是如果你不测试所有的方法,
SomeMath
中的一个变化可能会破坏
MoreMath
的用法?这也是一件坏事。我想这可能也要视情况而定。比如它是否扩展了我控制的类。但是无论如何,我是一个完全的测试新手,所以我很想知道哪些人比我想的更聪明:-)

通常我不会在子类中测试行为,除非子类改变了父类中预期的行为。如果它使用相同的行为,则无需对其进行测试。如果您计划对父类进行破坏性更改,但子类仍然需要旧的行为,那么我将首先在子类中创建测试以定义其所需的行为,然后在父类测试和后续代码更改中进行更改。这遵循了YAGNI原则——您不需要它——并延迟了子测试的实现,直到它们真正有了目的。

我只测试SomeMath类的
Add
方法。如果
MoreMath
只继承了它,并且完全不做任何新的事情,那么为这两种代码编写测试将是纯粹的重复代码,仅此而已。在这些事情上保持一点务实总是好的。

首先,我认为至少有两个因素可能会影响你做一件或另一件事的决定:

  • 遗产的目的是什么
  • 这项测试的目的是什么
<>在TDD场景中,我倾向于编写一个单一的测试用例,用于验证它是从SOMMASE派生的,然后考虑所有从SomeMath继承的成员被覆盖。

然而,这意味着,从设计的角度来看,MoreMath是从SomeMath派生出来的一个重要设计方面。如果您以多态的方式使用SomeMath,则肯定会出现这种情况。但是,如果您只是简单地使用Inheritance进行重用,情况就不会如此了(不过,不推荐这样做)

在后一种情况下(继承用于重用),父类和子类之间没有概念上的联系,将来可能会试图破坏继承。在这种情况下,进行测试以验证父项是否正确是一种糟糕的保护措施

从质量保证(QA)的角度来看,每个类别的每个成员都应该进行严格的测试。这意味着您应该为每个子类重复测试代码,即使测试代码是相同的,因为您需要验证没有以意外方式重写虚拟方法。然而,为了保持干燥,您可以将它们作为参数化测试来编写,或者可以使用诸如


就我个人而言,我很少进入QA阶段。通常,在TDD期间创建的测试套件是一个足够的安全网。。。但这一切都取决于您构建的软件的类型。

我将使我的
MoreMath
测试类继承自
SomeMath
测试类,从而继承该类的所有测试。这意味着我只需要为新功能编写额外的测试,但所有子类的功能都经过了全面测试。

在我目前的位置上,我们遇到了一个类似的问题,我们希望开发接口,但要确保接口的每个实现都能正确运行(例如,不同的数据层应该表现相同,而不管该层的实现如何)。我们解决它的方法(使用NUnit)是在单元测试中有一个继承结构:

public interface ICalculator
{
  public int Add(int a, int b)
}

public class Calculator : ICalculator
{
  public int Add(int a, int b) { return a + b; }
}

public class TestCalculatorInterface
{
   public abstract SomeMath GetObjectToTest();

   [Test]
   public void TestAdd()
   {
       var someMath = GetObjectToTest();
       ...
   }
}    

[TestFixture]
public class TestCalculator: TestCalculatorInterface
{
   public virtual Calculator GetObjectToTest() { return new Calculator(); }
}
我们在基本接口测试中没有[TestFixture]属性,但在所有测试方法中都有[test]属性。TestCalculator类就是[TestFixture]但是继承了基类的所有测试,这使得子类只负责为该接口提供要测试的对象


我会为您的案例采用类似的模式,因此测试针对所有类运行,但只编写一次。

我认为这个答案假设单元测试仅以TDD方式编写。在我看来,这只是答案的一半,但请参阅我的答案进行更全面的检查。考虑到您提出的问题类型没有明确的对错答案我认为在7分钟后接受答案是个坏主意。因为它涵盖了更多的角度:)