[深入JUnit] 为什么别测试private函数

news/2024/7/2 23:44:33 标签: 测试, java

比如说,Bird是我们要测试的class,它有public, protected,以及private的方法。

// 文件位置:src/test/sample/Bird.java
package test.sample;
class Bird {
  public void fly() { ... }
  public void eat() { ... }
  protected void singRandomly() { 
    final Song s = findASong(<some_random_number>);
    singASong(s);
  }
  
  private Song findASong() { ... }
  private void singASong() { ... }
}

现在有一个BirdTest class。对这个class而言,它可见的所有函数,是Bird.class.getDeclaredMethods()的返回值。

代码细节请看
junit.internal.MethodSorter#getDeclaredMethods()
http://grepcode.com/file/repo1.maven.org/maven2/junit/junit/4.12/org/junit/internal/MethodSorter.java#MethodSorter.getDeclaredMethods%28java.lang.Class%29
以及java.lang.Class#getDeclaredMethods()
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/java/lang/Class.java#Class.getDeclaredMethods%28%29

所有的public, protected, private方法BirdTest都能看到。但是,看到不等于能调用

// 文件位置:tst/test/sample/BirdTest.java
package test.sample;
class BirdTest {
  @Test
  public void testFly_CaseDescription1() {
    ...
    bird.fly(); //当然ok,因为Bird#fly是public的
  }

  @Test
  public void testSingRandomly_CaseDescription1() {
    ... 
    bird.sing(); //ok,因为BirdTest也在test.sample package下面。否则是非法的。
  }

  @Test
  public void testFindASong() {
    ...
    bird.findASong(); // 非法,不能调用Bird的私有函数。
  }
}

在上面的代码里,由于BirdTestBird在一个package test.sample里,所以Bird所有的publicprotected函数,对BirdTest可见。但是,private应该是不可调用的。

当然,有人会告诉你如何利用java reflection的API来让private method也可以调用

// 无关紧要的parameter用 '_' 略去了
Method findASong = targetClass.getDeclaredMethod("findASong", _);
findASong.setAccessible(true);
return findASong.invoke(_, _);

但是,这打破了Bird类的封装,是非常不好的。设想,改动private的方法的声明会造成test failure,那么private就失去意义了。与protected其实区别不大。

那么应该怎么做呢?

  • 不去测试private函数,好的private函数都应该是很小很简单的,测试那调用了private函数的public和protected方法即可。

  • 或者,也许这个private函数其实应该被声明称protected。

如果以上方法你都觉得不合适,而某一个private函数很复杂,很需要测试。那么,根据Single Responsibility原则,这个private函数就应该被放到一个单独的class里面。

class BirdSongs {
  protected Song findASong(Integer id) { ... }
  protected void singASong(Song s) { ... }
}

然后,对BirdSongs#findASong进行测试

如果您有不同意见,欢迎与我讨论。


http://www.niftyadmin.cn/n/999148.html

相关文章

读书笔记:《算法图解》第三章 递归

定义&#xff1a; 在数学与计算机科学中&#xff0c;是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。例如&#xff0c;当两面镜子相互之间近似平行时&#xff0c;镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过…

Android实战技巧:为从右向左语言定义复杂字串

我们所使用的语言&#xff0c;无论是中文还是英语&#xff0c;都是从左向右读的(Left-To-Right)&#xff0c;因此我们已经十分习惯了从左向右读(Left-To-Right)。一旦遇到了从右向左读的(Right-To-Left)&#xff0c;便会十分的不习惯&#xff0c;甚至比见到火星文还难受。光看着…

apache配置httpd-vhosts

1、打开 bin\apache\apache2.2.8\httpd.conf 中去掉 Include conf/extra/httpd-vhosts.conf前的#号2、打开 conf/extra/httpd-vhosts.conf 中添加 NameVirtualHost *:80之后根据项目依照下面的格式来修改<VirtualHost *:80> ServerAdmin webmasterdummy-host.example.…

C# winfrom gridview常用知识

在WinForm开发中,DataGrid/DataGridView被广泛使用于绑定数据库中数据进行呈现.整理一些关于DataGrid/DataGridView使用的文章,涉及DataGrid/DataGridView基本功能,自定义绘制控件,数据导入/导出(Excel),打印等&#xff1a; http://www.cnblogs.com/peterzb/archive/2009/05/29…

Activity中的四种启动模式

在Android中每个界面都是一个Activity&#xff0c;切换界面操作其实是多个不同Activity之间的实例化操作。在Android中Activity的启动模式决定了Activity的启动运行方式。 Android总Activity的启动模式分为四种&#xff1a; [html] view plaincopy Activity启动模式设置&#x…

文件IO操作的最佳实践

知识点梳理 本文主要关注的 Java 相关的文件操作&#xff0c;理解它们需要一些前置条件&#xff0c;比如 PageCache&#xff0c;Mmap(内存映射)&#xff0c;DirectByteBuffer(堆外缓存)&#xff0c;顺序读写&#xff0c;随机读写…不一定需要完全理解&#xff0c;但至少知道它…

OpenStack 企业私有云的若干需求(6):大规模扩展性支持

本系列会介绍OpenStack 企业私有云的几个需求&#xff1a; 自动扩展&#xff08;Auto-scaling&#xff09;支持多租户和租户隔离 &#xff08;multi-tenancy and tenancy isolation&#xff09;混合云&#xff08;Hybrid cloud&#xff09;支持主流硬件支持、云快速交付 和 SLA…

Linux 系统文件夹结构

登录后&#xff0c;在当前的命令窗口中输入命令&#xff1a; <span style"font-size:14px;">ls /</span>你的终端上会出现例如以下的代码&#xff1a; <span style"font-size:14px;">dengdeng-Lenovo:~/文档$ ls / bin dev initrd.…