一次使用PowerMock的事故

项目结构:Spring boot+Dubbo+Zookeeper+PowerMock

为了在项目中更好地编写单元测试,在项目中引入了PowerMock做单元测试。

例子

先看一个正常例子:Spring boot+PowerMock

包依赖如下:

        testCompile "org.powermock:powermock-api-mockito2:1.7.1"
        testCompile "org.powermock:powermock-module-junit4:1.7.1"
        testCompile "org.mockito:mockito-core:2.8.47"
        testCompile "org.hamcrest:hamcrest-library:1.3"

Service服务,即我要测试的类和函数:

@Service
public class MyService {
    
    @Autowired
    private MyRepository myRepository;

    public Boolean needPopupWindow(Long id) {
        // ... 省略其他

        return myRepository.count(myExample) > 0;
    }

    // ... 省略其他
}

在其内部调用了 MyRepository的函数

public interface MyRepository {

    long count(MyExample myExample);// 利用PowerMock 屏蔽该函数
    // ... 省略其他
}

现在我要测试 MyService的 needPopupWindow(Long id)函数,单测如下:

@RunWith(PowerMockRunner.class)
@ContextConfiguration(classes = {MyApplicationContext.class})
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PowerMockIgnore({"javax.management.*"}) //忽略一些mock异常
public class MallInsuranceServiceMockTest {

    @InjectMocks  //把被模拟对象注入
    @Autowired
    private MyService myService;

    @Mock // 修改、屏蔽其某些行为
    private MyRepository myRepository;

    @Before
    public void setUp() {
        ReflectionTestUtils.setField(myService, "myRepository", myRepository);//修改注入
    }

    @Test
    public void should_get_true_when_service_given_Id() {
        doReturn(1).when(myRepository).count(ArgumentMatchers.any(MyExample.class));

        Boolean isShow = myService.needPopupWindow(2L);

        assertThat(isShow, is(true));
    }
}

此时,PowerMock工作的很好。

问题

然后,当项目集成了Dubbo和Zookeeper后,该单元测试就不能运行了。报错如下:

2018-09-21 20:23:10,932 WARN  [main-SendThread(XXXXXXXXX:2181)] zookeeper.ClientCnxn$SendThread (ClientCnxn.java:1168) - Session 0x0 for server XXXXXXXX, unexpected error, closing socket connection and attempting reconnect
java.lang.ClassCastException: class sun.security.provider.ConfigFile
    at java.lang.Class.asSubclass(Class.java:3404) 
    at javax.security.auth.login.Configuration$2.run(Configuration.java:254) 
    at javax.security.auth.login.Configuration$2.run(Configuration.java:247) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at javax.security.auth.login.Configuration.getConfiguration(Configuration.java:246) 
    at org.apache.zookeeper.client.ZooKeeperSaslClient.<init>(ZooKeeperSaslClient.java:107) 
    at org.apache.zookeeper.ClientCnxn$SendThread.startConnect(ClientCnxn.java:1005) 
    at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1064) 

查看代码发现:

这里判断:sun.security.provider.ConfigFile 不是 javax.security.auth.login.Configuration 子类,抛出异常。

但是在JDK中,发现两个类是父子关系:

对比前后两次,集成Zookeeper后,使用了sun.security.provider.ConfigFile 来建立连接。

原因

然后,检查两个类的类加载器,发现如下,javax.security.auth.login.Configuration 由 MockClassLoader加载的

sun.security.provider.ConfigFile 由 ExtClassLoader 加载

即,关系如下,此时两个类不再是父子关系。

因此,clazz.isAssignableFrom(this) 就返回 false,导致抛出异常。

总结:PowerMock使用MockClassLoader加载了javax.* ,导致原有类父子关系发生变化。

那么问题来了,怎么不让PowerMock 加载 javax.* 呢?

思考

思考:既然Powermock加载了javax.security.auth.login.Configuration才导致的问题,那就能不能不加载它?
查看文档发现,powermock提供了这样的接口。利用注解@PowerMockIgnore 即可。源码如下:

/**
 * <p>
 * This annotation tells PowerMock to defer the loading of classes with the
 * names supplied to {@link #value()} to the system classloader.
 *  省略其他
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PowerMockIgnore {
    
    String[] value() default "";
    
    /**
     * Set to <code>true</code> if packages from configuration should merge with list of packages/classes from {@link #value()}.
     * Default value: <code>true</code>
     *
     * @return <code>true</code> if packages from configuration should merge with list of packages/classes from {@link #value()}
     * @since 1.7.0
     */
    boolean globalIgnore() default true;
}

即:这个注释告诉PowerMock延迟类的加载。

修改如下:

@PowerMockIgnore({"javax.management.*", "javax.security.*"}) //忽略一些mock异常

问题解决。

猜你喜欢

转载自blog.csdn.net/hustzw07/article/details/82807694