我正在研究Java项目。我是单元测试的新手。在Java类中对私有方法进行单元测试的最佳方法是什么?

评论

在StackOverflow上检查此问题。提到并讨论了几种技术。单元测试私有方法的最佳方法是什么?

我一直认为私有方法不需要测试,因为您应该测试可用的方法。一种公共方法。如果您不能破坏公共方法,私有方法到底在做什么真的很重要吗?

公共方法和私有方法都应进行测试。因此,测试驱动程序通常需要位于要测试的类中。这样。

@Rig-+1-您应该能够从公共方法中调用私有方法的所有必需行为-如果不能,则无论如何都不能调用该功能,因此没有必要进行测试。

本文对我很有用。enterprisecraftsmanship.com/posts/unit-testing-private-methods

#1 楼

通常,您不直接对私有方法进行单元测试。由于它们是私有的,因此请考虑将其作为实现细节。没有人会打电话给他们中的一个,并期望它以特定的方式工作。

您应该测试您的公共界面。如果调用私有方法的方法按预期工作,则可以通过扩展假定私有方法正常工作。

评论


+1 bazillion。如果从未调用过私有方法,请不要对其进行单元测试,将其删除!

–史蒂文·劳(Steven A. Lowe)
11年8月14日在6:08

我不同意。有时,私有方法只是实现细节,但它仍然足够复杂,需要进行测试以确保其正确运行。公共接口可能提供的抽象水平太高,无法编写直接针对此特定算法的测试。由于共享数据的缘故,将其分解为一个单独的类并不总是可行的。在这种情况下,我想可以测试一个私有方法。

–quant_dev
2011年8月14日在8:04

@quant_dev:有时需要将一个类重构为其他几个类。您的共享数据也可以放入单独的类中。说,“上下文”类。然后,您的两个新类可以引用上下文类以获得它们的共享数据。这似乎是不合理的,但是如果您的私有方法足够复杂以至于需要进行单独的测试,则它是一种代码气味,表明您的对象图需要变得更加细粒度。

–菲尔
2012年7月17日14:47

这个答案不回答问题。问题是如何测试私有方法,而不是是否应该测试它们。是否应该对私有方法进行单元测试是一个有趣的问题,值得辩论,但这里不是。适当的回答是在问题中添加一条评论,指出测试私有方法可能不是一个好主意,并提供指向一个单独问题的链接,该问题将更深入地解决此问题。

– TallGuy
2014年1月30日22:39

@TallGuy请允许我在这里将我的答案提炼到其核心。答案是“一个单元如何测试私有方法?”是“一个没有。”

–亚当·李尔♦
2014年1月30日22:40



#2 楼

通常,我会避免这样做。如果您的私有方法如此复杂以至于需要单独的单元测试,则通常意味着它应该拥有自己的类。这可能会鼓励您以可重用的方式编写它。然后,您应该测试新类并在旧类中调用它的公共接口。

另一方面,有时将实现细节分解为单独的类会导致具有复杂接口的类,其中很多数据在新旧类之间传递,或者传递到从OOP角度看可能看起来不错,但与问题域的直觉不匹配的设计(例如,将定价模型分为两部分只是为了避免测试私有方法)不是很直观,可能会在以后维护/扩展代码时导致问题。您不想拥有总是一起更改的“孪生类”。

当面临封装性和可测试性之间的选择时,我宁愿再谈谈。拥有正确的代码(即产生正确的输出)比无法正常工作的好的OOP设计更为重要,因为它没有经过充分的测试。在Java中,您可以简单地授予方法“默认”访问权限,并将单元测试放在同一包中。单元测试只是您正在开发的程序包的一部分,可以在测试和要测试的代码之间建立依赖关系。这意味着,当您更改实现时,可能需要更改测试,但是没关系-每次更改实现都需要重新测试代码,如果需要修改测试来做到这一点,则只需执行它。

通常,一类可能提供多个接口。有一个用户界面和一个维护者界面。第二个可以公开更多内容,以确保对代码进行适当的测试。它不必是对私有方法的单元测试,例如可以是日志记录。日志记录也会“破坏封装”,但是我们仍然这样做,因为它是如此有用。

评论


+1有趣的点。最后,它是关于可维护性,而不是遵守良好OOP的“规则”。

–菲尔
2012年7月17日14:55

+1我完全同意封装性的可测试性。

–理查德
2012年11月27日10:44

#3 楼

私有方法的测试将取决于它们的复杂性;一些单行私有方法并不能真正保证测试的额外努力(这也可以称为公共方法),但是某些私有方法可能与公共方法一样复杂,并且很难通过公共接口进行测试。

我的首选技术是将私有方法包设为私有,这将允许访问同一包中的单元测试,但仍将其封装在所有其他代码中。这将带来直接测试私有方法逻辑的优势,而不必依赖公共方法测试来覆盖(可能)复杂逻辑的所有部分。

如果将其与Google Guava库中的@VisibleForTesting批注配对使用,则可以清楚地将此包私有方法标记为仅对测试可见,因此,任何其他类都不应调用此方法。

