【问题标题】:Should one test internal implementation, or only test public behaviour?应该测试内部实现,还是只测试公共行为?
【发布时间】:2010-10-25 18:16:34
【问题描述】:

给定软件...

  • 系统由几个子系统组成
  • 每个子系统都由几个组件组成
  • 每个组件都使用许多类实现

...我喜欢为每个子系统或组件编写自动化测试。

我不会为组件的每个内部类编写测试(除非每个类都有助于组件的公共功能,因此可以通过组件的公共 API 从外部进行测试/测试)。

当我重构组件的实现时(我经常这样做,作为添加新功能的一部分),因此我不需要更改任何现有的自动化测试:因为测试仅依赖于组件的公共 API,并且公共 API 通常是在扩展而不是更改。

我认为此政策与 Refactoring Test Code 之类的文件形成鲜明对比,其中包含...

  • “...单元测试...”
  • "...系统中每个类的测试类..."
  • “...测试代码/生产代码比率...理想情况下被认为接近 1:1 ...”

...我认为我不同意所有这些(或至少不实践)。

我的问题是,如果您不同意我的政策,您能解释一下原因吗?这种程度的测试在什么场景下不够用?

总结:

  • 公共接口经过测试(并重新测试),很少更改(添加但很少更改)
  • 内部 API 隐藏在公共 API 之后,无需重写测试公共 API 的测试用例即可更改

脚注:我的一些“测试用例”实际上是作为数据实现的。例如,UI 的测试用例由包含各种用户输入和相应的预期系统输出的数据文件组成。测试系统意味着测试代码读取每个数据文件,将输入重放到系统中,并断言它得到了相应的预期输出。

虽然我很少需要更改测试代码(因为通常会添加而不是更改公共 API),但我确实发现有时(例如每周两次)需要更改一些现有的数据文件。当我更好地更改系统输出(即新功能改进现有输出)时,可能会发生这种情况,这可能会导致现有测试“失败”(因为测试代码仅尝试断言输出未更改)。为了处理这些情况,我执行以下操作:

  • 重新运行自动化测试套件,它有一个特殊的运行时标志,告诉它不要断言输出,而是将新输出捕获到新目录中
  • 使用可视化差异工具查看哪些输出数据文件(即哪些测试用例)已更改,并验证这些更改是否良好且符合新功能的预期
  • 通过将新输出文件从新目录复制到运行测试用例的目录来更新现有测试(覆盖旧测试)

脚注:“组件”是指“一个 DLL”或“一个程序集”之类的东西……大到足以在系统的体系结构或部署图上可见的东西,通常使用几十个或 100 个来实现类,以及仅由大约 1 个或少数几个接口组成的公共 API ......可能分配给一个开发人员团队的东西(其中不同的组件被分配给不同的团队),因此将根据Conway's Law 拥有相对稳定的公共 API。


脚注:文章 Object-Oriented Testing: Myth and Reality 说,

误区:黑盒测试就足够了。 如果你认真做好测试用例 使用类接口设计或 规范,你可以放心 课程得到充分锻炼。 白盒测试(查看 设计方法的实现 测试)违反了非常概念 封装。

现实:OO 结构很重要,部分 II. 许多研究表明, 黑盒测试套件被认为是 开发人员非常彻底 只锻炼三分之一到一半 的语句(更不用说路径或 州)在实施下 测试。有三个原因 这。首先,输入或状态 选择 通常 锻炼 正常 路径,但不要强迫所有可能的 路径/状态。二、黑匣子 单靠测试无法揭示惊喜。 假设我们已经测试了所有 系统的指定行为 正在测试中。有信心有 没有我们需要的未指定行为 知道系统的任何部分是否有 未被黑匣子行使 测试套件。这是唯一的办法 可以通过代码获取信息 仪器仪表。三、经常 难以行使例外和 未经检查的错误处理 源代码。

