写点什么

命令行如何执行 jar 包里面的方法

用户头像
FunTester
关注
发布于: 2021 年 09 月 15 日

最近遇到一个尴尬的问题,由于公司机测试环境的机房迁移,导致办公区的网络跟测试环境网络之前延迟比较大,大到什么程度呢?大到不能正常使用测试环境。


由于网络组一直在排查,暂时没有答复,所以只能采取一个比较临时的办法。我自己在本机用的Java写的测试框架以及Groovy写的测试脚本,具体情况可参考:如何统一接口测试的功能、自动化和性能测试用例


由于本人之前拥有的一台独立物理测试机被收回,现在分给测试组的只有一个docker容器起来的服务。本来最优的方案是在docker file文件时候吧Groovy SDK加上去,保证一个Groovy运行环境,但也被否掉了,只留了一个口子给我,就是上传文件到项目Git中,然后通过够部署项目把文件弄到docker容器中。


Groovy SDK又比较大,完事儿还需要重新设置环境变量等等问题,我想到了两个其他方案:


  • 将项目buildjar包,测试用例(也就是某个类的main方法),通过执行jar包中的class类的main方法,达到执行不同测试用例的目的,顺手做一个参数化。

  • 定义一个统一的main方法入口,通过反射执行不同的方法。


显然第二个思路用途更广,但是实现起来略微麻烦了一些,而且传参的时候比较复杂,个人建议还是优先考虑第一种方式。


下面分享这两种方式的实现。

执行 class 的 main 方法

首先我写一个测试用例,内容如下:


package com.okayqa.composer.performance.teach1_1
import com.fun.frame.execute.Concurrentimport com.fun.frame.httpclient.ClientManageimport com.fun.frame.httpclient.FanLibraryimport com.fun.frame.thread.HeaderMarkimport com.fun.frame.thread.RequestThreadTimesimport com.fun.utils.ArgsUtilimport com.okayqa.composer.base.OkayBaseimport com.okayqa.composer.function.IMSocket
class ActivityUnread extends OkayBase{ public static void main(String[] args) { ClientManage.init(5, 5, 0, "", 0) def util = new ArgsUtil(args) def thread = util.getIntOrdefault(0, 100) def times = util.getIntOrdefault(1, 100) def base = getBase() def socket = new IMSocket(base) socket.getActivityUnread(81951375949,43519,43504) def request = FanLibrary.getLastRequest()
def mark = new HeaderMark("requestid") def times1 = new RequestThreadTimes(request, times, mark)
new Concurrent(times1, thread, "activity未读消息").start()

allOver() }}
复制代码


然后使用Mavenpackage命令打包。执行Java命令即可执行jar包中某个classmain方法,可参数化。


java -cp okay_test-1.0-SNAPSHOT.jar com.okayqa.composer.performance.teach1_1.ActivityUnread 1 1 start


下面是输出:


INFO-> 当前用户:fv,IP:10.60.192.21,工作目录:/Users/fv/Documents/workspace/okay_test/target/,系统编码格式:UTF-8,系统Mac OS X版本:10.15.7INFO-> requestid: Fdev1607495809625INFO-> 请求uri:https://teacherpad-stress.xk12.cn/api/t_pad/user/login,耗时:368 msINFO-> 教师:61951375269,学科:null,名称:61951375269,登录成功!INFO-> requestid: Fdev1607495810141INFO-> 请求uri:https://ailearn-composer-interface-stress.xk12.cn/api/composer/activity/course_list/unread_num,耗时:106 msINFO->~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~>  {>  ① . "data":{>  ② . . . "activity_unread_num":[>  ③ . . . . . {>  ③ . . . . . "activity_id":43519,>  ③ . . . . . "msg_count":208>  ② . . . },>  ② . . . {>  ③ . . . . . "activity_id":43504,>  ③ . . . . . "msg_count":0>  ③ . . . . . }>  ② . . . ]>  ① . },>  ① . "meta":{>  ② . . . "emsg":"成功",>  ② . . . "ecode":0>  ① . }>  }~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~INFO-> gc回收线程开始了!INFO-> 线程:activity未读消息0,执行次数:1,错误次数: 0,总耗时:1 sINFO-> 总计1个线程,共用时:0.059 s,执行总数:1,错误数:0,失败数:0INFO-> 数据保存成功!文件名:/Users/fv/Documents/workspace/okay_test/target/long/data/1activity未读消息20201209143650INFO->~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~>  {>  ① . "rt":56,>  ① . "total":1,>  ① . "qps":17.857142857142858,>  ① . "failRate":0.0,>  ① . "threads":1,>  ① . "startTime":"2020-12-09 14:36:50",>  ① . "endTime":"2020-12-09 14:36:50",>  ① . "errorRate":0.0,>  ① . "executeTotal":1,>  ① . "mark":"activity未读消息20201209143650",>  ① . "table":"">  }~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~INFO->INFO-> gc回收线程结束了!
复制代码


