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

由于网络组一直在排查,暂时没有答复,所以只能采取一个比较临时的办法。我自己在本机用的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 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 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 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 getAllMethodName(String path) {        List 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             String str = all[i].getName();            methods.add(str);        }return methods;    }/**     * 获取某包下所有类     *     * @param packageName 包名     * @return 类的完整名称     */public static List getClassName(String packageName) {        List 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 !!!

  • 还有一种神器可以解决这个问题:arthas,可以通过arthas命令redefine实现Java热更新的方式替换方法类,这个比较复杂,而且适用范围更窄,不可取。

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

FunTester热文精选

  • 如何成为全栈自动化工程师
  • 绑定手机号性能测试
  • 固定QPS压测初试
  • 开源礼节
  • Groovy在JMeter中应用专题
  • 基于WebSocket的client封装
  • 基于Socket.IO的Client封装
  • Socket.IO接口多用户测试实践
  • 好书推荐《Java性能权威指南》
  • 所谓UI测试

点击阅读原文,查看公众号历史文章

centos 命令行执行多个命令_命令行如何执行jar包里面的方法相关推荐

  1. Jar包冲突解决方法 Unknown lifecycle phase mvn Eclipse中执行maven命令

    1.如下图,右击需要执行maven命令的工程,选择"Debug As"或"Run As",再选择"Maven build..." 进行如上操 ...

  2. java打包拆包_[Java] Java 打包成jar包 和 解压jar包

    解压jar包 jar xf xxx.jar 打包成jar包 方法一:通过jar命令 jar命令的用法: 下面是jar命令的帮助说明: 用法:jar {ctxui}[vfm0Me] [jar-file] ...

  3. jar包 jdk 停_一文读懂jar包的小秘密

    简介 java程序员每天不是在创建jar包就是在创建jar包的路上,并且各种依赖引用都是以jar包的形式展示的.但是随着现代IDE的出现,我想很多程序员已经基本上很少直接和jar包打交道了. 换句话说 ...

  4. 常用jar包_几个解决 Maven Jar 包冲突的小技巧.

    前言 大家在项目中肯定有碰到过Maven的Jar包冲突问题,经常出现的场景为: 本地运行报NoSuchMethodError,ClassNotFoundException.明明在依赖里有这个Jar包啊 ...

  5. java jar在后台运行_【Java】部署jar包并后台运行

    Linux环境部署: 1.执行jar包的命令和在windows操作系统上是一样 java -jar jarName-0.0.1-SNAPSHOT.jar; 注: 关闭服务器连接时会关闭此程序进程,(推 ...

  6. mac 命令行 解压7z文件_命令行压缩解压缩一 7z

    命令行压缩解压缩一 7z 1) 简介 7z,全称7-Zip, 是一款开源软件.是目前公认的压缩比例最大的压缩解压缩软件. 主页:http://www.7-zip.org/ 中文主页:http://7z ...

  7. mac 命令行 解压7z文件_命令行压缩解压7z

    命令行压缩解压一 7z 1) 简介7z,全称7-Zip, 是一款开源软件.是目前公认的压缩比例最大的压缩解压软件.主页:http://www.7-zip.org/中文主页:http://7z.spar ...

  8. 命令行curl上传文件_命令行基础知识:使用cURL下载文件

    命令行curl上传文件 Client URL, or simple cURL is a library and command-line utility for transferring data b ...

  9. scala学习-Linux命令行运行jar包传入main方法参数

    1.eclipse写了一个程序,直接打包成jav普通jar包,然后无法选择主类,但是运行的时候,可以指定参数,(参数在程序里用 args(i)引用,注意是括号) 2.运行的时候,参数跟在jar包后面, ...

最新文章

  1. 解读模拟摇杆原理及实验
  2. [云炬创业基础笔记]第二章创业者测试17
  3. mysql emoji表情_让MySQL支持Emoji表情 mysql 5.6
  4. 损失函数与优化器理解+【PyTorch】在反向传播前为什么要手动将梯度清零?optimizer.zero_grad()
  5. 对象输出流 ObjectOutputStream java
  6. 【福利】计算机公开课合集(视频教程)
  7. OpenShift 4.6 新特性 - 用 Windows MachineConfig Operator 管理 Windows Container
  8. 卷积神经网络——第一周 卷积神经网络基础——第二部分
  9. Java程序员必备!Dagger2源码分析(二(1)
  10. Pwn2Own 2020线上争霸赛落幕:Fluoroacetate 团队四度蝉联 Master of Pwn!
  11. Zabbix---5 监控linux服务器目录大小
  12. 苹果mac好用的记事本软件:Ulysses 24
  13. redis 加载mysql_Mysql Redis PostgreSQL数据库查看客户端连接
  14. 「无捆绑不锁页」极度纯净win10 32位专业版下载地址
  15. 1.0 信息化与信息系统
  16. python如何输入整数求和_Python如何输入数字并求和
  17. 【愚公系列】2022年10月 微信小程序-电商项目-商品详情页面说明和商品导航
  18. (附源码)springboot宠物管理系统 毕业设计 121654
  19. HDOJ 4622 Reincarnation (hash)
  20. video标签设置autoplay(自动播放)无效

热门文章

  1. STM32工作笔记0052---串口通信原理--UART
  2. SpringCloud工作笔记052---各种数据库在java中的连接配置_以及连接驱动
  3. SpringCloud学习笔记014---可以使用json对rabbitmq进行封装,方便数据传输
  4. 解决Fail to create the java Virtual Machine
  5. 学习使用c++编写opencv的一些记录
  6. typescript parseint不能传number_Typescript 使用日志
  7. verilog学习记(加法器)
  8. 51单片机几种精确延时(包含自动适应主时钟)
  9. 成功没有捷径python_成功,大部分时候并没有捷径
  10. 使用函数求余弦函数的近似值_WPS-Excel表格实用技巧-排名函数RANK函数的使用