该技术的支持者认为,这将破坏封装并打开私有方法以在同一程序包中进行编码。尽管我同意这样做会破坏封装并向其他类开放私有代码,但我认为测试复杂的逻辑比严格封装更重要,并且不使用包私有方法(明确将其标记为仅对测试可见)是开发人员的责任使用和更改代码库。

测试前的私有方法:

private int add(int a, int b){
    return a + b;
}


准备测试的打包私有方法:

@VisibleForTesting
int add(int a, int b){
    return a + b;
}


注意:将测试放在同一程序包中并不等同于将它们放在同一物理文件夹中。通常,将主代码和测试代码分隔为单独的物理文件夹结构是一种好的做法,但是只要在相同的程序包中定义了类,该技术就可以使用。

评论


@VisibleForTesting实际上有所帮助。

– iCantC
3月31日4:57

@VisibleForTesting也在androidx.annotation中,几乎每个AndroidX库都附带了该文件。

–Big McLargeHuge
4月6日15:39

但是为什么方法调用不能编译为私有呢?

–雷阳
9月21日下午6:41

#4 楼

如果您不能使用外部API或不想使用外部API,您仍然可以使用纯标准JDK API来通过反射访问私有方法。这是一个示例

MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);


检查Java教程http://docs.oracle.com/javase/tutorial/reflect/或Java API http://docs.oracle .com / javase / 7 / docs / api / java / lang / reflect / package-summary.html了解更多信息。

@kij回答他的问题时,有时使用反射的简单解决方案测试私有方法真的很好。

评论


这似乎是不恰当的。您现在如何在116个测试中处理自动重构?手动?

– Wisienkas
19年11月12日在10:13

@Wisienkas在javadoc中使用(at)see标记进行单元测试,当自动重构更新时,它将更新javadoc-测试将失败并且答案将在本地显示。理想情况下,开发人员将事先检查其提交(diff)并更新丢失的字符串(在更改的javadoc附近)。

–杰森·佩隆(Jason Pyeron)
4月2日下午13:21

这种方法永不过时,对吗?这种方法仍然合法吗?

– Eray Tuncer
5月27日11:07



#5 楼

我通常会保护此类方法。假设您的课程位于:

src/main/java/you/Class.java


您可以创建测试类,如下所示:

src/test/java/you/TestClass.java


现在您可以访问受保护的方法并可以对其进行单元测试(JUnit或TestNG并不重要),但是您可以将这些方法保留给不需要的调用者。

请注意,这需要maven样式的源树。

#6 楼

单元测试用例意味着测试代码单元。这并不意味着测试接口,因为如果您正在测试接口,则并不意味着您正在测试代码单元。这成为一种黑匣子测试。另外,最好是在最小的单元级别上查找问题,而不是在接口级别上确定问题,然后尝试调试哪个部分不起作用。
因此,应该对单元测试用例进行测试,而不论其范围如何。以下是测试私有方法的一种方法。

如果使用Java,则可以使用提供Deencapsulation.invoke的jmockit来调用正在测试的类的任何私有方法。它最终使用反射来调用它,但是围绕它提供了一个不错的包装器。 (https://code.google.com/p/jmockit/)

评论


这如何回答所提问题?

– gna
2014年2月5日下午6:46

@gnat,问题是在Java类中对私有方法进行单元测试的最佳方法是什么?我的第一点回答了这个问题。

–agrawalankur
2014年2月5日在19:46

您的第一点只是广告工具,但并不能解释为什么您认为它比其他工具更好,例如,如最佳答案中所建议并解释的那样,间接测试私有方法

– gna
2014年2月5日在19:49



jmockit已移开,旧的官方页面指向有关“合成尿液和人工小便”的文章。顺便说一句,Wich仍然与嘲笑的东西有关,但在这里不相关:)新的官方页面是:jmockit.github.io

–纪尧姆
19年8月19日在12:12

#7 楼

首先,正如其他作者所建议的那样:如果确实需要测试私有方法,请三思。如果是这样,...

在.NET中,您可以将其转换为“ Internal”方法,并使“ InternalVisible”包成为您的单元测试项目。

在Java中您可以在要测试的类中编写测试本身,并且您的测试方法也应该能够调用私有方法。我真的没有Java的丰富经验,所以这可能不是最佳实践。

谢谢。

#8 楼

如果您真的需要测试私有方法,我的意思是使用Java,您可以使用fest assert和/或fest reflect。它使用反射。

使用maven导入库(给出的版本不是我认为的最新版本)或直接将其导入您的类路径中:

<dependency>
     <groupId>org.easytesting</groupId>
     <artifactId>fest-assert</artifactId>
     <version>1.4</version>
    </dependency>

    <dependency>
     <groupId>org.easytesting</groupId>
     <artifactId>fest-reflect</artifactId>
    <version>1.2</version>
</dependency>


例如,如果您有一个名为“ MyClass”的类,其私有方法名为“ myPrivateMethod”,该方法将String作为参数并将其值更新为“ this is cool testing!”,则可以执行以下junit测试:

import static org.fest.reflect.core.Reflection.method;
...