我应该补充一点,我正在进行白盒功能测试:我查看代码(在实现中)并编写功能测试(驱动公共 API)来测试各种代码分支(功能实现的详细信息)。

【问题讨论】:

  • 这开始看起来像 stackoverflow.com/questions/182325/… 的副本——请查看该问题是否解决了您的问题。
  • @darch 如果不是重复的话,它肯定很接近;感谢您指出。该主题中接受的答案是单元测试的一个好处是它们是可重复/自动化的:在我的例子中,我已经自动化了我的功能测试,以便它们是可重复的。

标签: unit-testing refactoring automated-tests integration-testing code-coverage


【解决方案1】:

答案很简单:您在描述功能测试,这是软件 QA 的重要组成部分。测试内部实现是单元测试,它是软件 QA 的另一部分,具有不同的目标。这就是为什么你觉得人们不同意你的方法。

功能测试对于验证系统或子系统是否完成了它应该做的事情很重要。客户看到的任何东西都应该以这种方式进行测试。

单元测试用于检查您刚刚编写的 10 行代码是否符合预期。它让您对自己的代码更有信心。

两者是互补的。如果您在现有系统上工作,那么功能测试可能是首先要做的事情。但是一旦你添加代码,单元测试也是一个好主意。

【讨论】:

  • 当我实现一个新功能时,我会通过功能测试来练习它(即新功能的实现)。为什么/何时可能是“单元测试的好主意”?功能测试还不够吗?单元测试是不是有点浪费时间(例如,因为如果重构实现则需要重新设计)?我很少会编写单元测试:有一次我需要练习一个包含系统日期的类(通过等待真实系统日期更改来进行真正的功能测试并不方便)。另外,如果我是开发两个组件的人,...
  • ... 那么我将倾向于一起测试两者(即“集成测试”):而不是创建它们中的任何一个的“模拟”,让我自己测试另一个。
  • 单元测试允许更准确地发现错误的来源。不,这不是浪费时间,因为有很多东西无法通过功能测试正确测试,但仍然值得测试。通常,“难以模拟”的错误对于单元测试非常有用。我说的是所有那些返回 NULL 而不是有效指针的函数、网络连接丢失、配置文件不可读的情况……是的,你必须与你的代码一起重构它们。
【解决方案2】:

我的做法是通过公共 API/UI 测试内部。如果某些内部代码无法从外部访问,那么我会重构以删除它。

【讨论】:

  • 您是否使用代码覆盖工具来发现无法从外部访问或无法访问的内部代码?我想知道这样的代码是如何产生的。
  • 有时会发生这种情况,以异常处理块为例。出于这个原因,他们中的大多数有时会未经测试。
  • @ChrisW:根据我想花多少精力,我使用调试跟踪或 gcov(集成到 Xcode)。关于该代码是如何产生的,使用 TDD 确实有助于我不编写它。但有时功能会被删除或修改。 @Vinegar:通常我尝试测试异常处理块,至少使用我只运行一次的手动测试用例。如果我无法想象到达该代码的情况,我倾向于将其删除。
  • 我遵循同样的策略,并使用代码覆盖工具(不同语言的不同工具)来确保我已经触及了大多数分支。我还使用版本控制 (git) 和错误跟踪 (Jira) 系统来找出我很难达到的分支的目的。
【解决方案3】:

我面前没有我的 Lakos 副本,所以我不会引用,我只是指出他在解释为什么测试在各个层面都很重要方面做得比我做得更好。

只测试“公共行为”的问题是这样的测试给你的信息很少。它会捕获许多错误(就像编译器会捕获许多错误一样),但无法告诉您错误在哪里。执行不佳的单元通常会长时间返回良好的值,然后在条件发生变化时停止这样做;如果该单元已被直接测试,那么它实施不当的事实会很快显现出来。

测试粒度的最佳级别是单元级别。通过其接口为每个单元提供测试。这允许您验证和记录您对每个组件行为方式的信念,这反过来又允许您通过仅测试它引入的新功能来测试依赖代码,这反过来又使测试保持简短和目标。作为奖励,它使用他们正在测试的代码进行测试。

