简体   繁体   English

儿童班的单元测试

[英]Unit testing of child classes

Say we have this hyper simple class hierchy: 假设我们有这个超级简单的类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;
    }
}

Should I write tests for the Add method when I write tests for the MoreMath class? 在为MoreMath类编写测试时,我应该为Add方法编写测试吗? Or should I only care about that method when I am testing the SomeMath class? 或者我在测试SomeMath类时是否应该只关心该方法? More generally: Should I test all methods of a class, or should I only test "new" methods? 更一般地说:我应该测试一个类的所有方法,还是应该只测试“新”方法?

I can kind of come up with some reasons for both sides. 我可以想出双方的一些理由。 For example, when testing all methods you will end up testing the same thing more than once, which is not so good and can become tedious. 例如,在测试所有方法时,您最终会不止一次地测试相同的东西,这不是很好,而且可能变得乏味。 But if you don't test all methods, a change in SomeMath might break usages of MoreMath ? 但是如果你不测试所有方法, SomeMath的更改可能会破坏MoreMath用法? Which would kind of be a bad thing too. 这也是一件坏事。 I suppose it probably depends a bit on the case too. 我想这也可能取决于案例。 Like if it extends a class I have control over or not. 就像它扩展了一个类,我可以控制或不控制。 But anyways, I'm a total test newbie, so I am curious to know what people smarter than I think about this :-) 但无论如何,我是一个全新的测试新手,所以我很想知道人们比我想的更聪明:-)

Normally I wouldn't test behavior in the child class unless the child class changes the behavior from that expected in the parent class. 通常我不会测试子类中的行为,除非子类改变了父类中预期的行为。 If it is using the same behavior, there is no need to test it. 如果它使用相同的行为,则无需进行测试。 If you are planning to make breaking changes to the parent class but the child class still needs the old behavior, then I would first create tests in the child class to define its required behavior, then I would make the changes in the parent tests and subsequent code changes. 如果您计划对父类进行重大更改但子类仍然需要旧行为,那么我将首先在子类中创建测试以定义其所需行为,然后我将在父测试中进行更改代码更改。 This follows the YAGNI principle -- you aren't going to need it -- and delays the implementation of the child tests until they actually have a purpose. 这遵循YAGNI原则 - 您不需要它 - 并延迟子测试的实施,直到它们实际有目的。

I'd only test the Add method of the SomeMath class. 我只测试SomeMath类的Add方法。 If MoreMath only inherits it and does absolutely nothing new, then writing tests for both would be pure duplicate code, nothing more. 如果MoreMath只继承它并且绝对没有什么新东西,那么为两者编写测试将是纯粹的重复代码,仅此而已。 It's always good to be a little pragmatic with these things imho. 用这些东西做一点务实总是好的。

At my current place we came across a similar problem where we wanted to develop to interfaces but ensure that each implementation of the interface behaved correctly (eg different data layers should behave the same regardless of the implementation of that layer). 在我目前的地方,我们遇到了一个类似的问题,我们希望开发接口,但要确保接口的每个实现都能正常运行(例如,不管该层的实现如何,不同的数据层应该表现相同)。 The way we solved it (with NUnit) was to have an inheritance structure within the unit tests: 我们解决它的方式(使用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(); }
}

We would not have the [TestFixture] attribute on the base interface test but have the [Test] attribute on all of the test methods. 我们在基本接口测试中没有[TestFixture]属性,但在所有测试方法上都有[Test]属性。 The TestCalculator class is the [TestFixture] but inherits all of the tests from the base class which leaves the sub-class responsible solely for providing the object to test for that interface. TestCalculator类是[TestFixture],但是继承了基类的所有测试,这使得子类只负责提供测试该接口的对象。

I would adopt a similar pattern for your case, so the tests are run against all of the classes but are only written once. 我会为你的情况采用类似的模式,所以测试是针对所有类运行的,但只写一次。

First of all, I think that there are at least two factors that may influence your decision to do one or the other: 首先,我认为至少有两个因素可能会影响您做出其中一个的决定:

  • What is the purpose of the inheritance? 继承的目的是什么?
  • What is the purpose of the test in question? 有问题的测试的目的是什么?

In TDD scenarios, I would tend to write a single test case for MoreMath that verifies that it derives from SomeMath, and then consider all of the members inherited from SomeMath to be covered. 在TDD场景中,我倾向于为MoreMath编写一个测试用例来验证它是从SomeMath派生的,然后考虑从SomeMath继承的所有成员都被覆盖。

However, that implies that, from a design perspective, it is an important design aspect that MoreMath derives from SomeMath. 但是,这意味着,从设计角度来看,MoreMath派生自SomeMath是一个重要的设计方面。 This would definitely be the case if you use SomeMath in a polymorphic way. 如果您以多态方式使用SomeMath,肯定会出现这种情况。 However, it wouldn't be the case if you simply use inheritetance for reuse (not recommended, though). 但是,如果您只是使用继承来重用(不推荐),则情况并非如此。

In the latter case (inheritance is used for reuse), there is no conceptual connection between the parent and child classes, and you may be tempted to break the inheritance in the future. 在后一种情况下(继承用于重用),父类和子类之间没有概念连接,并且您可能想要在将来中断继承。 In such cases, having a test that verifies that the parent is correct is a poor safeguard. 在这种情况下,进行测试以验证父母是否正确是一个很差的保障措施。

From a Quality Assurance (QA) standpoint, each and every member of each and every class should be tested rigorously. 从质量保证(QA)的角度来看,每个班级的每个成员都应该经过严格的测试。 This means that you should repeat the test code for each child class even if the test code would be the same, because you need to verify that no virtual method was overridden in an unexpected way. 这意味着即使测试代码相同,您也应该为每个子类重复测试代码,因为您需要验证没有以意外方式覆盖虚拟方法。 However, to stay DRY you can write them as Parameterized Tests, or perhaps use a tool such as Pex . 但是,要保持DRY,您可以将它们编写为参数化测试,或者使用Pex等工具。

Personally, I rarely get to the QA phase. 就个人而言,我很少进入质量保证阶段。 Usually, the test suite created during TDD is an adequate safety net... but that all depends on the type of software you build. 通常,在TDD期间创建的测试套件是一个足够的安全网......但这一切都取决于您构建的软件类型。

I would make my test class for MoreMath inherit from the test class for SomeMath , thus inheriting all of the tests of that class. 我会让我的测试类MoreMath从测试类继承SomeMath ,从而继承所有类的测试。 This means I only have to write additional tests for the new features, but all the subclass' features are fully tested. 这意味着我只需要为新功能编写额外的测试,但所有子类的功能都经过了全面测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM