Oop 如何测试私有方法、类和模块?

Oop 如何测试私有方法、类和模块?,oop,bdd,Oop,Bdd,我已经看过关于这个主题的其他讨论(关于StackOverflow),但是其他问题似乎是特定于语言的,而这不是特定于语言的,我正在考虑不再使用私有方法、类和模块 我想测试我的私有方法、类和模块,以便更容易找到bug。为了让我做到这一点,我考虑不再使用私有方法、类和模块,原因有二:(1)我看不到在不注入测试代码或使用某种“魔法”的情况下测试私有方法、类或模块的合理方法;(2)改进代码重用。注意,我没有考虑不再使用私有变量和属性,因为数据需要保护,并且不提供行为,因此在测试期间不需要公开 举个蹩脚的例

我已经看过关于这个主题的其他讨论(关于StackOverflow),但是其他问题似乎是特定于语言的,而这不是特定于语言的,我正在考虑不再使用私有方法、类和模块

我想测试我的私有方法、类和模块,以便更容易找到bug。为了让我做到这一点,我考虑不再使用私有方法、类和模块,原因有二:(1)我看不到在不注入测试代码或使用某种“魔法”的情况下测试私有方法、类或模块的合理方法;(2)改进代码重用。注意,我没有考虑不再使用私有变量和属性,因为数据需要保护,并且不提供行为,因此在测试期间不需要公开

举个蹩脚的例子,如果您正在编写一个名为
OneOperations
的模块,它有两个公共方法
addOne
subtractOne
,以及两个私有方法
add
subtract
。如果您不允许自己拥有私有方法,您可以将这两个私有方法放入另一个模块(
basicOperations
)中,它们是公共的,并将这些方法导入
OneOperations
模块中。由此,您现在应该能够为两个模块中的所有方法编写测试代码,而无需注入代码。这样做的一个优点是,现在可以通过导入
basiccoperations
模块在其他模块中使用
add
subtract
方法(2-提高代码重用)

我觉得这是个坏主意,但我缺乏真实世界的经验来证明我不这么做,这就是为什么我在StackOverflow上发布了这个问题

那么,如何测试私有方法、类和模块呢?编写私有方法、模块和类难道不是一个潜在的解决方案吗?

1)与本主题的许多其他答案一样,主要问题是为什么要测试私有方法?类的目的是为其客户机提供一些功能。如果您有全面的单元测试来证明这个类的公共接口的行为是正确的,那么为什么您关心它在其私有方法中做什么呢

2) 你完全没有私人方法的想法看起来像是割断了你的腿。对于小型项目,可能会对每个微小的行为进行很好的分离和测试。但对于大型项目来说,这是一种过分的做法。重要的是域逻辑的行为是否正确

例如,考虑一种方法:

public double getDistanceSquared(Point other)
{
    return getDifferenceSquared(this.x, other.x)
      + getDifferenceSquared(this.y, other.y); 
}

private double getDifferenceSquared(double v1, double v2)
{
    return (v1 - v2)*(v1 - v2);
}
Ad1)如果
getDistanceSquared
为所有测试用例返回正确的结果,那么单元测试
getDifferenceSquared
方法真的有意义吗

Ad2)创建一个单独的类来计算双打之间的平方距离-如果只有一个地方使用它,将导致大量的小类,并进行数百万次测试。此外,您的域类的构造函数将接受10个不同的接口,用于它们内部所做的每一件小事

维护所有这些都是很多不必要的工作。假设您希望更改计算距离的方法(可能使用一些预先计算的值)。
getDistanceSquared
的行为不会改变。但是您必须更改
getDifferenceSquared
的所有测试,即使您不必关心如何计算距离,只要计算正确

在不必要的时候,潜入小细节会让你忘记你真正在做什么——你失去了“大局观”。珍惜你的时间,关注重要的问题

另外,作为旁注,单元测试的主要关注点不是您所建议的“定位bug”。它们采用干净的设计,提供代码行为的始终最新文档,并允许方便的重构,从而为您提供灵活性。此外,它们还向您保证代码按照您的预期工作


还有另一种方法,那就是如何生成私有方法

如果我们正确地遵循TDD过程,那么我们编写的第一件事就是测试。此时,测试应该包含我们的所有代码,例如

public void ShouldAddTwoNumbers()
{
  (1 + 1).ShouldEqual(2);
}
是的,那看起来很可怕。但是考虑一下,当我们写的时候会发生更多的测试。

public void ShouldAddTwoMoreNumbers()
{
  (2 + 2).ShouldEqual(4);
}
现在我们有了一些东西,可以把它变成反应堆

public void ShouldAddTwoNumbers()
{
  Add(1, 1).ShouldEqual(2);
}

public void ShouldAddTwoMoreNumbers()
{
  Add(2, 2).ShouldEqual(4);
}

private int Add(int a, int b)
{
  return a+b;
}
现在我们有了一个私有方法,可以在测试类中进行测试。只有当您完成进一步的重构以将代码移出应用程序时,私有性才成为一个问题。大多数自动重构工具都会在此时为您提供更改方法签名的选项,以便私有方法仍然可以访问,因为它不是私有的

(有一个很棒的练习叫做TDD,就像我刚才解释的Keith Braithwaite的意思一样)

然而,这并不是我们重构和开发的结束。在编写和重构测试时,我们应该做的一件事是删除旧测试,例如当功能重复时。另一个是提取新的方法,这样我们就不会重复自己。这两种情况都可能导致在非测试代码库中有私有方法的情况


因此,我的建议是要务实,为面前的代码做出最好的决定。我不建议您不要创建私有方法,但我会看看导致您创建它们的因素。

我觉得这个问题是特定于语言的,因为不同的语言有不同的“打开”(破坏)方法“隐私”的方式。然而,我认为1)测试,从编写测试的意义上说,应该只应用于公共方法。毕竟,它们定义了您测试的“语义”,而私有方法只是实现。2)对于调试,这是i