私人/受保护的方法应该进行单元测试吗?

Raj*_*Rao 79 tdd unit-testing

在TDD开发中,您通常首先要创建接口,然后开始针对该接口编写单元测试.当您逐步完成TDD过程时,您最终会创建一个实现接口的类,然后在某些时候您的单元测试将通过.

现在我的问题是关于我可能必须在我的类中编写的私有和受保护的方法,以支持接口公开的方法/属性:

  • 班级中的私有方法是否应该有自己的单元测试?

  • 班级中受保护的方法是否应该有自己的单元测试?

我的想法:

  • 特别是因为我正在编写接口,我不应该担心受保护/私有方法,因为它们是黑盒子.

  • 因为我正在使用接口,所以我正在编写单元测试以验证定义的合同是否由实现接口的不同类正确实现,所以我再也不担心私有/受保护的方法,并且应该通过调用它的单元测试来执行它们.接口定义的方法/属性.

  • 如果我的代码覆盖率没有显示受保护/私有方法被命中,那么我没有正确的单元测试,或者我有没有被使用的代码,应该被删除.

小智 102

不,我不认为测试私有或受保护的方法.类的私有和受保护方法不是公共接口的一部分,因此它们不会暴露公共行为.通常,这些方法是在您将测试变为绿色后通过重构创建的.

因此,这些私有方法由断言公共接口行为的测试隐式测试.

在更哲学的说明中,请记住,您正在测试行为,而不是方法.因此,如果您考虑被测试类可以执行的操作集,只要您可以测试并断言该类的行为与预期一致,是否存在由类在内部使用的私有(和受保护)方法来实现这种行为无关紧要.这些方法是公共行为的实现细节.

  • 我喜欢你说单元测试,测试行为而不是方法的事实!这澄清了很多事情. (21认同)
  • 您是否仍然认为这仍适用于基类实现受保护行为的情况,公众应该继承和使用?那么受保护的方法仍然是公共接口的一部分,不是吗? (3认同)
  • 这并不能清除受保护可见性的情况.似乎受保护的方法也是接口的一部分,通常,它是一个扩展点,故意保护为这样.我会说在那些情况下,你也应该对它们进行单元测试.您不希望任何人在将来改变事物并打破依赖于这些行为扩展点的类. (3认同)

Cha*_*oth 41

我不赞同大多数海报.

最重要的规则是:工作代码启动关于公共/受保护/私人的理论规则.

您的代码应该经过全面测试.如果你可以通过为公共方法编写测试来实现这一点,那么就可以充分运用受保护/私有方法,这很好.

如果你不能,那么要么重构,要么你可以,或弯曲受保护/私人规则.

关于给孩子们进行测试的心理学家,有一个很棒的故事.他给每个孩子两块木板,两端各有一根绳子,要求他们尽可能快地穿过房间,不要将脚碰到地板.所有的孩子都使用小板滑雪板,每块板子一只脚,用绳子抓住它们,然后滑过地板.然后他给了他们同样的任务,但只使用了一块板.他们在地板上转动/"走",一只脚踏在单板的每一端 - 而且它们更快!

仅仅因为Java(或任何语言)具有一个功能(私有/受保护/公共)并不一定意味着您正在编写更好的代码,因为您使用它!

现在,总会有方法来优化/最小化这种冲突.在大多数语言中,您可以使方法受到保护(而不是公共),并将测试类放在同一个包(或其他)中,并且该方法可用于测试.正如其他海报所描述的那样,有一些注释可以提供帮助.您可以使用反射来获取私有方法(yuck).

背景也很重要.如果您正在编写供外部人员使用的API,则公共/私有更重要.如果这是一个内部项目 - 谁真的关心?

但最终,请考虑缺少测试导致的错误数量.然后比较"过度可见"方法导致的错误数量.那个答案应该会推动你的决定.

  • 所以在你开始为私人方法编写测试之前,我会说总是重新考虑你的设计.看看事情是否可以概括并变成纯粹的功能方法.如果是这样,您可以将它们提取到自己的构造中.然后,此构造可以拥有自己的公共接口并进行单元测试.请记住,私人方法中的复杂行为通常可能是一个班级承担更多责任的标志.所以,请先重新考虑您的设计. (6认同)
  • 如果一个方法很关键,并且具有复杂的逻辑,那么断言它的行为对于防止错误非常有用。为这种方法编写单元测试甚至可以帮助您以某种探索性方式实现该方法。因此,即使它是私有的,我也会说它值得进行单元测试。但是,有一个很大的但是,您必须记住测试是代码耦合。如果您为方法编写测试,则会阻止重构。 (4认同)
  • “工作代码”是有效的代码。如果您的私有(或准私有)方法中存在错误,而您的公共方法的测试未发现该错误,则说明有问题。也许您的设计是错误的,但很公平:我同意最好的解决方案是调用公共方法的测试。但这并不总是可行的,尤其是当您添加或修复遗留代码时。(我是根据经验说的,在一个有 100 万行代码的项目中。)经过测试的代码总是比未经测试的代码好,句号。即使我们打破了关于只测试公共方法的好规则! (2认同)
  • 关于“测试是代码耦合...防止重构”的位(在顶部)是 100% 错误的。在建筑比喻中,测试是脚手架,而不是具体的。事情变了,测试变了,被抛弃了,新的测试被写了。我同意好的设计可以最大限度地减少测试重写。但是,即使是最好的设计,也会发生变化。 (2认同)

Lun*_*ore 34

你写了:

