代码之家  ›  专栏  ›  技术社区  ›  Mike Stone

集成测试和单元测试有什么区别?

  •  269
  • Mike Stone  · 技术社区  · 16 年前

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

    例如,如果我有一个 Word 类,我将为 班级。然后,我开始写我的 Sentence 类,以及需要与 类,我经常编写单元测试,这样它们就可以同时测试这两个测试 判决 ... 至少在他们互动的地方。

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

    我是误解了集成测试,还是集成测试和单元测试之间的差别真的很小?

    19 回复  |  直到 5 年前
        1
  •  308
  •   Phil Reif    7 年前

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

    相反,a 单元测试

    假设你有这样一种方法:

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

    DoSomething 对您的客户来说非常重要:这是一个功能,是唯一重要的。这就是为什么您通常编写一个黄瓜规范来断言它:您希望 验证 沟通

    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
    

    毫无疑问:如果测试通过,您可以断言您正在交付一个工作特性。这叫什么 商业价值

    如果要为编写单元测试 剂量

    在实践中,你会做一些类似的事情:

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

    您可以通过依赖注入、工厂方法或任何模拟框架或者仅仅扩展测试中的类来实现这一点。

    假设里面有个窃听器 Log.DoSomething()

    这个功能不起作用,因为 Log 坏了,不是因为 [Do your job with someInput] 没有尽到自己的职责。顺便说一下, [用someInput做你的工作] 是该方法的唯一责任。

    日志 在100个其他特性中使用,在100个其他类的100个其他方法中使用。

    是的,100个功能都会失败。但是,幸运的是,100个端到端测试也失败了,暴露了问题所在。而且,是的: 他们说的是实话 .

    但是, 的单元测试是绿色的,因为它使用的是假的 日志 显然是在撒谎

    (如果 DoSomething() [用someInput做你的工作] 有一些错误。)

    假设这是一个类中断的系统: A system with a broken class

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

    A single bug will break several features, and several integration tests will fail

    另一方面,同一个bug只会破坏一个单元测试。

    The same bug will break just one unit test

    现在,比较两种情况。

    同一个bug只会破坏一个单元测试。

    • 你所有的特征都是用坏的 是红色的
    • 所有的单元测试都是绿色的,只有 日志

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

    区别

    集成测试表明 不起作用。但它们在 问题可能是。

    正是这个错误。要绘制这些信息,它们必须在模拟环境中运行该方法,在该环境中,所有其他依赖项都应该正常工作。

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

    这个回复基本上是我在这里写的总结: Unit tests lie, that's why I love them

        2
  •  62
  •   ShellZero    7 年前

    当我编写单元测试时,我通过模拟依赖关系将被测试代码的范围限制到我当前正在编写的类。如果我正在写一个句子类,并且句子依赖于单词,我将使用一个模拟单词。通过模仿Word,我可以只关注它的接口,并测试我的句子类与Word接口交互时的各种行为。这样我只测试句子的行为和实现,而不是同时测试单词的实现。

        3
  •  43
  •   Rob Cooper    16 年前

    我总是被告知 单元测试 单元 HtmlSanitizer 也许)。

    集成测试 怎样 组件单独工作,但是当您将html输入到 ,它神奇地知道它是否有效。。

        4
  •  23
  •   Robert Koritnik    13 年前

    单元测试使用模拟

    你所说的是集成测试,实际上是测试系统的整个集成。但是当你做单元测试的时候,你应该单独测试每个单元。其他一切都应该被嘲笑。所以在你的情况下 Sentence 类,如果它使用 Word 同学们,然后你们的 类应该被嘲笑。这样,你只需要测试 判决 类功能。

        5
  •  17
  •   Jon Limjap    16 年前

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

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

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

        6
  •  13
  •   Michu93    5 年前

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

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

    enter image description here

    当您只使用单元测试时会发生这种情况(通常两个窗口都在工作,不幸的是不能一起工作):

    enter image description here

    source1 source2

        7
  •  12
  •   DevelopingChris    16 年前

    采用单一责任设计,其黑白相间。不止一个责任,这是一个集成测试。

    通过duck测试(looks、quacks、waddles,它是一个duck),它只是一个包含多个新对象的单元测试。

    当您进入mvc并对其进行测试时,控制器测试总是集成的,因为控制器同时包含模型单元和视图单元。在那个模型中测试逻辑,我称之为单元测试。

        8
  •  10
  •   Lutz Prechelt    6 年前

    测试的性质

    A

    集成测试 在许多模块中,都有一个测试,它期望在合作中出现问题 这些模块使得单独使用单元测试很难发现这些问题。

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

    • :这就是测试的目的。只有一个 单元测试和集成测试的结合 可以给你充分的风险降低,因为一方面,单元测试本质上不能测试模块之间的正确交互,另一方面集成测试只能在很小的程度上行使非平凡模块的功能。
    • 测试写作努力 :集成测试可以节省工作量,因为这样您就不需要编写存根/伪造/模拟。但是在实现(和维护)时,单元测试也可以节省精力这些存根/赝品/模拟比配置没有它们的测试设置更容易。
    • 测试执行延迟
    • 调试工作 控制住问题。如果您以前只更改了几行代码,这并不是一个大问题,但是由于集成测试运行缓慢,您可能已经更改了 在这么短的时间内运行它们。。。

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

    两者兼用的语用方法

    因此,一种实用的方法是:尽可能灵活地依赖集成测试,并在风险太大或不方便的地方使用单元测试。

        9
  •  8
  •   Captain Man Joachim Sauer    9 年前

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

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

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

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

    测试是重要的,F.I.R.S.T是重要的,对定义的吹毛求疵是浪费时间,只会让新手混淆测试。

        10
  •  4
  •   Lance Fisher    16 年前

    如果class1的单元测试是测试class1的特性,class2的单元测试是测试class1的特性,并且它们没有访问数据库,我想我还是会把几个交互类称为单元测试。

    当一个测试在我的大部分堆栈中运行,甚至访问数据库时,我称之为集成测试。

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

        11
  •  4
  •   Michael Neale    16 年前

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

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

        12
  •  4
  •   grom    16 年前

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

    Integration Testing 是软件测试的一个阶段,在这个阶段中,单个软件模块作为一个组进行组合和测试。

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

        13
  •  4
  •   CharlesB Craig McQueen    12 年前

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

        14
  •  3
  •   Christian Hagelid    16 年前

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

    因此,您需要进行单元测试以验证所构建的工作单元是否正常工作,然后集成测试将验证您添加到存储库中的任何内容都不会破坏其他内容。

        15
  •  2
  •   CVertex    16 年前

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

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

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

        16
  •  2
  •   BenKoshy Mayinx    6 年前

    类比解释

    上面的例子做得很好,我不必重复了。所以我将着重用例子来帮助你理解。

    集成测试检查是否一切正常。想象一下手表上有一系列齿轮在一起工作。集成测试将是:手表显示的时间是否正确?三天后它还能告诉你正确的时间吗?

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

    单元测试

    这些都是非常特殊的测试类型。他们告诉你某件事是成功还是失败。这种测试的关键在于,它只测试一个特定的东西,同时假设其他东西都正常工作。这是关键点。

    例子: 让我们用一个例子来阐述这一点:

    • 以汽车为例。
    • 集成 汽车测试:例如,汽车是否会前后呼应?如果它做到了这一点,你可以放心地说,汽车是工作从一个整体的观点。这是一个集成测试。如果它坏了,你就不知道它到底坏在哪里:是散热器、变速器、发动机还是化油器?你不知道。可能是任何东西。
    • 对于汽车:发动机在工作吗?这项测试假设车内其他一切都正常。这样,如果这个特定的单元测试失败了:您可以非常确信问题出在引擎中,这样您就可以快速地隔离并修复问题。

    使用存根

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

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

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

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

        17
  •  1
  •   John Smithers    16 年前

    这个问题有点学术性,不是吗?;-) 对我来说,集成测试是对整个部分的测试,而不是十有八九的两个部分在一起。 对于项目,我们有大量的单元测试。

        18
  •  1
  •   Pavel Feldman    16 年前

    我想是的,是的。跨2个类的单元测试变成了集成测试。

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

    真正不同的例子如下 1) 1000000个元素的数组很容易进行单元测试,工作正常。 2) BubbleSort很容易在10个元素的模拟数组上进行单元测试,而且工作正常 3) 集成测试表明有些东西不太好。

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

        19
  •  1
  •   Paulo Merson Basit    11 年前

    此外,重要的是要记住,单元测试和集成测试都可以自动化,并可以使用JUnit编写。 在JUnit集成测试中,可以使用 org.junit.Assume

        20
  •  0
  •   StevePoling    6 年前

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