换种说法,只测试公共行为是正确的,只要您注意到每个公开可见的类都有公共行为。

【讨论】:

  • 您说的很对:我已将“组件”的定义作为脚注添加到 OP。 Lakos 对“组件”的定义是“一个源文件”,比我使用的要小得多。我所说的“组件”可能是 Lakos 所说的“包”。
  • 您说“仅测试公共行为......会捕获许多错误(就像编译器会捕获许多错误一样),但无法告诉您错误在哪里。”两个 cmets: 1) 任何错误通常都与我目前正在编辑的任何内容相关联并且尚未签入(考虑到我经常签入,这大大缩小了范围)。 2)单元测试(每个类)不一定有帮助,因为错误通常不在一个类中,而是在类之间的交互中。
  • 为此我们进行了基于交互的测试。你不知道吗:)。看一下这个。 woodwardweb.com/programming/state_based_tes.html
  • 致 ChrisW:关于你的第 2 点 -> 这正是你想要单元测试的原因。如果 A 类和 B 类的测试运行良好,但使用 A 和 B 的功能测试失败,你就知道这是一个交互问题。否则,您必须调查所有三种可能性(A 有错误,B 有错误,A+B 不能互相配合)
【解决方案4】:

到目前为止,这个问题已经得到了很多很好的回答,但我想添加一些我自己的注释。作为前言:我是一家大公司的顾问,该公司为广泛的大客户提供技术解决方案。我这样说是因为根据我的经验,我们需要比大多数软件商店进行更彻底的测试(也许 API 开发人员除外)。以下是我们为确保质量而采取的一些步骤:

  • 内部单元测试:
    开发人员应该为他们编写的所有代码创建单元测试(阅读:每个方法)。单元测试应涵盖积极的测试条件(我的方法是否有效?)和消极的测试条件(当我需要的参数之一为 null 时,该方法是否抛出 ArgumentNullException?)。我们通常使用 CruiseControl.net 之类的工具将这些测试整合到构建过程中
  • 系统测试/组装测试:
    有时这一步被称为不同的东西,但这是我们开始测试公共功能的时候。一旦您知道所有单独的单元都按预期运行,您就想知道您的外部功能也按您认为的方式工作。这是一种功能验证形式,因为目标是确定整个系统是否按应有的方式工作。请注意,这不包括任何集成点。对于系统测试,您应该使用模拟界面而不是真实界面,以便您可以控制输出并围绕它构建测试用例。
  • 系统集成测试:
    在流程的这个阶段,您希望将集成点连接到系统。例如,如果您正在使用信用卡处理系统,您将希望在此阶段合并实时系统以验证它是否仍然有效。您可能希望执行与系统/组装测试类似的测试。
  • 功能验证测试:
    功能验证是用户通过系统运行或使用 API 来验证其是否按预期工作。如果您已经构建了一个发票系统,那么您将在这个阶段从头到尾执行您的测试脚本,以确保一切都按照您的设计工作。这显然是流程中的一个关键阶段,因为它会告诉您您是否已完成工作。
  • 认证测试:
    在这里,您将真实用户放在系统前面,让他们试一试。理想情况下,您已经在某个时候与您的利益相关者测试过您的用户界面,但这个阶段将告诉您您的目标受众是否喜欢您的产品。您可能听说过其他供应商将其称为“候选版本”。如果在这个阶段一切顺利,你就知道你可以投入生产了。认证测试应始终在您将用于生产的环境(或至少相同的环境)中执行。

当然,我知道不是每个人都遵循这个过程,但是如果你从头到尾看,你可以开始看到单个组件的好处。我没有包括诸如构建验证测试之类的东西,因为它们发生在不同的时间线(例如,每天)。我个人认为单元测试至关重要,因为它们可以让您深入了解应用程序的哪个特定组件在哪个特定用例中失败。单元测试还将帮助您隔离哪些方法正常运行,这样您就不必在它们没有任何问题时花时间查看它们以获取有关失败的更多信息。

