13

当我编写代码时,我只编写我需要的函数。

这种方法是否也适用于编写测试?

我应该提前为每个我能想到的用例编写一个测试,只是为了安全起见,还是应该只在遇到一个用例时为它编写测试?

4

11 回答 11

13

我认为当你编写一个方法时,你应该测试预期和潜在的错误路径。这并不意味着您应该扩展您的设计以涵盖所有潜在用途——将其留到需要的时候,但您应该确保您的测试已经定义了面对无效参数或其他条件的预期行为。

据我了解,YAGNI 意味着您不应该开发还不需要的功能。从这个意义上说,您不应该编写一个测试来驱使您开发不需要的代码。不过,我怀疑这不是你要问的。

在这种情况下,我会更关心您是否应该编写涵盖意外用途的测试 - 例如,由于传递 null 或超出范围参数而导致的错误 - 或者重复仅在数据方面有所不同的测试,而不是功能. 在前一种情况下,正如我上面指出的,我会说是的。您的测试将记录您的方法在遇到错误时的预期行为。这对使用您的方法的人来说是重要的信息。

在后一种情况下,我无法给你一个明确的答案。你当然希望你的测试保持 DRY —— 不要编写一个简单地重复另一个测试的测试,即使它有不同的数据。或者,除非您使用数据的边缘情况,否则您可能不会发现潜在的设计问题。一个简单的例子是计算两个整数之和的方法:如果将它maxint作为两个参数传递会发生什么?如果您只有一个测试,那么您可能会错过这种行为。显然,这与上一点有关。只有您可以确定何时真正需要进行测试。

于 2009-06-03T15:52:22.057 回答
9

是的,YAGNI 绝对适用于编写测试。

例如,我不编写测试来检查任何属性。我假设属性以某种方式工作,直到我找到一个与规范不同的东西,我不会对它们进行测试。

您应该始终考虑编写任何测试的有效性。如果编写测试对您没有明显的好处,那么我建议您不要这样做。然而,这显然是非常主观的,因为你可能认为不值得别人认为非常值得付出努力。

另外,我会编写测试来验证输入吗?绝对地。但是,我会做到这一点。假设您有一个带有 3 个整数参数的函数,它返回一个双精度值。您将围绕该功能编写多少测试。我会在这里使用 YAGNI 来确定哪些测试会给你带来良好的投资回报率,哪些是无用的。

于 2009-06-03T15:47:36.580 回答
4

根据需要编写测试。测试就是代码。预先编写一堆(最初失败的)测试打破了 TDD 的红色/修复/绿色循环,并且使识别有效失败与未编写代码变得更加困难。

于 2009-06-03T15:48:51.430 回答
4

您应该为在此开发阶段要实现的用例编写测试。

这带来了以下好处:

  1. 您的测试有助于定义此阶段的功能。
  2. 你知道你什么时候完成了这个阶段,因为你所有的测试都通过了。
于 2009-06-03T15:50:32.493 回答
3

理想情况下,您应该编写涵盖所有代码的测试。否则,您的其余测试将失去价值,最终您将反复调试那段代码。

所以不行。YAGNI 不包括测试 :)

于 2009-06-03T15:49:07.640 回答
3

您可能会在这里遇到一些差异,但通常,编写测试(对我而言)的目标是确保您的所有代码都按应有的方式运行,没有副作用,以可预测的方式并且没有缺陷。因此,在我看来,您讨论的仅针对遇到的用例编写测试的方法对您没有真正的好处,实际上可能会造成伤害。

如果您忽略的被测单元的特定用例导致最终软件出现严重缺陷怎么办?在这种情况下,除了错误的安全感之外,花在开发测试上的时间给你带来了什么吗?

(作为记录,这是我在使用代码覆盖率来“衡量”测试质量时遇到的问题之一——这个衡量标准如果低,可能表明你没有进行足够的测试,但如果高,应该不要用来假设你是坚如磐石的。测试常见情况,测试边缘情况,然后考虑单元的所有 ifs 和 buts 并测试它们。)

轻度更新

我应该注意,我可能来自与这里的许多人不同的角度。我经常发现我正在编写库风格的代码,也就是说,将在多个项目中重用的代码,用于多个不同的客户。因此,我通常不可能肯定地说某些用例根本不会发生。我能做的最好的事情是记录它们不是预期的(因此可能需要在之后更新测试),或者 - 这是我的偏好:) - 只是编写测试。我经常发现选项 #2 更适合日常使用,这仅仅是因为我在新应用程序 Y 中重用组件 X 时更有信心。在我看来,自动化测试是什么所有关于。

于 2009-06-03T15:49:57.907 回答
3

当然,为你不确定是否会实现的用例编写测试毫无意义——这对任何人来说都应该是显而易见的。

对于您知道将要实现的用例,测试用例的收益会递减,即,当您可以用一半的工作覆盖所有重要和关键路径时,尝试覆盖每一个可能的晦涩角落案例并不是一个有用的目标——假设,当然,忽略一个很少发生的错误的代价是可以承受的;在编写航空电子软件时,我当然不会满足于低于 100% 的代码和分支覆盖率。

于 2009-06-03T15:55:23.647 回答
2

您当然应该推迟为尚未实现的功能编写测试用例。测试应该只针对现有功能或您将要加入的功能编写。

但是,用例与功能不同。您只需要测试您已确定的有效用例,但可能会发生很多其他事情,并且您希望确保这些输入得到合理的响应(很可能是错误消息) .

显然,您不会获得所有可能的用例。如果可以,则无需担心计算机安全性。你应该至少得到更合理的那些,并且当问题出现时,你应该将它们添加到用例中进行测试。

于 2009-06-03T15:58:38.767 回答
1

我认为这里的答案是,就像在很多地方一样,这取决于。如果一个函数提供的合约声明它执行 X,并且我看到它有相关的单元测试等,我倾向于认为它是一个经过良好测试的单元并使用它,即使我没有在其他地方以完全相同的方式使用它。如果该特定使用模式未经测试,那么我可能会遇到令人困惑或难以追踪的错误。出于这个原因,我认为测试应该涵盖所有(或大部分)已定义的、记录在案的单元行为。

如果您选择以增量方式进行测试,我可能会在文档注释中添加该函数“仅针对 [某些类型的输入] 进行测试,其他输入的结果未定义”。

于 2009-06-03T16:14:14.637 回答
0

我经常发现自己正在为我不希望正常程序流调用的情况编写测试,TDD。“假装直到你做到”的方法让我开始,通常,输入一个空值 - 足以让我知道函数调用应该是什么样子,它的参数将具有什么类型以及它将具有什么类型返回。需要明确的是,我不会只在我的测试中向函数发送 null ;我将初始化一个类型化变量以保存空值;这样,当 Eclipse 的 Quick Fix 为我创建函数时,它已经具有正确的类型。但我并不期望程序通常会向函数发送空值,这种情况并不少见。所以,可以说,我正在编写一个我 AGN 的测试。但如果我从价值观开始,有时它太大了。一世' m 从一开始就设计 API 并推动其实际实现。因此,通过开始缓慢并假装它直到我成功,有时我会为我不希望在生产代码中看到的案例编写测试。

于 2009-06-03T16:15:57.540 回答
0

如果您以 TDD 或 XP 风格工作,那么您不会像您所说的那样“提前”编写任何内容,您将在任何给定时刻处理非常精确的功能,因此您将编写所有必要的测试,以确保该功能按您的预期工作。

测试代码与“代码”本身类似,您不会为您的应用程序的每个用例预先编写代码,那么您为什么要提前编写测试代码呢?

于 2009-06-03T16:46:26.890 回答