在TDD开发中,您通常首先要创建接口,然后开始针对该接口编写单元测试.当您逐步完成TDD过程时,您最终会创建一个实现接口的类,然后在某些时候您的单元测试将通过.

请让我用BDD语言改写一下:

在描述类为什么有价值以及它的行为方式时,通常首先要创建一个如何使用类的示例,通常是通过其接口*.当您添加所需的行为时,您最终会创建一个提供该值的类,然后在某些时候您的示例可以正常工作.

*可能是类的实际Interface或简单的可访问API,例如:Ruby没有接口.

这就是为什么你不测试私有方法 - 因为测试是如何使用类的一个例子,你实际上不能使用它们.如果你愿意,你可以做的事情是将私有方法中的职责委托给协作类,然后模拟/存储帮助程序.

使用受保护的方法,您会说扩展类的类应该具有某些特定的行为并提供一些值.然后,您可以使用类的扩展来演示该行为.例如,如果您正在编写有序集合类,则可能需要演示具有相同内容的两个扩展是否相等.

希望这可以帮助!


for*_*rir 15

当您为类编写单元测试时,您不必关心类的功能是否直接在公共接口上的方法中实现,或者是否在一系列私有方法中实现.所以是的,您应该测试您的私有方法,但是您不需要直接从您的测试代码中调用它们(直接测试私有方法将您的实现紧密地耦合到您的测试并使重构变得不必要).

受保护的方法在您的类和未来的子级之间形成不同的契约,因此您应该在与公共接口类似的程度上对其进行测试,以确保合同得到很好的定义和运用.


S.L*_*ott 12

没有!只测试接口.

TDD的一大好处是确保无论您如何选择实施私有方法,界面都能正常工作.


FGM*_*FGM 9

完成上面其他人所说的,我会说受保护的方法是某种接口的一部分:它恰好是暴露于继承而不是组合的接口,这是每个人在考虑接口时都会考虑的问题.

将方法标记为受保护而非私有意味着它应该被第三方代码使用,因此需要定义和测试某种契约,就像公共方法定义的普通接口一样,这些接口对于继承和组合都是开放的. .


Did*_* A. 7

编写测试有两个原因:

  1. 断言预期的行为
  2. 防止行为回归

承担(1)断言预期的行为:

当您声明预期的行为时,您希望确保代码按照您的想法运行.这实际上是一种自动执行常规手动验证的方式,任何开发人员在实现任何类型的代码时都会执行此操作:

  • 我写的东西有用吗?
  • 这个循环真的结束了吗?
  • 它按照我认为的顺序循环吗?
  • 这是否适用于空输入?

这些是我们在脑海中回答的问题,通常,我们也会尝试在头脑中执行代码,确保它看起来确实有效.对于这些情况,让计算机以明确的方式回答它们通常很有用.所以我们编写一个断言它的单元测试.这使我们对代码充满信心,帮助我们尽早发现缺陷,甚至可以帮助实际实现代码.

在您觉得有必要的地方这样做是个好主意.任何有点难以理解或非常重要的代码.即使是微不足道的代码也可以从中受益.这完全取决于你自己的信心.多久做一次以及走多远取决于你自己的满意度.当你可以自信地回答"是"时停止:你确定这有效吗?

对于这种测试,您不关心可见性,接口或其中任何一种,您只关心工作代码.所以,是的,如果您认为需要对您进行测试以回答问题,那么您将测试私有和受保护的方法.

采取行动(2)防止行为回归:

一旦你有了工作代码,你需要有一个机制来保护这些代码免受未来的破坏.如果没有人再次触摸你的源和你的配置,你就不需要这样,但在大多数情况下,你或其他人会触及你的软件的来源和配置.这种内部摆弄极有可能破坏您的工作代码.

大多数语言中都存在机制,以防止这种损害.可见性功能是一种机制.私有方法被隔离并隐藏.封装是另一种机制,您可以在其中划分内容,以便更改其他隔离专区不会影响其他隔离专区.

这种方法的一般机制称为:编码到边界.通过在代码的各个部分之间创建边界,可以保护边界内的所有内容不受其外部内容的影响.边界成为交互点,以及事物相互作用的契约.

这意味着通过打破界面或破坏它的预期行为来改变边界会损坏并可能破坏依赖它的其他边界.这就是为什么进行单元测试是一个好主意,它针对这些边界并断言它们不会在语义和行为上发生变化.

这是您典型的单元测试,在提及TDD或BDD时,每个人都会讨论这个问题.重点是加强边界并保护它们免受变化.您不希望为此测试私有方法,因为私有方法不是边界.受保护的方法是限制边界,我会保护它们.它们不暴露于世界,但仍暴露于其他隔间或"单位".

该怎么做?

正如我们所见,有一个很好的理由对单元测试公共和受保护的方法进行单元测试,因为断言我们的接口不会改变.并且还有充分的理由来测试私有方法,以断言我们的实现工作.那么我们应该对它们进行单元测试吗

是和否.

首先:测试所有您认为需要明确证明其在大多数情况下有效的方法,以便能够确信您的代码能够工作,无论可见性如何.然后,禁用这些测试.他们已经完成了工作.

最后:为您的边界编写测试.对系统其他单元使用的每个点进行单元测试.确保此测试断言语义契约,方法名称,参数数量等.还要确保测试断言单元的可用行为.您的测试应该演示如何使用该装置,以及该装置可以做什么.保持这些测试的启用,以便它们在每次代码推送时运行.

注意:您禁用第一组测试的原因是允许重构工作发生.主动测试是代码耦合.它可以防止将来修改它正在测试的代码.您只希望这适用于您的接口和交互合同.