"因为是private所以不测试"?别想当然

16 阅读3分钟

"不要测试private方法"

这是个著名的原则。我基本上也同意。

但你是不是因为这条规则,放弃了真正需要的测试?

public和private的区别

public方法是稳定的接口——对外公开的概念窗口。针对它的测试自然也是稳定的。

而private方法是内部实现细节。重构时会增减,public方法变化时也会受影响。它本质上是不稳定的。针对它的测试自然也会不稳定。

不稳定测试的代价

针对不稳定方法的测试,维护成本太高。

想重构,但改测试的成本太大,于是放弃了。这种情况需要避免。

所以"不要测试private方法"才会成为原则。

共用逻辑的两难

多个public方法调用的共用逻辑。通过public方法测试的话,可能要在多处重复相同的验证。

直接测试共用逻辑,只需要写一处。

但正如前面所说,不稳定测试的代价很高。如果要直接测试private方法,必须仔细斟酌是否真的有必要。

"提取成单独的类"的疑问

"想测试的话,就提取成另一个类的public方法。"这是常见的建议。

当然,如果从设计角度看分离是合理的,那就应该提取。符合单一职责原则的拆分是好事。

但仅仅为了写测试而拆分类?那是另一回事。高内聚的类被拆散。原本private的逻辑变成了public(虽然是在另一个类里)。两者都应该避免。

为了测试的便利而扭曲生产代码,这应该不是我们的本意。

private的接口

private方法不是接口——这是常识。

但如果你强烈觉得某个private方法应该单独测试,那么它很可能是被多个public方法共享的,通往"内部概念"的窗口。

如果这个内部概念足够重要,就值得像设计public方法一样认真对待。结果会是:这个private方法变成了"稳定的内部接口"。针对它的测试也会变得稳定。

这本质上和"提取成单独的类"是一回事。整理代码,但不拆分、不公开——仅此而已。

原则与例外

遵守原则很重要。但不理解为什么就盲目遵守,是想当然。不应该仅仅因为可见性就否定正确的判断。

不过,适用的场景并不多。大多数private方法只是实现细节,或者是不需要单独测试的内部概念。

只有满足以下所有条件时,才允许例外:

  • 你强烈觉得需要单独测试
  • 它是不应该成为单独类的内部概念
  • 代码已经整理成稳定的接口

缺一不可。否则,不要写那个测试。

而且,这种判断需要经验。如果团队里没有能做出判断的人,如果Code Review没有正常运作——那就全面禁止吧。

不要停在"因为是private"。多想一步。

原文: sijiaoh.com/zh/posts/pr…