看谁跑的快—性能测试框架

1.项目简介:
描述一个程序的好坏可以很好的帮助我们去优化程序,提高代码水平。在比较有相同功能的方法时,我们通常会在特定的负载下,看系统的响应时间和表现。
所以以基于JMH基准测试框架为灵感,编写了一款简单的测试框架“看谁跑的快”
2.项目灵感
在学习String和StringBuffer的时候,String使用“+”号连接字符串效率比StringBuffer的apppend()的效率要低很多。嗯…怎么证明?
而在学习排序算法的时候,直到Java自带的Arrays.Sort()的效率非常高,所以就想知道自己的排序算法和自带的排序算法的区别。
由此,引出了自己想写的项目
3.项目思路

  • ​ 测试用例准备

​ 1.首先高中物理在进行试验的时候,我们要进行多组试验,而且要控制变量,反应在代码中,我们 肯定不能只测试一组数据,而是一定要多测试,达到减小误差的效果,为了实现配置可修改
通过自定义注解的方式配置试验组和每组要测试的数目

​ 2.要测试的类都放到一个com.test.Cases包下然后进行并且要测试的类都要继承一个接口口, Case(只代表一个标记而已,不定义任何方法)

​ 3.要测试的方法使用自定义注解BenchMark打上标记

  • ​ 测试用例的加载

    1.定义一个类用于实例化测试用例类

    ​ 逻辑:1.获取com.test.Cases所在的绝对路径,

    ​ 2.获取Cases文件操作类

    ​ 3.获取类名

    ​ 4.通过反射来获取类的实例对象,并看其是否实现了Case接口,只有实现Case的类才是 要测试的类,将其加入到ArrayList中

    2.运行测试方法,进行测试

    ​ 逻辑:1.通过步骤1,已经得到了存放Case实例的集合

    ​ 2.通过Case 类对象获取Method,

    ​ 3.通过Metod,获取方法注解BenchMark,只有被@BenchMark方法注解过的类才可以 进行测试

    ​ 4.通过Metod,获取配置信息的注解,并获取内部的值

    ​ 5.通过invoke进行注解

    4.目录结构

    [外链图片转存失败(img-t5GQH13T-1563706700726)(D:\知识点总结\捕获.PNG)]

5.代码实现

自定义注解

参数配置

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Measurement {
    int group() default 5;
    int iteration() default 10;
}

测试类的标注

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BenchMark {
}

测试用例的自动加载

public class CaseLoader {
    public CaseRunner load() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        String pkg="com\\test\\Cases";
        String pkgDot="com.test.Cases";
        //通过反射和类加载器拿到文件的绝对路径
        Enumeration<URL> url = this.getClass().getClassLoader().getResources(pkg);
        List<String>  caseName=new ArrayList<String>();
        while(url.hasMoreElements()){
            URL url1=url.nextElement();
            //获取路径类型
            String protocol = url1.getProtocol();
            if(!"file".equals(protocol)){
               continue;
            }
            //将路径进行解码操作
            String name1=URLDecoder.decode(url1.getPath(),"UTF-8");
            //获取文件操作类
            File file=new File(name1);
            //获取文件下的列表
            File[] files=file.listFiles();
            for (File i:files){
                //如果这个文件是目录直接跳出
                if(i.isDirectory()){
                    continue;
                }
                //获取文件名
                String name=i.getName();
                if(i.isFile()){
                    //将文件名存入caseName中
                    caseName.add(name.substring(0,name.length()-6));
                }
            }
        }
        //获取实例对象
        List<Case> listCase=new ArrayList<Case>();
        for(String i:caseName){
            if(hasIntfCase(i,Case.class)){
                Class<?> cls=Class.forName(pkgDot+"."+i);
                listCase.add((Case) cls.newInstance());
            }
        }
        return new CaseRunner(listCase);

    }

    private boolean hasIntfCase(String i, Class<Case> caseClass) throws ClassNotFoundException {
        Class<?> cls=Class.forName("com.test.Cases."+i);
        Class<?>[] interfaces = cls.getInterfaces();
        for(Class<?> j:interfaces)
        if(j==caseClass){
            return true;
        }
        return false;
    }
}

测试:

public class CaseRunner {
    private List<Case> list;

    public CaseRunner(List<Case> list) {
        this.list = list;
    }
    public void run() throws InvocationTargetException, IllegalAccessException {
        //初始化测试参数
        int iteration=10,group=5;
        //遍历Case用例,进行测试
        for(Case cases:list){
            //获取Class对象
            Class<Case> cls= (Class<Case>) cases.getClass();
            //获取类级别的配置参数
            Measurement measurement = cls.getAnnotation(Measurement.class);
            int it=iteration,g=group;
            if(measurement!=null) {
                it = measurement.iteration();
                g = measurement.group();
            }
            //获取类中所有方法
            Method[] methods=cls.getMethods();
            run(methods,cases,it,g);
    }
      private void run(Method[] methods,Case cases,int it,int g ) throws InvocationTargetException, IllegalAccessException {

        for(Method method:methods) {
            BenchMark benchMark1 = method.getAnnotation(BenchMark.class);
            if (benchMark1 == null) {
                continue;
            }
            Measurement measurement1 = method.getAnnotation(Measurement.class);
            if (measurement1 != null) {
                it = measurement1.iteration();
                g = measurement1.group();
            }
            for (int i = 0; i < g; i++) {
                System.out.println("第" + i + "组");
                long start = System.currentTimeMillis();
                for (int j = 0; j < it; j++) {
                    method.invoke(cases);
                }
                long end = System.currentTimeMillis();
                System.out.println("花费" + (end - start + 1)+"ms");
            }
        }
        
    }
}

com.test.Cases包下如果有需要测试的类,将待测试类实现Cases,然后要测试方法使用@BenchMark标注,并初始化参数@Measurement(group=xxx,iteration=xxx)

6.总结

​ 完成了基本测试功能,可以自定义测试参数,对各类的代码进行测试,

​ 项目总的来说还有许多不足的地方,比如没有实现预热功能,不能项目启动时,性能没有达到最好,没有实现存jar包导入项目,会继续跟进,完善项目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值