集成测试和单元测试之间的区别是什么?

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

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

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

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

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

60546 次浏览

使用单一责任设计,它是黑白的。不止一个责任,它是一个集成测试。

通过鸭子测试(看起来,嘎嘎叫,摇摇摆摆,它是一只鸭子),它只是一个包含不止一个新对象的单元测试。

当你进入mvc并测试它时,控制器测试总是集成的,因为控制器包含一个模型单元和一个视图单元。在这个模型中测试逻辑,我称之为单元测试。

我认为,当您开始考虑集成测试时,您更多地是在谈论物理层之间的交叉,而不是逻辑层之间的交叉。

例如,如果您的测试只关心生成内容,那么它就是一个单元测试:如果您的测试只关心写入磁盘,那么它仍然是一个单元测试,但是一旦您同时测试了I/O和文件的内容,那么您就有了一个集成测试。当您在服务中测试函数的输出时,这是单元测试,但一旦您进行服务调用并查看函数结果是否相同,那么这就是集成测试。

从技术上讲,你不能只对一个类进行单元测试。如果您的类是由其他几个类组成的呢?这会自动地使它成为一个集成测试吗?我不这么想。

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

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

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

如果class1的单元测试是测试class1的特性,而class2的单元测试是测试它的特性,并且它们不访问数据库,我想我仍然会把两个相互作用的类称为单元测试。

当一个测试运行了我的堆栈的大部分,甚至到达数据库时,我把它称为集成测试。

我真的很喜欢这个问题,因为TDD讨论有时对我来说有点太纯粹了,看到一些具体的例子对我来说是件好事。

这个问题有点学术,不是吗?: -) 我的观点是: 对我来说,整合测试是测试整个部分,而不是十个部分中的两个部分在一起。 我们的集成测试显示,主构建(包含40个项目)是否会成功。 对于项目,我们有大量的单元测试。 对我来说,关于单元测试最重要的一点是,一个单元测试不能依赖于另一个单元测试。所以对我来说,你上面描述的两个测试都是单元测试,如果它们是独立的。对于集成测试来说,这并不重要

我也这么做——我把它们都称为单元测试,但在某些时候,我有一个“单元测试”,它涵盖了太多内容,我经常把它重命名为“..IntegrationTest”——只是名字改变了,其他的都没有改变。

我认为从“原子测试”(测试一个微小的类或方法)到单元测试(类级别)和集成测试——然后是功能测试(通常从上到下覆盖了更多的东西)——有一个延续——似乎没有一个干净的分界线。

如果您的测试设置了数据,可能还加载了数据库/文件等,那么它可能更像是一个集成测试(我发现集成测试使用更少的模拟和更多的真实类,但这并不意味着您不能模拟出系统的某些部分)。

我的10位:D

我总是被告知单元测试单个组件的测试——应该充分地练习。现在,这往往有很多层次,因为大多数组件是由较小的部件组成的。对我来说,单位是系统的一个功能部分。所以它必须提供一些有价值的东西(即不是字符串解析的方法,而是HtmlSanitizer)。

集成测试是下一个步骤,它采取一个或多个组件,并确保他们一起工作,因为他们应该。这样你就不用担心如何的组件是单独工作的,但是当你在HtmlEditControl中输入html时,它会神奇地知道它是否有效。

这是一条真正可移动的线。我宁愿更专注于让该死的代码完全停止工作^_^

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

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

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

我把那些白盒测试类的测试称为单元测试。类需要的任何依赖项都被替换为伪依赖项(mock)。

集成测试是同时测试多个类及其交互的测试。在这些情况下,只有一些依赖关系是伪造的。

我不会调用Controller的集成测试,除非它们的依赖项之一是真实的(即不是伪造的)(例如IFormsAuthentication)。

