PowerMock文档翻译-绕过封装:白名单
绕过封装
概要
Whitebox提供了一些方法来帮助你绕过封装。通常,不推荐修改一个非公共字段,但是有时候为了更好的进行优化,我们必须通过这种方式进行测试。
- 使用
Whitebox.setInternalState(..)
给类或者类的实例的非公共成员变量设置值。 - 使用
Whitebox.getInternalState(..)
获得类或者类的实例的非公共成员变量的值。 - 使用
Whitebox.invokeMethod(..)
调用类或者类的实例的非公共方法。 - 使用
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);
这种一个更加类型安全的方式,但是加入类中存在多个相同类型的变量,你还是只能通过属性名来进行操作。