我知道所谓的单元测试和集成测试的教科书定义。我好奇的是什么时候该写单元测试了……我将写它们来覆盖尽可能多的类集。

例如,如果我有一个Word类,我将为Word类编写一些单元测试。然后,我开始编写我的Sentence类,当它需要与Word类交互时,我经常会编写单元测试,这样它们就可以同时测试Sentence和Word……至少在他们相互作用的地方。

这些测试本质上变成集成测试了吗?因为它们现在测试这两个类的集成,还是仅仅是一个跨越两个类的单元测试?

一般来说,由于这条不确定的界线,我很少实际编写集成测试……或者我是否使用成品来查看所有部件是否正常工作,即实际的集成测试,即使它们是手动的,并且很少在每个单独的功能范围之外重复?

我是否误解了集成测试,或者集成测试和单元测试之间真的只有很小的区别?


当前回答

单元测试是一种测试方法,用于验证源代码的各个单元是否正常工作。

集成测试是软件测试的一个阶段,在这个阶段中,各个软件模块被组合起来并作为一个组进行测试。

Wikipedia将单元定义为应用程序中最小的可测试部分,在Java/ c#中是一个方法。但是在你的单词和句子类的例子中,我可能只会为句子写测试,因为我可能会发现使用一个模拟单词类来测试句子类是多余的。所以句子是我的单位,单词是这个单位的实现细节。

其他回答

单元测试是针对工作单元或代码块进行测试。通常由单个开发人员执行。

集成测试是指当开发人员将其代码提交到源代码控制存储库时,最好在集成服务器上执行的测试。集成测试可以由Cruise Control等实用程序执行。

因此,您可以执行单元测试来验证您构建的工作单元是否正常工作,然后集成测试验证您添加到存储库中的任何内容都没有破坏其他内容。

单元测试使用模拟

您正在讨论的是集成测试,它实际上测试了系统的整个集成。但是当你做单元测试时,你应该分别测试每个单元。其他一切都应该被嘲笑。在你的Sentence类的例子中,如果它使用了Word类,那么你的Word类应该被嘲笑。这样,您将只测试您的Sentence类功能。

当我编写单元测试时,我通过模拟依赖项将被测试代码的范围限制在我当前正在编写的类中。如果我正在编写一个Sentence类,而Sentence依赖于Word,那么我将使用一个模拟Word。通过模拟Word,我可以只关注它的界面,并在与Word界面交互时测试我的Sentence类的各种行为。这样我只测试了Sentence的行为和实现,而没有同时测试Word的实现。

一旦我编写了单元测试,以确保Sentence在基于Word的界面与Word交互时行为正确,然后我编写集成测试,以确保我对交互的假设是正确的。为此,我提供了实际的对象,并编写了一个测试,练习一个最终将同时使用Sentence和Word的功能。

你测试的性质

模块X的单元测试是仅在模块X中预期(并检查)问题的测试。

许多模块的集成测试是一种预期由模块之间的合作产生的问题的测试,这样单独使用单元测试就很难发现这些问题。

从以下方面考虑测试的性质:

Risk reduction: That's what tests are for. Only a combination of unit tests and integration tests can give you full risk reduction, because on the one hand unit tests can inherently not test the proper interaction between modules and on the other hand integration tests can exercise the functionality of a non-trivial module only to a small degree. Test writing effort: Integration tests can save effort because you may then not need to write stubs/fakes/mocks. But unit tests can save effort, too, when implementing (and maintaining!) those stubs/fakes/mocks happens to be easier than configuring the test setup without them. Test execution delay: Integration tests involving heavyweight operations (such as access to external systems like DBs or remote servers) tend to be slow(er). This means unit tests can be executed far more frequently, which reduces debugging effort if anything fails, because you have a better idea what you have changed in the meantime. This becomes particularly important if you use test-driven development (TDD). Debugging effort: If an integration test fails, but none of the unit tests does, this can be very inconvenient, because there is so much code involved that may contain the problem. This is not a big problem if you have previously changed only a few lines -- but as integration tests run slowly, you perhaps did not run them in such short intervals...

记住,集成测试仍然可能存根/伪造/模拟它的一些依赖项。 这在单元测试和系统测试(最全面的集成测试,测试所有系统)之间提供了大量的中间地带。

务实的方法使用两者

因此,一种务实的方法是:尽可能灵活地依赖集成测试,在风险太大或不方便的地方使用单元测试。 这种思维方式可能比单元测试和集成测试的一些教条主义区分更有用。

在我看来,答案是“为什么这很重要?”

是不是因为您只做单元测试而不做集成测试?反之亦然?当然不是,你应该尝试两者兼得。

是因为单元测试需要快速、隔离、可重复、自我验证和及时,而集成测试不需要吗?当然不是,所有的测试都应该是这样。

这是因为您在单元测试中使用模拟,而在集成测试中不使用它们吗?当然不是。这意味着,如果我有一个有用的集成测试,我不允许为某些部分添加模拟,担心我将不得不将我的测试重命名为“单元测试”或将其交给另一个程序员来处理。

是不是因为单元测试测试一个单元,而集成测试测试许多单元?当然不是。这有什么实际意义呢?关于测试范围的理论讨论在实践中无论如何都是行不通的,因为术语“单元”完全依赖于上下文。在类级别上,单元可能是一个方法。在程序集级别上,单元可能是类,而在服务级别上,单元可能是组件。 甚至类也会使用其他类,那么哪个是单位呢?

这无关紧要。

测试很重要,F.I.R.S.T也很重要,对定义吹毛求疵是浪费时间,只会让测试新手感到困惑。