懒人测试框架

项目-懒人测试框架

-项目功能:

简单便利的测试相关方法的性能,比如说String的字符串相加和StringBuilder的add方法的性能**

- 技术栈:

1. 1.性能测试
关键字:

  • 系统:自己开发的程序 - 生活中的一项实体系统
  • 负载:单位时间内客户请求的数量
  • 响应时间:客户从发起请求到接收到成功的响应时间 - 对我们软件系统来说,通信本身也会耗时
  • 稳定性: 指任意时间,响应时间的波动情况,只有波动越小的系统,就是好系统。

1. 2.注解的使用

①我们的代码中出现了太多的魔法数字,注入 10 组,每组 1000 次这样的代码,这类数字应该是配置出来的而不是写死在代码中的,因此我们通过Measurement注解来配置。
②通过Benchmark注释来标注类中哪些方法需要测试

2. 3.反射的使用 :

①在CaseRunner类中利用反射取出实现Case接口的类、 
②反射调用类中方法
③反射获取注解参数

1. 4.io的使用

①根据一个固定类,找到类加载器
②根据类加载器找到类文件的路径 
③扫描路径中的所有类文件,并用字符串截取得到类名称,在通过实现Case接口条件判断出哪些是需要测试的类。

1. 5.集合的使用

①得到类的实例放在list集合下
②配合泛型添加集合

创建一个naven项目 - 测试字符串相加String 和 StringBilder 的性能
最简化版本:

public class StringConcat { 
   private static String addString() {
        String a = ""; 
        for (int i = 0; i < 10; i++) { 
                a += i; 
        }
        return a; 
   }
    private static String addStringBuilder() { 
        StringBuilder sb = new StringBuilder(); 
        for (int i = 0; i < 10; i++) { 
            sb.append(i); 
         }
            return sb.toString(); 
     }
    public static void main(String[] args) { 
        long t1 = System.nanoTime(); 
        addStringBuilder(); 
        long t2 = System.nanoTime(); 
        addString(); 
        long t3 = System.nanoTime();
        // 单位是纳秒 
        System.out.println("StringBuilder + 耗时:" + (t2 - t1)); 
        System.out.println("String + 耗时:" + (t3 - t2)); 
      } 
}

查询结果: 我们交换执行顺序后发现俩个方法执行的结果差别很大

猜测可能影响性能的因素:

  • 1)执行时间过短
  • 2)次数太少(实验次数)
  • 3)优化原因/AOT(编译器)静态 /JIT(编译器)动态
  • 4)预热,机器预热,java预热
  • 5)其他原因

所以我们把一些影响因素都进行调整,并且优化代码
项目目录:
在这里插入图片描述

2.1执行次数增大: 通过数据可以基本反映出StringBiluder的性能快与String

2.2增加Measurement注解设置测试组数以及测试次数,通过反射获得注解信息。

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

2.3增加Benchmark注解 标识哪些方法是用来被测试的方法

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Benchmark {

}

2.4增加Case接口,相当于指示器,待测试类实现此接口

package com.gxg;

//指示器,待测试类实现此接口
public interface Case {
}

2.5为方便客户,自动加载需要测试的case类,添加CaseLoader类

package com.gxg.CommUtils;

import com.gxg.Case;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class CaseLoader {
    public CaseRunner load() throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
        String pkgdot = "com.gxg.cases";
        String pkg = "com/gxg/cases";
        List<String> classNameList = new ArrayList<String>();
        ClassLoader classLoader = this.getClass().getClassLoader();
            Enumeration<URL> urls = classLoader.getResources(pkg);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (!url.getProtocol().equals("file")) {
                    //如果不是*.class文件,暂时不支持
                    continue;
                }
                //URL解码
                String dirname = URLDecoder.decode(url.getPath(), "UTF-8");
                File dir = new File(dirname);
                if (!dir.isDirectory()) {
                    //不是目录
                    continue;
                }
                File[] files = dir.listFiles();
                if (files == null) {
                    continue;
                }
                for (File file : files) {
                    String filename = file.getName();
                    String className = filename.substring(0, filename.length() - 6);
                    classNameList.add(className);
                }
            }
            List<Case> caseList = new ArrayList<Case>();
            //通过反射取出实现Case接口的类,就是需要测试的类
            for (String className : classNameList) {
                Class<?> cls = Class.forName(pkgdot + "." + className);
                if (hasInterface(cls, Case.class)) {
                    caseList.add((Case) cls.newInstance());
                 }
            }
            return new CaseRunner(caseList);
     }
    private boolean hasInterface(Class<?> cls,Class<?> interfce){
            Class<?>[] interf = cls.getInterfaces();
            for(Class<?> i : interf){
             if(i == interfce){
                 return true;
             }
         }
         return false;
    }
}

2.6具体实现类CaseRunner类

package com.gxg.CommUtils;

import com.gxg.Case;
import com.gxg.annotations.Benchmark;
import com.gxg.annotations.Measurement;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

public class CaseRunner {
        private static final int DEFAULT_ITERATIONS = 10;
        private static final int DEFAULT_GROUP = 10;

        private final List<Case> caseList;
        public CaseRunner(List<Case> caseList){
            this.caseList = caseList;
        }
        public void run(){
            //找到对象中那些方法是需要测试的方法
            for(Case bCase : caseList){
                System.out.println(caseList);
                int iterations = DEFAULT_ITERATIONS;
                int group = DEFAULT_GROUP;

                //先获取类级别的配置
                Measurement classMeasuremennt = bCase.getClass().getAnnotation(Measurement.class);
                if(classMeasuremennt != null){
                    iterations = classMeasuremennt.iteration();
                    group = classMeasuremennt.Group();
                }
                Method[] methods = bCase.getClass().getMethods();
                for(Method method : methods){
                    Benchmark benchmark = method.getAnnotation(Benchmark.class);
                    if(benchmark == null){
                        continue;
                    }
                    Measurement methodmeasurement = method.getAnnotation(Measurement.class);
                    if(methodmeasurement != null){
                        iterations = methodmeasurement.iteration();
                        group = methodmeasurement.Group();
                    }
                    runCase(bCase,method,iterations,group);
                }
            }
        }
        private void runCase(Case bCase,Method method,int iterations,int group){
            System.out.println(method.getName());
            for(int i = 1; i <= group; i++){
                System.out.println("第" + i + "次实验");
                long t1 = System.nanoTime();
                for(int j = 1; j < iterations; j++){
                    try {
                        method.invoke(bCase);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
                long t2 = System.nanoTime();
                System.out.println("耗时:" + (t2 - t1) + "纳秒");
            }
        }
}

2.7测试类StringConcatCase

package com.gxg.cases;

import com.gxg.Case;
import com.gxg.annotations.Benchmark;
import com.gxg.annotations.Measurement;

@Measurement(iteration = 5,Group = 5)
public class StringConcatCase implements Case {
    @Benchmark
    public static void addString(){
        String str = "";
        for(int i = 0; i < 10; i++){
            str += i;
        }
    }
    @Benchmark
    public static void addStringBuilder(){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < 10; i++){
            sb.append(i);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值