当然,单元测试也可能是错误的,但是如果您根据功能/技术规范开发测试用例(您有一个,对吗?;)),您应该不会有太多麻烦。

【讨论】:

  • 我想我会将这些步骤命名为“单元测试”(一个单元)、“组件测试”(每个较大的组件)、“集成测试”(几个组件)、“系统测试”(整个系统)和“验收测试”(由客户和/或最终用户)。
  • ChrisW,当然可以随意命名它们;我提供的名称是我们在公司使用的名称。我见过互换组装/系统测试,但是是的。归根结底,对我们来说重要的是概念和执行。
  • 也许单元测试并不一定能提高软件的整体最终质量:而是单元测试提供更早测试的主要原因(即pre-component-test和pre-集成测试)。没有经过单元测试的软件可以和经过单元测试的软件一样好:因为功能测试的覆盖率可以和单元测试的覆盖率一样好(甚至更好)。单元测试影响的不是最终产品的质量,而是开发过程的成本和效率。
  • 经过单元测试的软件可能比没有单元测试的软件便宜(因为集成测试期间的调试可能比单元测试期间的调试效率更低且成本更高);或者它可能更昂贵(因为编写和维护单元测试以及功能测试本身就是额外的成本)。
  • 我在某种程度上同意,ChrisW,但我认为以更低的成本和更高的效率开发的软件本质上就具有更高的质量。此外,有人可能会争辩说,如果你用单元测试来构建一些东西需要更短的时间,你就有更多的资源可以分配给更多的功能,这对你的受众和你的公司都有好处。只是我的 0.02 美元。我认为你的想法是正确的。 :)
【解决方案5】:

如果您正在练习纯测试驱动开发,那么您只有在测试失败后才实施任何代码,并且只有在没有失败测试时才实施测试代码。此外,只实现最简单的方法以使测试失败或通过。

在有限的 TDD 实践中,我已经看到这如何帮助我清除代码生成的每个逻辑条件的单元测试。我不完全相信我的私有代码的逻辑特性 100% 都被我的公共接口公开。实践 TDD 似乎与该指标相辅相成,但仍然存在公共 API 不允许的隐藏功能。

我想你可以说这种做法可以保护我免于将来在我的公共接口中出现缺陷。要么您觉得这很有用(并且可以让您更快地添加新功能),要么您觉得这是在浪费时间。

【讨论】:

  • 前两段看懂了,第三段第一句看不懂。
  • 通过对我所有的内部代码进行测试,当我选择使用更多最初不向公众公开的内部代码时,我会受到保护。这就是我所说的“未来缺陷”。当我扩展我的程序时,我更有可能涵盖最初没有公开的内部案例。
【解决方案6】:

您可以编写功能测试代码;没关系。但是您应该在实现上使用测试覆盖率进行验证,以证明被测试的代码都具有与功能测试相关的目的,并且它实际上做了一些相关的事情。

【讨论】:

  • 你是说功能测试不涵盖实现,因此应该有额外的(非功能?)测试?或者你是说我应该验证(也许使用像 NCover 这样的代码覆盖工具)功能测试是否覆盖了实现?
  • 可以说只有在你的函数中服务于可检测目的的代码才应该在你的应用程序中。如果您无法定义执行某些代码部分的功能,那么在系统中拥有该代码有什么意义? (出于这个原因,FAA 要求飞机软件的测试覆盖率达到 100%)。您应该使用代码覆盖率工具!如果你没有获得足够高的覆盖率(你不是在制造飞机,100% 可能没有必要),你应该编写更多的功能测试,以执行其他测试未涵盖的代码。
  • 您是说功能测试可以而且应该提供足够的代码覆盖率,并且我应该测量/测试覆盖了多少代码。说到覆盖率,覆盖功能的测试比覆盖代码的测试更重要。例如,我可以编写一个 10 行的程序和一个覆盖 100% 的测试,但如果该程序没有实现所需的所有功能,那将是不够的。
  • @ChrisW:是的,您可以编写这样的测试。但是,该测试可能无法通过检查,因为它代表了您想要的所有功能。本次讨论的重点是您是否应该专注于编写黑盒(“需求/功能”导向的测试)或白盒测试。我建议使用测试覆盖工具来检测白盒未测试,您可以只专注于编写功能测试。
