如何测试Java的变量和方法

欢迎进入Java社区论坛,与200万技术人员互动交流 >>进入

下面的代码示例演示了经过简化的模仿对象方法:

清单 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字节码技术。

[1][2]

世上没有绝望的处境,只有对处境绝望的人。

如何测试Java的变量和方法

相关文章:

你感兴趣的文章:

标签云: