各种杂七杂八问题
什么时候用PrepareForTest
- 当使用PowerMockito.whenNew方法时,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是需要mock的new对象代码所在的类。
- 当需要mock final方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是final方法所在的类。
- 当需要mock静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解@PrepareForTest里写的类是静态方法所在的类。
- 当需要mock私有方法的时候, 只是需要加注解@PrepareForTest,注解里写的类是私有方法所在的类
- 当需要mock系统类的静态方法的时候,必须加注解@PrepareForTest和@RunWith。注解里写的类是需要调用系统方法所在的类
PowerMock无法mock掉spring的PostContruct
假如你的postConstruct中含有如下代码:
@PostContruct
public void init() {
XRepository.query();
}
你会发现你没法mock掉XRepository的行为,这是因为引入spring体系后,最典型的是比如使用@Sql注解的时候,会引入一些TestExecutionListener,这些spring专用的listener会要求spring进行上下文初始化,要求bean的初始化,而这些初始化可能在@BeforeClass之前,@Before之前,测试类的静态代码块之前,导致你没有办法通过常规的junit的常规手段将postConstruct中的内容mock掉。
在经过很多资料搜索后,此处使用的办法是使bean在初始化的过程中无法执行postConstruct方法,直接断其根源,一旦spring尝试调用postConstruct方法,就返回一个假的方法调用,而不是尝试对postConstruct代码块中一些需要mock的元素作提前mock。
package com.vip.spring;
import lombok.SneakyThrows;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MockitoSpyBeanPostProcessor implements BeanPostProcessor {
@SneakyThrows
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 用于忽略postConstruct注解
Class<?> userClass = ClassUtils.getUserClass(bean);
Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(userClass);
List<Method> collect = Arrays.stream(allDeclaredMethods).filter(method -> AnnotationUtils.findAnnotation(method, PostConstruct.class) != null).collect(Collectors.toList());
if (CollectionUtils.isEmpty(collect)) {
return bean;
}
Object spy = PowerMockito.spy(bean);
Method postConstructMethod = collect.get(0);
PowerMockito.doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return null;
}
}).when(spy, postConstructMethod.getName());
return spy;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
这样这个testCase不就变成一个goodCase了吗?
记一次单元测试 java.lang.ClassCastException: com.sun.crypto.provider.HmacSHA1 cannot be cast to javax.crypto.MacSpi
在用单元测试Junit测试部门的SDK时,有个md5鉴权步骤,出现了java.lang.ClassCastException: com.sun.crypto.provider.HmacSHA1 cannot be cast to javax.crypto.MacSpi错误,经排查是Junit编写问题,在@Test下面新增@PowerMockIgnore("javax.crypto.*")即可成功通过测试!