【解决方案7】:

您不应该盲目地认为一个单元 == 一个类。我认为这可能适得其反。当我说我写一个单元测试时,我正在测试一个逻辑单元——提供一些行为的“东西”。一个单元可以是单个类,也可以是多个类一起工作以提供该行为。有时它开始是一个单一的班级,但后来演变成三或四个班级。

如果我从一个类开始并为此编写测试,但后来它变成了几个类,我通常不会为其他类编写单独的测试——它们是被测试单元中的实现细节。这样我可以让我的设计成长,我的测试也不会那么脆弱。

我曾经在这个问题上的想法与 CrisW 恶魔开始完全一样 - 更高级别的测试会更好,但在获得更多经验后,我的想法被缓和到“每个班级都应该有一个测试班”之间。每个单元都应该有测试,但我选择定义的单元与我以前所做的略有不同。它可能是 CrisW 所说的“组件”,但通常它也只是一个类。

此外,功能测试可以很好地证明您的系统完成了它应该做的事情,但是如果您想通过示例/测试 (TDD/BDD) 来推动您的设计,那么低水平测试是自然而然的结果。当你完成实现时,你可以扔掉那些低级测试,但这将是一种浪费——测试是一个积极的副作用。如果您决定进行剧烈的重构以使低级测试无效,那么您将它们扔掉并编写一次新的。

将测试/证明您的软件的目标与使用测试/示例来推动您的设计/实施分开可以大大澄清这个讨论。

更新:另外,进行 TDD 基本上有两种方法:由外向内和由内向外。 BDD 促进由外而内,从而导致更高级别的测试/规范。但是,如果您从细节开始,您将为所有类编写详细的测试。

【讨论】:

  • 当“很多时候它也只是一个类”时,您进行此类测试的动机是什么?相反,为什么不通过测试/执行它有助于实现的外部可见功能来覆盖这个类(“外部可见功能”意味着从包外部公开/可见,任何单个类只是一个实现细节)?
  • 正如我所说,我使用测试来驱动我的设计/代码。如果我只对验证我的解决方案的行为感兴趣,那么高级测试就足够了。但是,当我实现细节时,它们对我的帮助还不够,因此设计中的大多数“职责”都有自己的测试。
【解决方案8】:

我同意这里的大部分帖子,但我会补充一点:

首先要测试公共接口,然后是受保护的,然后是私有的。

通常公共和受保护接口是私有和受保护接口的组合的摘要。

就个人而言:您应该测试所有内容。给定一个针对较小功能的强大测试集,您将更有信心相信隐藏的方法有效。我也同意另一个人关于重构的评论。代码覆盖率将帮助您确定额外的代码位在哪里,并在必要时将其重构。

