单元测试,静态和工厂

时间:2011-04-12 15:11:40

标签: java unit-testing junit static-methods

我正在Java中实现一个模型,它需要迭代一个集合并经历一些识别阶段,它涉及到循环,而循环等。这是我想要在细粒度级别上测试的东西所以我有信心它已经正确实施。

我用它作为开始单元测试的机会,因为我认为这对我的代码有益。从那以后,我一直在阅读一本书籍,以便快速掌握JUnit和单元测试。

基本上我的问题归结为我收到的两条相互矛盾的建议:

1)静力学是邪恶的。不要碰静电。不要测试私人,你可能想要一个班级 2)使用工厂进行创建以允许使用参数进行依赖注入 - 可能允许使用模拟和存根进行隔离。

在我的例子中,我希望按照以下方式执行操作:

double height = 223.42; // this was set iterating over a collection of doubles
//blah
HeightBounds b = HeightBounds.getHeightBounds(height);
//more blah

我这样做是为了避免构建一个非常长而复杂的代码块,我只能完整地测试它。这样我就可以测试公共可访问对象,以确保系统组件都能正常运行。

常识告诉我,静态工厂没有任何问题,并且它们很容易测试,但鉴于我正在学习测试驱动设计,我是否遗漏了一些令人眼花缭乱的东西?

谢谢

5 个答案:

答案 0 :(得分:3)

  

静力学是邪恶的。不要触摸静电。

“静态”这里可能意味着单身人士,即全球状态。这确实很难在单元测试中使用,并且可能会引入许多微妙的问题,因此最好一般地避免它。但是,static成员(字段,方法或内部类)通常不一定是个问题。

  

也不要测试私有,你可能想要一个类。

一般情况下,如果您觉得需要测试私有方法,则表明您的API不够精细,并且封闭类可能会尝试做太多。通常,您可以识别一些连贯的功能组,最好将其提取到单独的类中。这样可以改善您的设计,从而使单元测试更容易。

答案 1 :(得分:3)

静态工厂类引入了类和HeightBounds类之间的耦合。如果例如HeightBounds关闭并查找数据库中的信息,或者从Web服务中读取等等,这可能会使您的类难以测试。

如果您在课程中注入IHeightBounds实现,那么您可以将其模拟出去,这样您就可以测试当您的类的依赖关系做某些事情时会发生什么。

例如,如果HeightBounds抛出异常怎么办?还是返回null?或者您想测试何时返回特定的HeightBound?使用接口可以很容易地模拟这种行为,使用静态工厂会更难以制作数据以在类中创建所需的结果。

你仍然可以只有HeightBounds的单个实现,并且能够单独测试它,但是你可以在没有真正实现的情况下测试上面的方法。

我可能会有一个IHeightBoundFactory接口并在类中注入一个实现。

至于测试私人,通常你不想。您希望测试两件事中的一件,要么结果符合您的预期,要么交互符合您的预期。

如果您有一个名为Add的方法和一个名为GetAll的方法,那么当您致电Add然后拨打GetAll时,您可能需要对其进行测试你加了一个。你不关心它是如何实现的,只是它有效。这是测试结果。通常在这种情况下,您希望创建返回数据的模拟对象。这似乎是你的情况。

如果在调用Add时,您希望记录正在添加的内容,那么您希望测试与日志记录依赖项的交互,因此您将注入模拟依赖项并验证与该类的交互发生时你打电话给Add。通常在这种情况下,您希望创建具有期望设置的模拟对象,并验证是否已满足这些期望。在上述情况下,它看起来不是必要的。

答案 2 :(得分:2)

静态工厂的一个问题是,您无法通过模拟替换工厂(有时是工厂创建的对象)。 - 这就是IOC容器如此有用的一个原因。

答案 3 :(得分:1)

静态方法基本上可以杀死单元测试。

当您想要对应用程序进行单元测试时,其中一个基本想法是能够隔离代码的不同部分。您通常通过为您的环境连接模拟对象来完成此操作。如果使用静态方法,则无法连接任何内容。而且,使用静态方法会隐藏对象之间的依赖关系。

在你的最后一段中,你说你想学习测试驱动的设计。如果您在之后编写测试,那么您已经编写了代码,那么您的测试实际上并没有带来什么。先写下你的测试。

关于私有方法,您通常会对使用这些私有方法的公共方法进行完整的测试覆盖,因此您无论如何都要覆盖它们。如果你想要测试一个非常复杂的私有方法(你不应该),只需使用反射来使其可访问。我宁愿打破封装而不是使用未经测试的代码。

Misko Hevery在此主题上有一个非常nice post

答案 4 :(得分:0)

静态方法本身并不邪恶。在JDK中查看其中有多少。静态方法也需要进行单元测试,所以如果你有任何继续测试它们。

因此静态方法无法杀死单元测试。

但是如果你只是在单元测试中编写静态代码来编写更少的代码,那么这是一条错误的路径,因为正如其他人所说的那样,你应该使用常用的API更好地使用你的对象 - 因为这是你想要测试的。

首先编写测试的完整ACK - 它还可以帮助您设计更好的API,使您的课程正确。