在单元测试中访问实例的私有方法

时间:2019-05-17 14:04:27

标签: java

当我尝试对代码进行单元测试时,遇到了以下问题。如果我有一个创建实例的类,例如这样的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的一种方法可能是将可见性更改为protected。但是没有更好的方法来访问该方法吗?是否真的有必要仅由于需要访问的测试而使方法受保护?

3 个答案:

答案 0 :(得分:5)

通常,如果您的类中有一些私有方法,并且您觉得在测试它们时遇到问题,则表明它有点代码味道。它表明私人墙后隐藏了太多功能。

您可以将这种方法的可见性更改为受保护的软件包,因此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 ......;
  }
}

您现在可以选择单独测试该对象,集中精力处理特定情况下的重要内容。

答案 1 :(得分:2)

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

答案 2 :(得分:0)

为此使用Java Reflection:

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()并获取通过其名称在该类上声明的方法。 然后将该方法设置为可动态访问。如果您知道它返回的数据类型,则将其强制转换为它。