首页 如何测试Java的变量和方法

如何测试Java的变量和方法

举报
开通vip

如何测试Java的变量和方法如何测试Java的变量和方法 浏览:1 | 更新:2013-05-20 14:31 对于软件开发人员来说,单元测试是一项必不可少的工作。它既可以验证程序的有效性,又可以在程序出现BUG 的时候,帮助开发人员快速的定位问题所在。但是,在写单元测试的过程中,开发人员经常要访问类的一些非公有的成员变量或方法,这给测试工作带来了很大的困 扰。本文总结了访问类的非公有成员变量或方法的四种途径,以方便测试人员在需要访问类非公有成员变量或方法时进行选择。 尽管有很多经验丰富的程序员认为不应该提倡访问类的私有成员变...

如何测试Java的变量和方法
如何测试Java的变量和 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 浏览:1 | 更新:2013-05-20 14:31 对于软件开发人员来说,单元测试是一项必不可少的工作。它既可以验证程序的有效性,又可以在程序出现BUG 的时候,帮助开发人员快速的定位问题所在。但是,在写单元测试的过程中,开发人员经常要访问类的一些非公有的成员变量或方法,这给测试工作带来了很大的困 扰。本文总结了访问类的非公有成员变量或方法的四种途径,以方便测试人员在需要访问类非公有成员变量或方法时进行选择。 尽管有很多经验丰富的程序员认为不应该提倡访问类的私有成员变量或方法,因为这样做违反了 Java 语言封装性的基本 规则 编码规则下载淘宝规则下载天猫规则下载麻将竞赛规则pdf麻将竞赛规则pdf 。然而,在实际测试中被测试的对象千奇百怪,为了有效快速的进行单元测试,有时我们不得不违反一些这样或那样的规则。本文只讨论如何 访问类的非公有成员变量或方法,至于是否应该在开发测试中这样做,则留给读者自己根据实际情况去判断和选择。 方法一:修改访问权限修饰符 先介绍最简单也是最直接的方法,就是利用Java 语言自身的特性,达到访问非公有成员的目的。说白了就是直接将private 和protected 关键字改为public 或者直接删除。我们建议直接删除,因为在Java 语言定义中,缺省访问修饰符是包可见的。这样做之后,我们可以另建一个源码目录-- test 目录(多数IDE 支持这么做,如Eclipse 和JBuilder),然后将测试类放到test 目录相同包下,从而达到访问待测类的成员变量和方法的目的。此时,在其它包的代码依然不能访问这些变量或方法,在一定程度上保障了程序的封装性。 下面的代码示例展示了这一方法。 清单1. 原始待测类A 代码 public class A { private String name = null; private void calculate() { }} 清单2. 针对单元测试修改后的待测类A 的代码 public class A { String name = null; private void calculate() { }} 这种方法虽然看起来简单粗暴,但经验告诉我们这个方法在测试过程中是非常有效的。 当然,由于改变了源代码,虽然只是包可见,也已经破坏了对象的封装性,对于多数对代码安全性要求严格的系统此方法并不可取。 方法二:利用安全管理器 安全性管理器与反射机制相结合,也可以达到我们的目的。Java 运行时依靠一种安全性管理器来检验调用代码对某一特定的访问而言是否有足够的权限。具体来说,安全性管理器是java.lang.SecurityManager 类或扩展自该类的一个类,且它在运行时检查某些应用程序操作的权限。换句话说,所有的对象访问在执行自身逻辑之前都必须委派给安全管理器,当访问受到安全 性管理器的控制,应用程序就只能执行那些由相关安全策略特别准许的操作。因此安全管理器一旦启动可以为代码提供足够的保护。默认情况下,安全性管理器是没 有被设置的,除非代码明确地安装一个默认的或定制的安全管理器,否则运行时的访问控制检查并不起作用。我们可以通过这一点在运行时避开Java 的访问控制检查,达到我们访问非公有成员变量或方法的目的。为能访问我们需要的非公有成员,我们还需要使用Java 反射技术。Java 反射是一种强大的工具,它使我们可以在运行时装配代码,而无需在对象之间进行源代码链接,从而使代码更具灵活性。在编译时,Java 编译程序保证了私有成员的私有特性,从而一个类的私有方法和私有成员变量不能被其他类静态引用。然而,通过Java 反射机制使得我们可以在运行时查询以及访问变量和方法。由于反射是动态的,因此编译时的检查就不再起作用了。 下面的代码演示了如何利用安全性管理器与反射机制访问私有变量。 清单3. 利用反射机制访问类的成员变量 //获得指定变量的值public static Object getValue(Object instance, String fieldName) throws IllegalAccessException, NoSuchFieldException { Field field = getField(instance.getClass(),fieldName); // 参数值为true,禁用访问控制检查field.setAccessible(true); return field.get(instance); } //该方法实现根据变量名获得该变量的值public static Field getField(Class thisClass, String fieldName) throws NoSuchFieldException { if (thisClass == null) { throw new NoSuchFieldException("Error field !"); }} 其中getField(instance.getClass(),fieldName) 通过反射机制获得对象属性,如果存在安全管理器,方法首先使用this 和Member.DECLARED 作为参数调用安全管理器的 checkMemberAccess 方法,这里的this 是this 类或者成员被确定的父类。如果该类在包中,那么方法还使用包名作为参数调用安全管理器的 checkPackageAccess 方法。每一次调用都可能导致SecurityException.当访问被拒绝时,这两种调用方式都会产生 securityexception 异常 . setAccessible(true) 方法通过指定参数值为true 来禁用访问控制检查,从而使得该变量可以被其他类调用。我们可以在我们所写的类中,扩展一个普通的基本类 java.lang.reflect.AccessibleObject 类。这个类定义了一种setAccessible 方法,使我们能够启动或关闭对这些类中其中一个类的实例的接入检测。这种方法的问题在于如果使用了安全性管理器,它将检测正在关闭接入检测的代码是否允许 这样做。如果未经允许,安全性管理器抛出一个例外。 除访问私有变量,我们也可以通过这个方法访问私有方法。 清单4. 利用反射机制访问类的成员方法 public static Method getMethod(Object instance, String methodName, Class[] classTypes) throws NoSuchMethodException { Method accessMethod = getMethod(instance.getClass(),methodName, classTypes); //参数值为true,禁用访问控制检查accessMethod.setAccessible(true); return accessMethod; }private static Method getMethod(Class thisClass, String methodName, Class[] classTypes) throws NoSuchMethodException { if (thisClass == null) { throw new NoSuchMethodException("Error method !"); } try { return thisClass.getDeclaredMethod(methodName, classTypes); } catch (NoSuchMethodException e) { return getMethod(thisClass.getSuperclass(), methodName, classTypes); }} 获得私有方法的原理与获得私有变量的方法相同。当我们得到了函数后,需要对它进行调用,这时我们需要通过 invoke() 方法来执行对该函数的调用,代码示例如下: //调用含单个参数的方法public static Object invokeMethod(Object instance, String methodName, Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Object[] args = new Object[1]; args[0] = arg; return invokeMethod(instance, methodName, args); } //调用含多个参数的方法public static Object invokeMethod(Object instance, String methodName, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationT argetException { Class[] classTypes = null; if (args != null) { classTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] != null) { classTypes[i] = args[i].getClass(); } } } return getMethod(instance, methodName, classTypes)。invoke(instance, args); } 利用安全管理器及反射,可以在不修改源码 的基础上访问私有成员,为测试带来了极大的方便。尤其是在编译期间,该方法可以顺利地通过编译。但同时该方法也有一些缺点。第一个是性能问题,用于字段和 方法接入时反射要远慢于直接代码。第二个是权限问题,有些涉及Java 安全的程序代码并没有修改安全管理器的权限,此时本方法失效。 方法三:使用模仿(Mock)对象 在 单元测试的过程中模仿对象被广泛使用。它从测试中分离了外部的不需要的因素,并且帮助开发人员专注于被测试的功能。模仿对象(Mock object)的核心是构造一个伪类,在测试中通常用这个构造的伪类替换原来的需要访问相关环境(如应用服务器,数据库等)的需要测试的待测类,这样单元 测试便可以运行在本地环境下(这也是对单元测试的基本要求之一,不依赖于任何特定的环境),并可以正确的执行。此外,由于Java 语言不能多继承的特性,使得该方法也可以被用来作为非公有成员变量及方法的访问方法(测试类不能同时继承TestCase 和待测类),利用该方法,在模仿对象中改变类成员的访问控制权限,从而达到访问非公有类变量及方法的目的。 下面的代码示例演示了模仿对象方法。 本方法的应用场景在单元测试中非常常见,即在待测试的公有方法中,有一些受限制的成员变量是由其它私有方法来初始化的,在测试该方法的时候,需要给这个变量置初值才能完成测试。 清单5. 待测类A public class A { protected String s = null; public A() { } private void method() { s = "word"; System.out.println("this is mock test"); } public void makeWord() { String prefix = s; System.out.println("prefix is:" + prefix); }} 在待测类A 中,增加工厂方法。 清单6. 包含工厂方法的待测类A // 增加工厂方法的类Apublic class A { protected String s = null; public A getA() { return new A(); } private void method() { s = "word"; System.out.println("this is mock test"); } public void makeWord() { String prefix = s; System.out.println("prefix is:" + prefix); }}//伪类,在运行时替换类Apublic class MockA extends A{ public String s = null; public MockA(){ }}//测试类public class TestA extends TestCase{ public void setup(){ } public void teardown(){ } public void makeWordTest(){ A a = new MockA(); a.s = "test"; a.makeWord(); }} 此方法中有几个值得注意的地方,首先是将 创建代码抽取到工厂方法中,在测试子类中覆盖该工厂方法,然后令被覆盖的方法返回模仿对象。如果可以的话,添加需要原始对象的工厂方法的单元测试,以返回 正确类型的对象。模仿对象方法在处理许多对象依赖基础结构的其它对象或层时, 可以起到很好的效果。模仿对象符合实际对象的接口,但只要有足够的代码来"欺骗"测试对象并跟踪其行为。例如, 在单元测试中需要测试一个使用数据库的对象,或者需要测试连接J2EE 应用服务器的对象,通常的测试用例需要安装、配置和发送本地数据库副本、运行测试然后再卸装本地数据库或者需要安装、配置应用服务器、运行测试然后再卸装 应用服务器,操作可能很麻烦,.模仿对象提供了解决这一困难的途径。对于既需要访问相关环境又要访问非公有变量或方法的类来说,模仿对象非常适合,但是, 如果只是访问非公有变量或方法,那么传统的模仿对象法显得有些笨重,可以对该法进行简化,不使用工厂方法,达到同样的效果。 下面的代码示例演示了经过简化的模仿对象方法: 清单7. 简化的待测类A 的模仿对象 //伪类,在运行时替换类Apublic class MockA extends A{ public MockA(){ super(); s = "test"; }}//测试类public class TestA extends TestCase{ public void setup(){ } public void teardown(){ } public void makeWordTest(){ A a = new MockA(); a.makeWord(); }} 模仿对象方法既能消除运行环境的影响,又能解决多继承的难题,但是由于该方法使用子类的实例来替代父类的实例,对于私有成员变量及方法来说,仍然不能进行访问。 方法四:利用字节码技术 Java 编译器把Java 源代码编译成字节码 bytecode(字节码),既然在测试中尽量要避免改变原来的代码,那么最直接的改造Java 类的方法莫过于直接改写class 文件。通过修改字节码中的关键字,将私有的成员变量及方法改成公有的成员变量及方法,可以做到在不改变源码的情况下访问到需要的成员变量及方法。Java 规范有 class 文件的格式的详细说明,直接编辑字节码确实可以改变Java 类的行为,但是这也要求使用者对Java class 文件有较深的理解。目前,比较流行的字节码处理工具有Javassist,BCEL 和ASM 等。这几种工具各有特点,适合于不同的应用场景,如果读者对字节码技术感兴趣,可以阅读后面的参考文献。本文选择利用字节码工具ASM. ASM 能被用来动态生成类或者修改既有类的功能。它可以直接产生二进制class 文件,也可以在类被加载入Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类(。class)。ASM 作为Java 字节码操控框架,是所有同类工具中效率最高的一个,并且由于其采用了基于Vistor 模式的框架设计,它也是同类工具中最轻巧灵活的,尽管它的学习台阶相对要高一些,它仍然是达到本文目的的首选。 利用ASM 访问私有变量及方法,需要了解的比较重要的几个类:ClassReader、ClassVistor、MethodVisitor、FieldVisitor 和 ClassAdaptor 等。ClassReader 类可以直接由字节数组或由class 文件间接的获得字节码数据,它能正确的分析字节码,通过调用 accept 方法接受一个ClassVisitor 接口的实现类实例作为参数,然后依次调用ClassVisitor 接口的各个方法;ClassVisitor 接口中定义了对应Java 类各个成员的访问函数,比如visitMethod 会返回一个实现MethordVisitor 接口的实例,visitField 会返回一个实现FieldVisitor 接口的实例。不同Visitor 的组合,可以非常简单的封装对字节码的各种修改;ClassAdaptor 类为 ClassVisitor 接口提供了一个默认实现。创建一个ClassAdaptor 对象实例时,需要传入一个ClassVisitor 接口的实现类实例来访问字节吗。因此当我们需要对字节码进行调整时,只需从ClassAdaptor 类派生出一个子类,覆写需要修改的方法,完成相应功能后再把调用传递到下一个需要修改的visitor 即可。 本例的应用场景为,要对公有方法method() 进行单元测试,但是,该方法中有一个私有变量number 是由另一个私有方法makePaper() 付值,所以,需要在测试中为该私有变量置初值。 清单8. 待测类A class A{ private String number = ""; public void method() { if(number.eaquals("prefix")) System.out.println("method…"+number); else System.out.println(number +"is null"); } private void makePaper() { number="prefix"; System.out.println("makePaper…"); }} 清单9. 使用字节码访问类A //修改变量的修饰符public class AccessClassAdapter extends ClassAdapter { public AccessClassAdapter(ClassVisitor cv) { super(cv); } public FieldVisitor visitField(final int access, String name, final String desc, final String signature, final Object value) { int privateAccess = access; //找到名字为number的变量if (name.equals("number")) privateAccess = Opcodes.ACC_PUBLIC; //修字段的修饰符为public:在职责链传递过程中替换调用参数return cv.visitField(privateAccess, name, desc, signature, value); } public static void main(String[] args) throws Exception { ClassReader cr = new ClassReader("A"); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassAdapter classAdapter = new AccessClassAdapter(cw); cr.accept(classAdapter, ClassReader.SKIP_DEBUG); byte[] data = cw.toByteArray(); //生成新的字节码文件File file = new File("A.class"); FileOutputStream fout = new FileOutputStream(file); fout.write(data); fout.close(); }} 执行完该类,将产生一个新的A.class 文件。 测试类测试method 方法,先对变量进行置初值,然后就可以像其他单元测试一样,对 method 方法进行测试。 回页首 方法对比 方法修饰符使用难度缺陷 protected 缺省private 方法一:修改访问权限修饰符是是是低,有java编程基础即可。 由于需要修改源代码,虽然是同包可见,也会带来一些封闭性的问题。 方法二:利用安全性管理器是是是中,需要了解java安全性管理器及反射机制。一些对代码安全有要求的程序,程序员并没有修改security manager的权限,此时,安全管理器方法失效。 方法三:使用模仿对象是是否较高,需要了解设计模式和待测对象的内部实现细节。由于模仿对象要求伪类必需和待测类是继承与被继承的关系,所以当源码以private关键字修饰时,此方法失效。 方法四:利用字节码技术是是是高,需要操作和改写类部分的字节码。 学习成本高,需要了解Java字节码技术
本文档为【如何测试Java的变量和方法】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_348501
暂无简介~
格式:doc
大小:35KB
软件:Word
页数:0
分类:互联网
上传时间:2019-05-19
浏览量:2