分离这两种类型的测试对于在不同级别上测试系统很有用。此外,集成测试往往是长期存在的,而单元测试应该是快速的。执行速度的区别意味着它们的执行方式不同。在我们的开发过程中,单元测试在签入时运行(这很好,因为它们非常快),集成测试每天运行一次/两次。我试着尽可能多地运行集成测试,但通常敲击数据库/写入文件/制作rpc /等等会变慢。

这引出了另一个重要的问题,单元测试应该避免碰到IO(例如磁盘、网络、db)。否则他们会慢很多。设计这些IO依赖需要一些努力——我不能承认我一直忠实于“单元测试必须是快速的”规则,但如果你是,在一个更大的系统上的好处很快就会显现出来。

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

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

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

我认为是和是。跨越2个类的单元测试变成了集成测试。

您可以通过使用mock实现(MockWord类)测试Sentence类来避免这种情况,当系统的这些部分足够大,可以由不同的开发人员实现时,这一点非常重要。在这种情况下,Word是单独的单元测试,句子是在MockWord的帮助下进行单元测试,然后句子是用Word进行集成测试。

实际差异的例子如下 1) 1,000,000个元素的数组很容易进行单元测试,工作良好。 2) BubbleSort很容易在10个元素的模拟数组上进行单元测试,而且工作得很好 3)集成测试显示有些东西不是那么好

如果这些部分是由一个人开发的,在单元测试BubbleSoft时很可能会发现问题,因为开发人员已经有了真实的数组,他不需要模拟实现。

单元测试使用模拟

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

对我来说,关键的区别是集成测试揭示了一个特性是有效的还是坏的,因为它们在接近现实的场景中强调了代码。它们调用一个或多个软件方法或功能,并测试它们是否按预期工作。

相反,单元测试测试单个方法依赖于(通常是错误的)软件其余部分正确工作的假设,因为它显式地模拟每个依赖项。

因此,当实现某个特性的方法的单元测试显示为绿色时,表示该特性正在工作。

假设你有一个这样的方法:

public SomeResults DoSomething(someInput) {
var someResult = [Do your job with someInput];
Log.TrackTheFactYouDidYourJob();
return someResults;
}

DoSomething对你的客户是非常重要的:它是一个功能,唯一重要的事情。这就是为什么你通常要写一个Cucumber规范来断言它:你希望验证沟通特性是否工作。

Feature: To be able to do something
In order to do something
As someone
I want the system to do this thing


Scenario: A sample one
Given this situation
When I do something
Then what I get is what I was expecting for

毫无疑问:如果测试通过了,你就可以断言你交付了一个可以工作的特性。这就是你可以称之为业务价值的东西。

如果你想为DoSomething写一个单元测试,你应该假装(使用一些模拟)其余的类和方法都在工作(也就是说:方法使用的所有依赖项都在正确工作),并断言你的方法在工作。

在实践中,你可以这样做:

public SomeResults DoSomething(someInput) {
var someResult = [Do your job with someInput];
FakeAlwaysWorkingLog.TrackTheFactYouDidYourJob(); // Using a mock Log
return someResults;
}

你可以使用依赖注入,或者一些工厂方法,或者任何模拟框架,或者只是扩展测试中的类。

假设Log.DoSomething()中有一个错误。 幸运的是,Gherkin规范会找到它,并且您的端到端测试将失败

这个特性不能工作,因为Log坏了,而不是因为[Do your job with someInput]没有做它的工作。而且,顺便说一下,[Do your job with someInput]是该方法的唯一责任。

同样,假设Log在100个其他特性中使用,在100个其他类的100个其他方法中使用。

是的,100个功能都会失败。但幸运的是,100个端到端测试也失败了,并揭示了这个问题。并且,是的:他们说的是实话

这是非常有用的信息:我知道我的产品坏了。这也是非常令人困惑的信息:它没有告诉我问题在哪里。它告诉我的是症状,而不是根本原因。

