以交易核心trade举例
注意:xxx.ftl需要放在测试resources的template下
如果服务没有freemarker需要补充pom依赖
<dependency>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
<version>2.3.21version>
<scope>testscope>
dependency>
将想要写的单测和自己的名字输入执行
注意:如果是新服务导入需要对项目进行clean再编译,因为模板是读取target/test-classes/template/xxx.ftl
/**
* @ClassName UnitTestGen
* @Descriptioin
* @Author luoxiaoming
* @Date 2022/9/27 15:16
* @Version 1.0
*/
public class UnitTestGen {
public static void main(String[] args) throws Exception {
//根据类生成单测模板
UnitTestGen unitTestGen = new UnitTestGen();
unitTestGen.genUnitTestByClass(CcbLoanApplyServiceImpl.class, "author", GROOVY);
}
//groovy通用单测
public static final String GROOVY = "groovy";
//支持静态方法的groovy通用单测
public static final String STATIC_GROOVY = "staticGroovy";
//基础mockito单测
public static final String BASE_MOCKITO = "baseMockito";
//支持静态方法mockito单测
public static final String STATIC_MOCKITO = "staticMockito";
private void genUnitTestByClass(Class classBean, String author, String unitTestType) throws Exception {
//1. 创建FreeMarker的配置类
Configuration cfg = new Configuration(Configuration.getVersion());
//2. 指定模板加载器
// String templatePath = System.getProperty("user.dir")+"/trade/src/test/resources/";
String templatePath = parsePath(this.getClass().getResource("/template").getPath() + "/");
FileTemplateLoader fileTemplateLoader = new FileTemplateLoader(new File(templatePath));
cfg.setTemplateLoader(fileTemplateLoader);
//3. 获取模板
Template template = getUnitTestType(cfg, unitTestType);
//4. 构造数据模型
Map<String, Object> map = packageMapRes(classBean, author, getServerName(templatePath), unitTestType);
//5. 文件输出
/**
* 处理模型
* 参数一 数据模型
* 参数二 writer对象(FileWriter(文件输出),printWriter(控制台输出))
*/
template.process(map, new OutputStreamWriter(new FileOutputStream(new File((String) map.get("unitTestOutPutLocation"))), "UTF-8"));
// template.process(map, new PrintWriter(System.out));
System.out.println(String.format("单测地址-->%s", (String) map.get("unitTestOutPutLocation")));
}
/**
* 获取对应的单测模板
*
* @param unitTestType
* @return
*/
private Template getUnitTestType(Configuration cfg, String unitTestType) throws IOException {
Template template;
switch (unitTestType) {
case GROOVY:
template = cfg.getTemplate("GroovyUnitTestGen.ftl");
break;
case STATIC_GROOVY:
template = cfg.getTemplate("StaticGroovyUnitTestGen.ftl");
break;
case BASE_MOCKITO:
template = cfg.getTemplate("BaseMockitoUnitTestGen.ftl");
break;
case STATIC_MOCKITO:
template = cfg.getTemplate("StaticMockitoUnitTestGen.ftl");
break;
default:
throw new IllegalStateException("Unexpected value: " + unitTestType);
}
return template;
}
/**
* 比如 \E:\code\phoenix-trade\trade\target\test-classes\template\ 获取 trade
*
* @param templatePath
* @return
*/
private String getServerName(String templatePath) {
String serverName = "";
String[] split = templatePath.split("\\\\");
for (int i = 0; i < split.length; i++) {
if (split[i].equals("target")) {
serverName = split[i - 1];
}
}
return serverName;
}
private static Map<String, Object> packageMapRes(Class classObject, String author, String serverName, String unitTestType) {
Map<String, Object> map = new HashMap<>();
String simpleName = classObject.getSimpleName();
StringBuffer sbName = new StringBuffer();
sbName.append(simpleName.substring(0, 1).toLowerCase()).append(simpleName.substring(1));
List<String> methodNameList = new ArrayList<>();
//获取所有的setField 和 getField
List<String> filterMethod = getfilterMethod(classObject);
//过滤关键字 只要public
Arrays.stream(classObject.getDeclaredMethods()).filter(t -> t.getModifiers() == 1 && !filterMethod.contains(t.getName())).forEach(method -> methodNameList.add(method.getName()));
//import
List<String> importList = new ArrayList<>();
importList.add(classObject.getName());
List<AutoWireService> autowireSeviceList = new ArrayList<>();
//过滤只要private
Arrays.stream(classObject.getDeclaredFields()).filter(t -> t.getModifiers() == 2).forEach(field -> {
UnitTestGen unitTestGen = new UnitTestGen();
AutoWireService autoWireService = unitTestGen.new AutoWireService();
autoWireService.setUseService(field.getName());
String[] sp = field.getType().getName().split("\\.");
autoWireService.setMockService(sp[sp.length - 1]);
autowireSeviceList.add(autoWireService);
//导包
importList.add(field.getType().getName());
});
map.put("className", simpleName);
map.put("classShortName", sbName.toString());
map.put("date", DateTools.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
map.put("author", Optional.ofNullable(author).orElse("system"));
map.put("package", classObject.getPackage());
map.put("serviceList", autowireSeviceList);
map.put("methodNameList", methodNameList);
map.put("importList", importList);
//获取输出地址
StringBuffer sb = new StringBuffer(System.getProperty("user.dir"));
sb.append("/").append(serverName).append("/src/test/java/").append(classObject.getPackage().getName().replaceAll("\\.", "\\/")).append("/");
//若目录不存在创建目录
File filePath = new File(sb.toString());
if (!filePath.exists()) {
filePath.mkdirs();
}
sb.append(simpleName).append("Test");
if (GROOVY.equals(unitTestType) || STATIC_GROOVY.equals(unitTestType)) {
sb.append(".groovy");
} else {
sb.append(".java");
}
map.put("unitTestOutPutLocation", parsePath(sb.toString()));
return map;
}
@Data
public class AutoWireService {
String useService;
String mockService;
}
private static List<String> getfilterMethod(Class classObject) {
List<String> filterMethod = new ArrayList<>();
Arrays.stream(classObject.getDeclaredFields()).forEach(t -> {
StringBuffer setSb = new StringBuffer();
setSb.append("set");
setSb.append(t.getName().substring(0, 1).toUpperCase());
setSb.append(t.getName().substring(1));
filterMethod.add(setSb.toString());
StringBuffer getSb = new StringBuffer();
getSb.append("get");
getSb.append(t.getName().substring(0, 1).toUpperCase());
getSb.append(t.getName().substring(1));
filterMethod.add(getSb.toString());
});
return filterMethod;
}
private static String parsePath(String path) {
if (PlatformUtil.isWindows()) {
return path.replaceAll("/+|\\\\+", "\\\\");
} else {
return path.replaceAll("/+|\\\\+", "/");
}
}
}
${package}
import org.springframework.beans.factory.annotation.Autowired
import spock.lang.Subject
import spock.lang.Unroll
import com.jdh.boot.util.SpringContextUtil
<#list importList as import>
import ${import}
#list>
/**
* 需要在原来的Service上暴露对应属性set方法-@Setter
* @Descriptioin
* @Author ${author}
* @Date ${date}
*/
@Subject(${className}.class)
class ${className}Test extends SpockBaseRunner {
@Autowired
private ${className} ${classShortName}
<#list serviceList as service>
def ${service.useService} = Mock(${service.mockService})
#list>
@Override
def init() {
<#list serviceList as service>
${classShortName}.${service.useService} = ${service.useService}
#list>
}
@Override
def clean() {
<#list serviceList as service>
${classShortName}.${service.useService} = SpringContextUtil.getObject(${service.mockService}.class)
#list>
}
<#list methodNameList as methodName>
@Unroll
def "${className}-${methodName}"() {
given:
when:
${classShortName}.${methodName}()
then:
where:
}
#list>
}
${package}
import org.junit.runner.RunWith
import org.spockframework.runtime.Sputnik
import spock.lang.Subject
import spock.lang.Unroll
import spock.lang.Specification
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.modules.junit4.PowerMockRunnerDelegate
/**
* @Descriptioin
* @Author ${author}
* @Date ${date}
*/
//@PowerMockIgnore("javax.net.ssl.*")看需使用
@Subject(${className}.class)
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
@PrepareForTest([])
class ${className}Test extends Specification {
<#list methodNameList as methodName>
@Unroll
def "${className}-${methodName}"() {
given:
when:
${classShortName}.${methodName}()
then:
where:
}
#list>
}
${package};
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
<#list importList as import>
import ${import};
#list>
/**
* 基本的Junit Runner
* 可使用Springboot test集成的Mockito框架
* 不能Mock静态方法,如果需要Mock静态方法需要使用PowerMockRunner
* @Descriptioin
* @Author ${author}
* @Date ${date}
*/
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest
@Transactional
@Rollback
public class ${className}Test {
@InjectMocks
private ${className} ${classShortName};
<#list serviceList as service>
@Mock
private ${service.mockService} ${service.useService};
#list>
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
<#list methodNameList as methodName>
@Test
public void ${methodName}Test() {
}
#list>
}
${package};
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
<#list importList as import>
import ${import};
#list>
/**
* Powermock runner,不加载Spring上下文,否则在集成测试时会重复加载Spring上下文
* 如果需要mock静态方法,final方法等Power增强操作
* @Descriptioin
* @Author ${author}
* @Date ${date}
*/
@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*"})
@PrepareForTest()
public class ${className}Test {
@InjectMocks
private ${className} ${classShortName};
<#list serviceList as service>
@Mock
private ${service.mockService} ${service.useService};
#list>
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
<#list methodNameList as methodName>
@Test
public void ${methodName}Test() {
}
#list>
}