完美执行 1 !!!

反射执行方法

首先封装一个反射执行的工具类,代码如下:


package com.fun.frame.execute;
import com.alibaba.fastjson.JSON;import com.fun.base.exception.FailException;import com.fun.config.Constant;import com.fun.frame.SourceCode;import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import java.io.File;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.net.URL;import java.util.ArrayList;import java.util.Arrays;import java.util.List;
@SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "NP_NULL_ON_SOME_PATH_EXCEPTION"})public class ExecuteSource extends SourceCode {
private static Logger logger = LoggerFactory.getLogger(ExecuteSource.class);
/** * 执行包内所有类的非 main 方法 * * @param packageName */ public static void executeAllMethodInPackage(String packageName) { List<String> classNames = getClassName(packageName); if (classNames != null) { for (String className : classNames) { String path = packageName + "." + className; executeAllMethod(path);// 执行所有方法 } } }

/** * 执行一个类的方法内所有的方法,非 main,执行带参方法的代码过滤 * * @param path 类名 */ public static void executeAllMethod(String path) { Class<?> c = null; Object object = null; try { c = Class.forName(path); object = c.newInstance(); } catch (Exception e) { e.printStackTrace(); } Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { try { method.invoke(object); } catch (IllegalAccessException e) { logger.warn("非法访问导致反射方法执行失败!", e); } catch (InvocationTargetException e) { logger.warn("反射调用目标异常导致方法执行失败!", e); } catch (Exception e) { logger.warn("反射方法执行失败!", e); } finally { sleep(Constant.EXECUTE_GAP_TIME); } } }
/** * 提供给命令行main方法使用 * * @param params */ public static void executeMethod(String... params) { String[] ps = Arrays.copyOfRange(params, 1, params.length); executeMethod(params[0], ps); }
/** * 执行具体的某一个方法,提供内部方法调用 * * @param path */ public static void executeMethod(String path, Object... paramsTpey) { int length = paramsTpey.length; if (length % 2 == 1) FailException.fail("参数个数错误,应该是偶数"); String className = path.substring(0, path.lastIndexOf(".")); String methodname = path.substring(className.length() + 1); Class<?> c = null; Object object = null; try { c = Class.forName(className); object = c.newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { logger.warn("创建实例对象时错误:{}", className, e); } Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { if (!method.getName().equalsIgnoreCase(methodname)) continue; try { Class[] classs = new Class[length / 2]; for (int i = 0; i < paramsTpey.length; i = +2) { classs[i / 2] = Class.forName(paramsTpey[i].toString());//此处基础数据类型的参数会导致报错,但不影响下面的调用 } method = c.getMethod(method.getName(), classs); } catch (NoSuchMethodException | ClassNotFoundException e) { logger.warn("方法属性处理错误!", e); } try { Object[] ps = new Object[length / 2]; for (int i = 1; i < paramsTpey.length; i = +2) { String name = paramsTpey[i - 1].toString(); String param = paramsTpey[i].toString(); Object p = param; if (name.contains("Integer")) { p = new Integer(changeStringToInt(param)); } else if (name.contains("JSON")) { p = JSON.parseObject(param); } ps[i / 2] = p; } method.invoke(object, ps); } catch (IllegalAccessException | InvocationTargetException e) { logger.warn("反射执行方法失败:{}", path, e); } break; } }
/** * 获取当前类的所有用例方法名 * * @param path * @return */ public static List<String> getAllMethodName(String path) { List<String> methods = new ArrayList<>(); Class<?> c = null; Object object = null; try { c = Class.forName(path); object = c.newInstance(); } catch (Exception e) { FailException.fail("初始化对象失败:" + path); } Method[] all = c.getDeclaredMethods(); for (int i = 0; i < all.length; i++) { String str = all[i].getName(); methods.add(str); } return methods; }
/** * 获取某包下所有类 * * @param packageName 包名 * @return 类的完整名称 */ public static List<String> getClassName(String packageName) { List<String> fileNames = new ArrayList<>(); ClassLoader loader = Thread.currentThread().getContextClassLoader();// 获取当前位置 String packagePath = packageName.replace(".", Constant.OR);// 转化路径,Linux 系统 URL url = loader.getResource(packagePath);// 具体路径 if (url == null || !"file".equals(url.getProtocol())) { FailException.fail("获取包路径失败!"); } File file = new File(url.getPath()); File[] childFiles = file.listFiles(); for (File childFile : childFiles) { String path = childFile.getPath(); if (path.endsWith(".class")) { path = path.substring(path.lastIndexOf(OR) + 1, path.lastIndexOf(".")); fileNames.add(path); } } return fileNames; }

}
复制代码


使用Demo如下:


package com.fun.main;
import com.fun.frame.SourceCode;import com.fun.frame.execute.ExecuteSource;
public class ExecuteMethod extends SourceCode {
public static void main(String[] args) { args = new String[]{"com.fun.ztest.java.T.test", "java.lang.Integer", "1"}; ExecuteSource.executeMethod(args); }

}
复制代码


其中T的代码中test()方法如下:


    public static void test(int i) {        output(33333333 + i);    }
复制代码


这里我模拟了args参数,可以看出这里的参数非常复杂,都是较长的String字符串。


控制台输出:


INFO-> 当前用户:fv,IP:10.60.192.21,工作目录:/Users/fv/Documents/workspace/fun/,系统编码格式:UTF-8,系统Mac OS X版本:10.15.7WARN-> 方法属性处理错误!java.lang.NoSuchMethodException: com.fun.ztest.java.T.test(java.lang.Integer)  at java.lang.Class.getMethod(Class.java:1786) ~[?:1.8.0_51]  at com.fun.frame.execute.ExecuteSource.executeMethod(ExecuteSource.java:106) [classes/:?]  at com.fun.frame.execute.ExecuteSource.executeMethod(ExecuteSource.java:77) [classes/:?]  at com.fun.main.ExecuteMethod.main(ExecuteMethod.java:10) [classes/:?]INFO-> 33333334
Process finished with exit code 0
复制代码


  • 这里的报错是因为test()方法的参数是int并不是我传入的java.lang.Integer导致的,单并不影响后面的方法调用正常执行,可忽略。


完美执行 2 !!!




公众号 FunTester,非著名测试开发,文章记录学习和感悟,欢迎关注,交流成长。

FunTester 热文精选

发布于: 2021 年 09 月 15 日阅读数: 3
用户头像

FunTester

关注

公众号:FunTester,650+原创,欢迎关注 2020.10.20 加入

Have Fun,Tester! 公众号FunTester,坚持原创文章的测试人。 FunTester测试框架作者,DCS_FunTester分布式性能测试框架作者。

评论

发布
暂无评论
命令行如何执行jar包里面的方法