您如何测试私有方法,类和模块?

时间:2013-09-22 11:16:42

标签: oop bdd

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

我想测试我的私有方法,类和模块,以便我可以更轻松地找到错误。为了让我这样做,我考虑不再使用私有方法,类和模块有两个原因,(1)我认为没有合理的方法来测试私有方法,类或模块而不注入测试代码或使用某种排序“魔术”和(2)改进代码重用。请注意,我不会考虑不再使用私有变量和属性,因为数据需要保护并且不提供行为,因此在测试期间不需要公开。

作为一个蹩脚的例子,如果你正在编写一个名为OneOperations的模块,它有两个公共方法addOnesubtractOne,以及两个私有方法add和{{ 1}}。如果您不允许自己使用私有方法,则可以将两个私有方法放入另一个公共模块(subtract)中,并在basicOperations模块中导入这些方法。从这里你现在应该能够为两个模块中的所有方法编写测试代码而无需注入代码。这样做的一个优点是方法OneOperationsadd现在可以通过导入subtract模块在​​其他模块中使用(2 - 改进代码重用)。

我觉得这是一个坏主意,但我缺乏真实世界的经验来证明不这样做,这就是我在StackOverflow上发布这个问题的原因。

那么,您如何测试私有方法,类和模块?是不是将私有方法,模块和类编写为可能的解决方案?

2 个答案:

答案 0 :(得分:1)

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)如果getDifferenceSquared为所有测试用例返回正确的结果,那么单元测试getDistanceSquared方法真的有意义吗?

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

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

在没有必要的情况下潜入细微的细节会让你忘记你在做什么 - 你失去了“全局视图”。珍惜您的时间,专注于重要问题。

作为旁注,同样 - 单元测试的主要问题不是如您所建议的那样“定位错误”。它们采用简洁的设计,提供始终最新的代码行为文档,并允许方便的重构,为您提供灵活性。此外,他们向您保证代码按预期工作。

http://artofunittesting.com/definition-of-a-unit-test/

http://en.wikipedia.org/wiki/Unit_testing#Benefits

答案 1 :(得分:1)

还有另一种方法来看待这个问题,即如何生成私有方法?

如果我们正确地遵循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;
}

所以现在我们有一个私有方法,我们可以在我们的测试类中测试。只有当您完成进一步的重构以将代码移到您的应用程序中时,私有才会成为问题。大多数自动重构工具都会为您提供在此时更改方法签名的选项,以便私有方法仍然可访问,因为它不是私有的。

(Keith Braithwaite有一个名为 TDD的神话般的练习,好像你的意思,我刚刚在上面解释过)

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

所以我的建议是务实,为你面前的代码做出最好的决定。我不建议不创建私有方法,但我会考虑导致你创建它们的因素。