儿童班的单元测试

时间:2009-11-23 12:53:22

标签: c# unit-testing class-hierarchy

假设我们有这个超级简单的类hierchy:

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;
    }
}

在为Add类编写测试时,是否应该为MoreMath方法编写测试?或者,当我测试SomeMath类时,我应该只关心该方法吗?更一般地说:我应该测试一个类的所有方法,还是应该只测试“新”方法?

我可以想出双方的一些理由。例如,在测试所有方法时,您最终会不止一次地测试相同的东西,这不是很好,而且可能变得乏味。但是,如果您不测试所有方法,SomeMath中的更改可能会破坏MoreMath的用法?这也是一件坏事。我想这也可能取决于案例。就像它扩展了一个类,我可以控制或不控制。但无论如何,我是一个全新的测试新手,所以我很想知道人们比我想的更聪明: - )

5 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:1)

我只测试SomeMath类的Add方法。如果MoreMath只继承它并且绝对没有什么新东西,那么为两者编写测试将是纯粹的重复代码,仅此而已。用这些东西做一点务实总是好的。

答案 2 :(得分:1)

在我目前的地方,我们遇到了类似的问题,我们希望开发接口,但确保接口的每个实现都表现正确(例如,不管该层的实现如何,不同的数据层应该表现相同)。我们解决它的方式(使用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] ,但继承了基类的所有测试,这使得子类只负责提供测试该接口的对象。

我会为你的情况采用类似的模式,所以测试是针对所有类运行的,但只写一次。

答案 3 :(得分:0)

首先,我认为至少有两个因素可能会影响您做出其中一个的决定:

  • 继承的目的是什么?
  • 相关测试的目的是什么?

在TDD场景中,我倾向于为MoreMath编写一个测试用例来验证它是从SomeMath派生的,然后考虑从SomeMath继承的所有成员都被覆盖。

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

在后一种情况下(继承用于重用),父类和子类之间没有概念连接,并且您可能想要在将来中断继承。在这种情况下,进行测试验证父母是否正确是一个很差的保护措施。

从质量保证(QA)的角度来看,每个班级的每个成员都应该经过严格的测试。这意味着即使测试代码相同,您也应该为每个子类重复测试代码,因为您需要验证没有以意外方式覆盖虚拟方法。但是,要保持DRY,您可以将它们编写为参数化测试,或者使用Pex等工具。

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

答案 4 :(得分:0)

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