相关工具推荐:
TestMe:用于快速生成测试类
一、抽象一个公共类,将TestMe的配置内容该类MockTest
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.lang.reflect.Method;
/**
* mock测试
*/
@RunWith(MockitoJUnitRunner.class)
@Slf4j
public abstract class MockTest {
@Before
public void init() {
//公用设置
Snowflake.init("127.0.0.1", 8080, true);
}
/**
* 当代码中有需要访问某一个文件时,生成一个临时文件
* @return
*/
public String getTmpFilePath(){
try {
File mockFile = File.createTempFile("mock", ".txt");
return mockFile.getPath();
}catch (Exception e){
log.error("file create error",e);
return "";
}
}
public File getTmpFile(){
try {
File mockFile = File.createTempFile("mock", ".txt");
return mockFile;
}catch (Exception e){
log.error("file create error",e);
return null;
}
}
/**
* 当私有方法较大,正常逻辑不好覆盖时,单测私有方法
* @param obj
* @param cla
* @param instance
* @param methodName
* @param paramClass
*/
public void invokeMethod(Object obj, Class cla, Object instance, String methodName,Class paramClass) {
try {
getMethod(methodName, cla,paramClass).invoke(instance, obj);
//模拟失败
getMethod(methodName, cla,paramClass).invoke(cla.newInstance(), obj);
} catch (Exception e) {}
}
private Method getMethod(String methodName, Class cla, Class paramClass) throws Exception {
Method method = cla.getDeclaredMethod(methodName, paramClass);
method.setAccessible(true);
return method;
}
}
二、配置TestMe Templates
代码:
#parse("TestMe macros.java")
#set($hasMocks=$MockitoMockBuilder.hasMockable($TESTED_CLASS.fields))
#if($PACKAGE_NAME)
package ${PACKAGE_NAME};
#end
import static org.junit.Assert.*;
import org.junit.Test;
#if($hasMocks)
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.junit.Assert;
import com.替换路径.MockTest
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
#end
#parse("File Header.java")
@RunWith(MockitoJUnitRunner.class)
public class ${CLASS_NAME} extends MockTest{
#renderMockedFields($TESTED_CLASS.fields)
#renderTestSubjectInit($TESTED_CLASS,$TestSubjectUtils.hasTestableInstanceMethod($TESTED_CLASS.methods),$hasMocks)
#if($hasMocks)
@Before
public void setUp() {
MockitoAnnotations.${MockitoMockBuilder.initMocksMethod}(this);
}
#end
#foreach($method in $TESTED_CLASS.methods)
#if($TestSubjectUtils.shouldBeTested($method))
@Test
public void #renderTestMethodName($method.name)(){
#if($MockitoMockBuilder.shouldStub($method,$TESTED_CLASS.fields))
#renderMockStubs($method,$TESTED_CLASS.fields)
#end
#renderMethodCall($method,$TESTED_CLASS.name)
Assert.assertNotNull(new Object());
}
#end
#end
}
三、代码覆盖率提升技巧
针对所有的bean和枚举进行单测覆盖,可通用配置
import org.junit.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* @author awen
**/
public class CommonBeanTest {
String CLASSPATH_ALL_URL_PREFIX = "classpath:";
@Test
public void beanTest() throws Exception {
List<String> list = new ArrayList();
list.add("com.xxx.bean");
list.add("com.xxx.api");
for (String path : list) {
// 包名转路径
path = path.replaceAll("\\.", "\\/");
String packageSearchPath = CLASSPATH_ALL_URL_PREFIX + path + "/**/*.class";
// 获取所有的Pojo类
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
// 遍历
for (Resource r : resources) {
// 获取类名
String name = r.getURL().getPath();
name = name.substring(name.indexOf("com"), name.lastIndexOf("."));
name = name.replaceAll("/", ".");
// 获取类对象
Class c = this.getClass().getClassLoader().loadClass(name);
if(c.isEnum()){
enumCover(c);
}else if(!c.isInterface()){
copyBean(c);
}
}
}
}
private void copyBean(Class c) throws Exception {
try {
// 生成两个实例
Object dest = c.newInstance();
Object orig = c.newInstance();
// 测试toString方法
dest.toString();
// 测试Getter Setter方法
BeanUtils.copyProperties(dest, orig);
}catch (Exception e){
}
}
private void enumCover(Class c){
if(c.isEnum()){
Object[] enumConstants = c.getEnumConstants();
Object obj = enumConstants[0];
for(Object enumConstant:enumConstants) {
enumConstant.toString();
}
try{
Method method = c.getMethod("getByCode",String.class);
method.invoke(obj,obj.toString());
}catch (Exception e){
}
try{
Method method = c.getMethod("valueOf",String.class);
method.invoke(obj,obj.toString());
}catch (Exception e){}
}
}
}
其他技巧:
1、设置类中的私有变量:
ReflectionTestUtils.setField(被测试类实例,"变量",值);
2、实例化ApplicationContext,设置根据类型返回实例:
ApplicationContext applicationContext = Mockito.mock(ApplicationContext.class); Map<String, 抽象类> map = Maps.newHashMap(); map.put("1",实例); Mockito.when(applicationContext.getBeansOfType(抽象类.class)).thenReturn(map);
3、类中mock的方法时,如果有重载方法,可使用any(Class)转化为相关类型,再进行处理
when(类实例.方法(any(XXX.class),any(),any())).thenReturn(mock返回值);