【讨论】:

    【解决方案9】:

    您还在沿用这种方法吗? 我也相信这是正确的做法。 您应该只测试公共接口。 现在,公共接口可以是服务或从某种 UI 或任何其他来源获取输入的某个组件。

    但是您应该能够使用“测试优先”方法来发展公共服务或组件。即定义一个公共接口并测试它的基本功能。它会失败。 使用后台类 API 实现该基本功能。编写 API 以仅满足第一个测试用例。然后继续询问该服务可以做的更多和发展。

    唯一应该采取的平衡决策是将一个大服务或组件分解为几个可以重用的较小服务和组件。如果您坚信组件可以跨项目重用。然后应该为该组件编写自动化测试。但同样,为大型服务或组件编写的测试应该将已经测试的功能复制为组件。

    某些人可能会进行理论讨论,认为这不是单元测试。所以没关系。基本思想是进行自动化测试来测试您的软件。那么,如果它不在单位级别怎么办。如果它涵盖与数据库(由您控制​​)的集成,那么它只会更好。

    如果您开发了任何适合您的良好流程,请告诉我......自从您的第一篇文章......

    问候 满足

    【讨论】:

    • 我不同意“你应该只测试公共接口”。我说“你应该测试公共接口”和“测试私有/内部接口可能没有必要”。如果其他组件尚不存在,或者系统测试成本高昂,或者集成测试期间的错误修复困难或耗时,则单元/组件测试很有用。另外,从我对回归测试框架的描述中,你会发现我并不是在做测试优先的开发。
    【解决方案10】:

    我也亲自测试受保护的部分,因为它们对继承的类型是“公共的”...

    【讨论】:

    • 如果客户正在使用我的受保护部件(例如,如果我将带有受保护部件的库发送给其他程序员),我会发现这适用。
    【解决方案11】:

    我同意理想情况下代码覆盖率应该是 100%。这并不一定意味着 60 行代码就有 60 行测试代码,而是每个执行路径都经过测试。唯一比 bug 更烦人的是尚未运行的 bug。

    如果只测试公共 API,您将面临无法测试内部类的所有实例的风险。我这样说确实是在说明显而易见的事情,但我认为应该提及。对每种行为进行的测试越多,就越容易识别出它不仅被破坏,而且更容易识别出什么被破坏。

    【讨论】:

    • 你说,“这并不一定意味着 60 行代码就有 60 行测试代码”。 unit 测试的人似乎说每个类都应该有相应的测试......而我有对类集合的测试(即组件/包/程序集/库)......唯一的类我有测试的是定义外部 API 的公共类。
    • 我发现为了添加一个新功能,我需要添加一个新的测试用例(测试新功能)并且可能编辑十几个现有类(实现新功能) .注:编辑十几个现有类意味着编辑或创建十几个测试用例(每个类一个测试用例)。
    • 不,你只会编辑那些被破坏的测试用例。不要编辑没有损坏的测试。并创建了十几个类,在我们的例子中,它们已经存在了。
    【解决方案12】:

    我测试私有实现细节以及公共接口。如果我更改了实现细节并且新版本有错误,这使我可以更好地了解错误的实际位置,而不仅仅是它的影响。

    【讨论】:

      【解决方案13】:

      [我自己的问题的答案]

      也许很重要的变量之一是有多少不同的程序员在编码:

      • 公理:每个程序员都应该测试自己的代码

      • 因此:如果程序员编写并交付了一个“单元”,那么他们也应该测试过该单元,很可能通过编写“单元测试”

      • 推论:如果单个程序员编写整个包,那么程序员编写整个包的功能测试就足够了(无需编写包内单元的“单元”测试,因为这些单元是其他程序员无法直接访问/了解的实现细节)。

      类似地,构建可以测试的“模拟”组件的做法:

        1234563 “集成测试”,在其他团队交付可以测试您的组件的组件之前。
      • 如果您正在开发整个系统,那么您可以扩展整个系统...例如,开发一个新的 GUI 字段、一个新的数据库字段、一个新的业务事务和一个新的系统/功能测试,所有这些都是一次迭代的一部分,无需开发任何层的“模拟”(因为您可以改为针对真实事物进行测试)。

      【讨论】:

      • 如果你有选择,你应该使用“对手测试”。您不希望编写代码的人对其进行测试;他看不到漏洞,因为他相信它有效。您希望一个公正的甚至是敌对的测试人员考虑可能的漏洞并编写测试以验证这些情况不会发生。
      • Ira:我同意“对抗性测试”可能很有价值,但只能作为后期处理。在单元/集成测试级别依赖“对手测试”是非常浪费的。最糟糕的是,如果编写软件时不考虑可测试性,那么为它编写测试代码是非常困难的!软件工程师被免除了清理他们自己不可测试的代码的责任,并使测试人员的工作成为一场噩梦。我发现当开发人员编写大量测试并且稍后涵盖“对手测试”通过(或代码审查)时,它会更有效率。
      【解决方案14】:

      公理:每个程序员都应该测试自己的代码

      我不认为这是普遍正确的。

      在密码学中,有句名言:“很容易创建一个安全的密码,你自己都不知道如何破解它。”

      在您的典型开发过程中,您编写代码,然后编译并运行它以检查它是否按照您的想法进行。重复这个过程一段时间,你就会对自己的代码充满信心。

      你的自信会让你成为一个不那么警惕的测试人员。不与您分享代码经验的人不会有问题。

      另外,一双新的眼睛可能不仅对代码的可靠性,而且对代码的作用,都有更少的先入之见。结果,他们可能会想出代码作者没有想到的测试用例。人们会期望他们要么发现更多错误,要么更多地传播有关代码在组织中的作用的知识。

      另外,有一个论点是,要成为一名优秀的程序员,您必须担心边缘情况,但要成为一名优秀的测试人员,您必须过分担心 ;-) 而且,测试人员可能更便宜,所以它可能值得因此有一个单独的测试团队。

      我认为最重要的问题是:哪种方法最适合发现软件中的错误?我最近观看了一段视频(没有链接,抱歉),说明随机测试比人工生成的测试更便宜且同样有效。

      【讨论】:

      • 我并不是说测试他们自己的代码而不是其他人测试它:我的意思是,当他们在一个开发人员团队中工作时,他们应该测试他们自己的代码自己的代码别人测试它之前...换句话说,在一个团队中,您不能签入未经测试的代码,这些代码会破坏构建并干扰其他开发人员的工作...并且,其他集成测试所需的组件可能尚不存在……而且,调试集成中发现的错误更困难/更昂贵……因此,您在团队中工作的越多,尽早完成可能就越重要,单元测试。
      • 相反,您对软件的看法越一致,您对其他开发人员的干扰和依赖就越少,那么您就越有能力跳过早期的单元测试而只进行集成测试.
      【解决方案15】:

      这取决于您的设计以及最大价值所在。一种类型的应用程序可能需要与另一种不同的方法。有时您几乎无法通过单元测试发现任何有趣的东西,而功能/集成测试会产生惊喜。有时,单元测试在开发过程中会失败数百次,从而捕捉到许多正在酝酿中的错误。

      有时它是微不足道的。一些类结合在一起的方式使得测试每条路径的投资回报不那么诱人,所以你可以画一条线,继续敲击更重要/复杂/大量使用的东西。

      有时仅仅测试公共 API 是不够的,因为其中潜藏着一些特别有趣的逻辑,而且让系统运行并执行这些特定路径过于痛苦。到那时,测试它的胆量确实会得到回报。

      这些天来,我倾向于编写许多(通常非常)简单的类,它们只做一两件事情。然后,我通过将所有复杂的功能委托给这些内部类来实现所需的行为。 IE。我的交互稍微复杂一些,但类非常简单。

      如果我改变了我的实现并且不得不重构其中的一些类,我通常不在乎。我尽我所能保持我的测试绝缘,所以让它们再次工作通常是一个简单的改变。但是,如果我确实必须丢弃一些内部类,我通常会替换少数类并编写一些全新的测试。我经常听到人们抱怨在重构之后必须保持测试的最新状态,虽然这有时是不可避免的和令人厌烦的,但如果粒度级别足够精细,那么丢弃一些代码 + 测试通常没什么大不了的。

      我觉得这是可测试性设计与不打扰的主要区别之一。

      【讨论】:

      • 主要区别之一是什么?如果我在测试功能(比如验收测试),那么我认为需要测试的是需求或功能规范(而不是代码的设计或实现)。
      猜你喜欢
      • 2018-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 2015-11-07
      • 1970-01-01
      相关资源
      最近更新 更多