子类的单元测试

发布于 2024-08-12 00:17:43 字数 610 浏览 4 评论 0原文

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

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 的使用?这也是一件坏事。我想这也可能取决于具体情况。就像它是否扩展了我是否可以控制的类一样。但无论如何,我是一个完全的测试新手,所以我很想知道哪些人比我认为的更聪明:-)

Say we have this hyper simple class 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? Or should I only care about that method when I am testing the SomeMath class? 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? 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 :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

鯉魚旗 2024-08-19 00:17:44

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

I'd only test the Add method of the SomeMath class. If MoreMath only inherits it and does absolutely nothing new, then writing tests for both would be pure duplicate code, nothing more. It's always good to be a little pragmatic with these things imho.

一向肩并 2024-08-19 00:17:44

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

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

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

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

在后一种情况下(继承是为了重用),父类和子类之间没有概念上的联系,你将来可能会想要破坏继承。在这种情况下,进行测试来验证父母的正确性是一个很差的保障措施。

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

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

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.

However, that implies that, from a design perspective, it is an important design aspect that MoreMath derives from SomeMath. This would definitely be the case if you use SomeMath in a polymorphic way. 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. 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.

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.

醉酒的小男人 2024-08-19 00:17:44

在我现在的地方,我们遇到了一个类似的问题,我们想要开发接口,但要确保接口的每个实现都行为正确(例如,无论该层的实现如何,不同的数据层都应该表现相同)。我们解决这个问题的方法(使用 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],但从基类继承了所有测试,这使得子类仅负责提供用于测试该接口的对象。

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

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 (e.g. 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:

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. 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.

I would adopt a similar pattern for your case, so the tests are run against all of the classes but are only written once.

抱着落日 2024-08-19 00:17:44

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

I would make my test class for MoreMath inherit from the test class for SomeMath, thus inheriting all of the tests of that class. This means I only have to write additional tests for the new features, but all the subclass' features are fully tested.

走过海棠暮 2024-08-19 00:17:43

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文