PowerMock文档翻译-绕过封装:白名单

原文链接

绕过封装

概要

Whitebox提供了一些方法来帮助你绕过封装。通常,不推荐修改一个非公共字段,但是有时候为了更好的进行优化,我们必须通过这种方式进行测试。

  1. 使用Whitebox.setInternalState(..)给类或者类的实例的非公共成员变量设置值。
  2. 使用Whitebox.getInternalState(..)获得类或者类的实例的非公共成员变量的值。
  3. 使用Whitebox.invokeMethod(..)调用类或者类的实例的非公共方法。
  4. 使用Whitebox.invokeConstructor(..)可以为拥有私有构造函数的类创建实例。

示例

访问内部状态

当一个可变对象的方法被调用时,可能会改变其内部状态。当测试这类对象时,有一个很简单的方法可以察觉到其内部状态是否已经更新。PowerMock为此提供了几个反射工具类专门应用于单元测试。所有的反射工具类在包目录org.powermock.reflect.Whitebox下面。

为了进行演示,我们假设有这样一个类:

public class ServiceHolder {

	private final Set<Object> services = new HashSet<Object>();

	public void addService(Object service) {
		services.add(service);
	}

	public void removeService(Object service) {
		services.remove(service);
	}
}

假设我们想测试addService方法(可能会被认为这个示例太过简单了,但是不要介意,我们只是为了方便演示)。在这里假如我们想确认ServiceHolder在方法addService被调用后能正确更新其状态,也就是为services集合添加一个对象元素。可以通过为此类添加一个包私有或者protected的方法比如getService来获得services属性的值。但是这样做的话,我们想就相当于在ServiceHolder类中添加了一个除了测试没有其他作用的方法。这个方法也许会在其他地方被误用。另外一种选择是,可以在不改动代码的前提下使用Whitebox.getInternalState(..)来完成同样的事情,下面是示例:

@Test
public void testAddService() throws Exception {
	ServiceHolder tested = new ServiceHolder();
	final Object service = new Object();

	tested.addService(service);

  // 使用PowerMock访问内部变量
	Set<String> services = Whitebox.getInternalState(tested,"services");

	assertEquals("Size of the \"services\" Set should be 1", 1, services.size());
	assertSame("The services Set should didn't contain the expect service",
							    service, services.iterator().next());
}

使用PowerMock 1.0或者更高的版本可以通过指定字段类型获得内部变量。上面的案例我们还可以这样实现:

Set<String> services = Whitebox.getInternalState(tested, Set.class);

这种一个更加类型安全的方式,但是加入类中存在多个相同类型的变量,你还是只能通过属性名来进行操作。

调用私有方法

实例化私有构造函数的类

注意

参考代码

文章目录