MyClass objectToTest;

@Before
public void setUp(){
   objectToTest = new MyClass();
}

@Test
public void testPrivateMethod(){
   // GIVEN
   String myOriginalString = "toto";
   // WHEN
   method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
   // THEN
   Assert.assertEquals("this is cool testing !", myOriginalString);
}


该库还使您可以通过模拟替换任何bean属性(无论它们是私有的还是未写入setter),并将其与Mockito或任何其他对象一起使用其他模拟框架真的很棒。此刻,您唯一需要知道的(不知道在下一版本中是否会更好)是您要操作的目标字段/方法的名称及其签名。

评论


虽然反射使您可以测试私有方法,但是对于检测私有方法的使用却不利。如果您更改私有方法的名称,这将破坏您的测试。

–理查德
2012年11月27日10:42

测试将失败,但是从我的角度来看这是一个小问题。我完全同意您在以下有关程序包可见性的解释(对于单独的文件夹中的测试类,但程序包相同),但有时您不这样做真的有选择。例如,如果您在企业中的使命很短,没有应用“好的”方法(测试类不在同一包中,等等),而您又没有时间重构您正在使用的现有代码/测试,这仍然是一个不错的选择。

– Kij
2012年11月27日在11:14

#9 楼

我通常在C#中所做的是使我的方法受保护而不是私有的。
它是私有访问修饰符,但是它对所有未从被测类继承的类隐藏了该方法。

public class classUnderTest
{
   //this would normally be private
   protected void methodToTest()
   {
     //some code here
   }
}


任何此类不能直接从classUnderTest继承,也不知道methodToTest甚至存在。在我的测试代码中,我可以创建一个特殊的测试类,该类可以扩展并提供对该方法的访问...

class TestingClass : classUnderTest
{
   public void methodToTest()
   {
     //this returns the method i would like to test
     return base.methodToTest();
   }
}


该类仅存在于我的测试项目中。 br />其唯一目的是提供对该单一方法的访问。
它使我可以访问大多数其他类没有的地方。

评论


protected是公共API的一部分,并受到相同的限制(永远不能更改,必须记录在案……)。在C#中,您可以将Internal与InternalsVisibleTo一起使用。

– CodesInChaos
2013年12月13日11:03



#10 楼

如果将单元测试放在要测试的类的内部类中,则可以轻松测试私有方法。使用TestNG,单元测试必须是带有@Test注释的公共静态内部类,如下所示:

@Test
public static class UserEditorTest {
    public void test_private_method() {
       assertEquals(new UserEditor().somePrivateMethod(), "success");
    }
}


由于它是内部类,因此可以调用private方法。

我的测试是从maven运行的,它会自动找到这些测试用例。如果您只想测试一门课程,可以做

$ mvn test -Dtest=*UserEditorTest


来源:http://www.ninthavenue.com.au/how-to-unit-test-私有方法

评论


您建议在已部署的程序包的实际代码中包括测试代码(和其他内部类)吗?

–user40980
13年4月11日在3:36

测试类是您要测试的类的内部类。如果您不希望部署这些内部类,则可以从包中删除* Test.class文件。

–罗杰·基伊斯
13年4月11日在7:49

我不喜欢这个选项,但是对于很多人来说,这可能是一个有效的解决方案,不值得一票。

– Bill K
13年5月8日在17:15

@RogerKeays:从技术上讲,当您进行部署时,如何删除此类“测试内部类”?请不要说您是手工切割的。

– gyorgyabraham
2013年12月13日上午10:54

我不会删除它们。是。我部署的代码永远不会在运行时执行。真的很糟糕。

–罗杰·基伊斯
2013年12月15日,1:14

#11 楼

本书第10章“类”>“类组织”>“封装”中的“清洁代码”部分暗示了对私有方法的测试是伪提议。


我们希望保留变量和实用程序函数private,但我们并不痴迷于此。有时我们需要制作一个变量或实用函数protected,以便可以通过测试对其进行访问。对我们来说,测试就是规则。如果同一包中的测试需要调用函数或访问变量,则将其设为protected或包范围。但是,我们将首先寻找一种维护隐私的方法。松散封装始终是最后的选择。


IMHO private方法只是部分职责实现的包装,它是在非私有方法中调用的。因此,如果对private方法的测试非常重要,则应扩大访问范围,以便像JUnit这样的单元测试框架可以在其上运行测试。否则,“测试” private方法的唯一方法实际上是对非private方法的测试,该方法称为private方法

此外,去年还删除了JMockit的Deencapsultion类。这有点证明“对私有方法的测试”是一个伪命题

#12 楼

在Swift或Objective-C中,您将该方法标记为@testable。编译器知道何时编译单元测试(因为最终产品不是应用程序或库,而是“单元测试”可执行文件),在这种情况下,允许调用标记为@testable的私有方法,就像它们是公共的一样。该系统的优点是,您的方法可以保持所需的私有性,除了单元测试(不适用常规规则)外,并且不需要任何代码更改或预处理器技巧或类似操作即可支持此方法。

我认为其他语言也具有类似的功能。