然而,DoSomething的单元测试是绿色的,因为它使用了一个假的Log,被构建为永不崩溃。并且,是的:这显然是在撒谎。它传达了一个坏掉的功能是有效的。它如何有用?

(如果DoSomething()的单元测试失败,请确认:[Do your job with someInput]有一些错误。)

假设这是一个有破碎类的系统: A system with A broken class

.

一个bug就会破坏几个特性,几个集成测试就会失败。

一个bug会破坏几个功能,几个集成测试会失败

另一方面,相同的错误只会破坏一个单元测试。

相同的错误只会破坏一个单元测试

现在,比较这两种情况。

同样的错误只会破坏一个单元测试。

  • 使用坏掉的Log的所有功能都是红色的
  • 所有的单元测试都是绿色的,只有Log的单元测试是红色的

实际上,所有使用坏特性的模块的单元测试都是绿色的,因为通过使用模拟,它们删除了依赖项。换句话说,它们运行在一个理想的、完全虚构的世界里。这是唯一隔离和寻找虫子的方法。单元测试意味着模拟。如果你不嘲讽,你就不是单元测试。

的区别

集成测试告诉什么没有工作。但是它们在猜测,中没有用处,问题可能是。

单元测试是唯一能告诉你在哪里错误所在的测试。要绘制此信息,他们必须在模拟环境中运行该方法,在该环境中所有其他依赖项都应该正确工作。

这就是为什么我认为你的句子“或者它只是一个跨越2个类的单元测试”在某种程度上被取代了。一个单元测试不应该跨越2个类。

这个回复基本上是我在这里写的一个总结:单元测试会说谎,这就是我喜欢它们的原因

集成测试:测试数据库持久性 单元测试:模拟数据库访问。

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

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

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

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

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

这无关紧要。

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

此外,重要的是要记住,单元测试和集成测试都可以使用例如JUnit来自动化和编写。 在JUnit集成测试中,可以使用org.junit.Assume类来测试环境元素的可用性(例如,数据库连接)或其他条件。< / p >

你测试的性质

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

许多模块的集成测试是一个测试,它预期在模块之间的合作中出现的问题,这样单独使用单元测试就很难发现这些问题。

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

  • 减少风险:这就是测试的目的。只有单元测试和集成测试的组合可以完全降低风险,因为一方面,单元测试本质上不能测试模块之间的适当交互,另一方面,集成测试只能在很小的程度上测试一个重要模块的功能。
  • 测试编写工作:集成测试可以节省精力,因为你可能不需要写存根/假/模拟。但是单元测试也可以节省精力,当实现(和维护!)那些存根/假的/模拟的时候,碰巧比没有它们的测试设置更容易配置。
  • 测试执行延迟:涉及重量级操作的集成测试(例如访问外部系统,如db或远程服务器)往往很慢。这意味着可以更频繁地执行单元测试,如果出现任何失败,这将减少调试工作,因为您可以更好地了解您在此期间更改了什么。如果您使用测试驱动开发(TDD),这就变得尤为重要。
  • 调试工作:如果集成测试失败,但没有一个单元测试失败,这可能非常不方便,因为涉及的代码太多,以至于五月包含了问题。如果你之前只改变了几行,这不是一个大问题——但是由于集成测试运行得很慢,你可能在如此短的时间间隔内运行它们……
记住,集成测试仍然可能stub/fake/mock掉一些的依赖项。 这在单元测试和系统测试(最全面的集成测试,测试所有系统)之间提供了大量的中间地带

务实的方法使用两者

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

类比的简单解释

上面的例子已经很好了,我就不再重复了。所以我会用例子来帮助你们理解。

集成测试

集成测试检查是否一切都在一起工作。想象一下手表上的一系列齿轮一起工作。一个综合测试是:手表是否显示正确的时间?它还能在3天内显示正确的时间吗?

它只告诉你整个部件是否正常工作。如果它失败了:它不会告诉你它到底在哪里失败。

单元测试

