2009-11-23 156 views
4

说我们有这个超简单的类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的使用情况?这也会是一件坏事。我想这可能也取决于案件。就像它扩展了一个我可以控制或不控制的类。但无论如何,我是一个全面测试新手,所以我很想知道人们比我想的更聪明:-)

+1

考虑到你问了一个没有明确正确或错误答案的问题,我认为在7分钟后接受答案是一个坏主意。 – 2009-11-23 13:19:04

+0

好点。注意:) – Svish 2009-11-23 13:26:26

回答

0

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

  • 什么是继承的目的是什么?
  • 有问题的测试目的是什么?

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

但是,这意味着从设计的角度来看,MoreMath从SomeMath派生出来是一个重要的设计方面。如果以多态的方式使用SomeMath,情况肯定会如此。但是,如果您只是使用继承来重用(不建议,但是),情况并非如此。

在后一种情况下(继承用于重用),父类和子类之间没有概念上的联系,您可能会试图在将来打破继承。在这种情况下,通过一项验证父母是否正确的测试是一个不好的保障。

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

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

+0

作出这个答案,因为它是涵盖更多的角度:) – Svish 2009-11-23 14:52:09

5

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

+1

听起来像一个很好的做法:) – Svish 2009-11-23 13:11:09

+0

我认为这个答案假设单元测试只写在TDD时尚。在我看来,这只是答案的一半,但请参阅我的答案进行更全面的检查。 – 2009-11-23 13:16:54

1

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

0

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

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]属性,但有[测试]属性的所有测试方法。 TestCalculator类是[TestFixture],但它继承了基类中的所有测试,它使子类仅负责提供对象以测试该接口。

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

+0

我实际上做了一些类似的事情我自己一次:) – Svish 2009-11-23 14:51:28