【问题标题】:Access private method of an instance in unit test在单元测试中访问实例的私有方法
【发布时间】:2019-05-17 14:04:27
【问题描述】:

我在尝试对代码进行单元测试时遇到了以下问题。如果我有一个创建实例的类,例如像这样的 getter 方法:

public class Test {

   private static Test instance;
   private ArrayList<String> arrayList = new ArrayList<String>();

   public  static Test getInstance() {
      return instance;
   }

   private ArrayList<String> getArrayList() {
      return arrayList;
   }
}

如果现在我想在测试用例中访问 arrayList,它将失败,因为该列表是由不可访问的私有方法返回的。所以尝试这样的事情是行不通的:

public class AccessTest {

   private Test test;

   public void accessList(){
      test = Test.getInstance();
      test.getArrayList();
   }
}

因此,无论如何访问 arrayList 的一种方法可能是将可见性更改为受保护。但是没有更好的方法来访问该方法吗?是否真的有必要仅因为需要访问它的测试而使方法受到保护?

【问题讨论】:

  • 或包私有。一般来说,您宁愿测试公共 api。如果没有,请使用反射 API 访问私有字段。
  • getArrayList() 方法设为私有的用例是什么?它只能在类中使用,而不是直接使用arrayList字段对吗?
  • 不确定是否真的重复,但我的这个answer 提供了一种可能的方法。
  • 单元测试是关于单独测试一个单元的行为(一个单元不一定是一个类!),而不是测试它的方法。出于这个原因,通过私有方法测试某些东西没有多大意义,行为是通过(在你的情况下是隐式的)类接口暴露的,所以只有公共方法。一个更现实的例子会提出一个更好的问题。

标签: java


【解决方案1】:

一般来说,如果你的类中有一些私有方法并且你觉得你在测试它们时遇到了问题,这表明你有一点代码异味。这表明私人墙后面隐藏了太多功能。

您可以将此类方法的可见性更改为 package protected,这样 JUnit 测试就会看到它。还有一个 Google Guava 注释 @VisibleForTesting 或类似的东西。但同样 - 这是错误类设计的标志。

考虑将此类方法提取到单独的类中,然后将这些方法公开。

例如,看看下面的代码:

class ReportCreator {
  public File createSomeImportantReport(LocalDate date) {
    String fileName = provideFileName(date);
    File result = new File(fileName);
    return result;
  }

  private String provideFileName(LocalDate date) {
    // ... some complex business logic to generate file name based on date... ;)

    return fileName;
  }
}

有一个私有方法provideFileName() 可以做一些复杂的事情,假设你只测试createSomeImportantReport() 很难测试。

如果您将该功能外部化,看看会发生什么变化。

class ReportCreator {
  private FileNameProvider fileNameProvider;

  public File createSomeImportantReport(LocalDate date) {
    File result = new File(fileNameProvider.provideFileName(date));
    return result;
  }
}

class FileNameProvider {
  public String provideFileName(LocalDate date) {
    return ......;
  }
}

您现在可以选择单独测试该内容,专注于在特定情况下重要的内容。

【讨论】:

  • 我大体上同意这个说法,但是在某些情况下,您需要测试私有代码并为了测试而重新设计东西并没有多大意义。 @VisibleForTesting 存在是有原因的。
  • @uaraven 你能举出任何你真正需要测试私有方法的例子吗?那里的代码必须从某个公共/受保护的方法执行,否则它没有理由存在。
【解决方案2】:

尽管我没有看到私有 getter 的用例,但您可以使用包私有访问级别。这是默认访问级别,因此您不必指定它。然后,您可以通过在测试目录中的相同包名中添加测试类来对其进行测试。例如类位于src/main/java/application,然后测试类可以位于src/test/java/application

【讨论】:

  • '我没有看到私有 getter 的用例' - 非常正确。
【解决方案3】:

为此使用 Java 反射:

Test test = new Test();

Method getArrayListMethod = test.getClass().getDeclaredMethod("getArrayList", null);

getArrayListMethod.setAccessible(true);

ArrayList<String> list = (ArrayList<String>) getArrayListMethod .invoke(test);

System.out.println(list); // Prints the list

创建您的 Test 对象,使用方法 getClass() 并通过其名称获取在该类上声明的方法。 然后设置该方法可动态访问。如果您知道它返回的数据类型,则将其转换为它。

【讨论】:

  • -1,这是个糟糕的主意。您不应该使用反射来测试它们,这是您过度拟合测试的标志。这将使重构成为绝对的噩梦。例如。对方法的软引用 -> 运行时“方法未找到异常”,但一切编译正常。
猜你喜欢
  • 1970-01-01
  • 2014-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-07
相关资源
最近更新 更多