除访问私有变量,我们也可以通过这个方法访问私有方法。

清单 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, InvocationTargetException { 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 应用服务器的对象,通常的测试用例需要安装、配置和发送本地数据库副本、运行测试然后再卸装本地数据库或者需要安装、配置应用服务器、运行测试然后再卸装 应用服务器,操作可能很麻烦,.模仿对象提供了解决这一困难的途径。对于既需要访问相关环境又要访问非公有变量或方法的类来说,模仿对象非常适合,但是, 如果只是访问非公有变量或方法,那么传统的模仿对象法显得有些笨重,可以对该法进行简化,不使用工厂方法,达到同样的效果。

42/4<1234>