这些都是非常具体的测试类型。它们告诉你一件具体的事情是有效还是失败。这种类型的测试的关键是,它只测试一个特定的东西,同时假设其他一切都正常工作。这是关键。

< em >的例子: 让我们用一个例子来详细说明这一点:

  • 让我们以汽车为例。
  • 集成测试一辆车:例如,汽车是否开车到Woop Woop和回来?如果它做到了这一点,你就可以放心地说,从整体角度来看,汽车是在工作。这是一个整合测试。如果它坏了,你不知道它到底哪里坏了:是散热器、变速器、发动机还是化油器?你根本不知道。它可以是任何东西。
  • 单元测试 for a car:是发动机工作吗?这个测试假设汽车的其他部件都正常工作。这样,如果这个特定的单元测试失败了:您可以非常确信问题出在引擎上——这样您就可以快速地隔离并修复问题。

使用存根

  • 假设您的汽车集成测试失败。它不能成功地开到埃丘卡。问题出在哪里?

  • 现在让我们假设你的发动机使用一个特殊的燃油喷射系统,这个发动机单元测试也失败了。换句话说,集成测试和发动机单元测试都失败了。那么问题在哪里呢?(给自己10秒钟的时间来回答。)

  • 是发动机出了问题,还是燃油喷射系统出了问题?

你看到问题了吗?你不知道什么是失败。如果你使用不同的外部依赖关系,那么这10个依赖关系中的每一个都可能导致问题——你不知道从哪里开始。这就是为什么单元测试使用存根来假设其他一切都正常工作。

如果你是TDD的纯粹主义者,你会在写产品代码之前先写测试。当然,测试不能编译,所以您首先编译测试,然后让测试通过。

您可以在单元测试中这样做,但不能在集成测试或验收测试中这样做。如果您尝试进行集成测试,在您完成之前,什么都不会编译!

在单元测试中,您将测试每个分离的部分: enter image description here < / p >

在集成测试中,您将测试系统的许多模块:

enter image description here

这是当你只使用单元测试时发生的事情(通常两个Windows都可以工作,不幸的是不能一起工作):

enter image description here

< p >来源: source1 source2 < / p >

我在采访中经常被问到这个问题。到目前为止,我一直在吹嘘我的专业知识,并对组件测试和验收测试侃侃而谈。

多年来,我理解只有集成和单元测试。作为一个单独的开发人员,我可以编写单元测试来磨练我的技能,但我并不总是这么做。

单元测试

这是一个关键的区别。单元测试易于实现和执行,理想情况下不需要依赖。这就是嘲笑的意义所在。模拟一切通常更容易,特别是当你获得你所编写的其他函数的覆盖时。也许更简单,但这不是单元测试的思想。

我要重申,单元测试意味着易于运行且规模小。它们的失败可以让我们立即了解bug是在哪里引入的。

下面是测试的等级,从底部的廉价和丰富到顶部的缓慢、昂贵和很少:

测试费用和广度的层次结构。 < / >

还有几个层可以概念化,但为了清晰起见,省略了这些层。

集成测试

通过集成测试,您可以考虑引入严重的外部依赖项,例如虚拟机、虚拟网络和设备。可能您可以使用实际的调制解调器、路由器和防火墙,只要这些花费是合理的。

这些不会在本地运行,而是在构建服务器上运行。本地Jenkins和基于云的CI提供商的组合满足了这一需求。

其他测试术语

这是我在工业界工作了几年的体会。我们可以讨论组件测试,并得到一个定义,但如果这个定义没有被广泛使用,那么它就失去了价值。

验收测试就是我们所说的业务单元或客户需求。这些将引导一切的方向,并位于金字塔的顶端(想象一个美元符号)。

端到端测试是集成测试的同义词,但我在网上注意到它被放在上面。我想它可能与验收测试和集成测试更相关,后者往往更详细,涉众的兴趣更少(尽管部门内部有巨大的兴趣)。