首页 > 编程语言 >Java轻量级测试框架的实现与使用 总篇

Java轻量级测试框架的实现与使用 总篇

时间:2024-09-17 19:50:41浏览次数:10  
标签:Task Java int 样例 总篇 测试 main public 轻量级

Java轻量级测试框架的实现与使用 总篇

java8,jdk8,测试,assert

背景

每次写算法题,用例不过总要到本地调试一下,总觉得测试代码写起来又没营养又很麻烦,即便是借助junit测试框架也很麻烦,太重了,写完又觉得测试代码不美观需要删掉。

正好在学习spring过程中接触到注解,研究其原理时了解到反射,借由注解和反射,应该可以自定义一个轻量级的测试框架。

耗时十多天(业余时间),总算大概完成了,可喜可贺。内容大致分为两篇,同时还有整个过程中遇到的各种问题。

首先来看一下实际使用效果:

运行效果

待测方法(计算逻辑和返回值简化处理):

class Solution {
    @AssertExample(params = "[2,451,1]", expectResult = "0")
    public int singleNumber(int[] nums) {
        return 0;
    }
}
public class TQ1判断是否可以赢得数字游戏 {
    @AssertExample(params = {"[1,2,3,4,10]"}, expectResult = "false")
    @AssertExample(params = {"[1,2,3,4,5,14]"}, expectResult = "true")
    @AssertExample(params = {"[5,5,5,25]"}, expectResult = "true")
    public boolean canAliceWin(int[] nums) {
        // calculate...
        return 0;
    }
}
public class TQ1统计满足K约束的子字符串数量I {

    @AssertExample(params = {"10101", "1"}, expectResult = "12")
    @AssertExample(params = {"1010101", "2"}, expectResult = "25")
    @AssertExample(params = {"11111", "1"}, expectResult = "15")
    public int countKConstraintSubstrings(String s, int k) {
        int ans = 0;
        // calculate...
        return ans;
    }
}

正常测试通过的情况:

测试程序已启动,时间戳:1726036055742
待测试类 : 	main.leetcode.editor.cn.Solution
执行方法 : 	singleNumber(int[])
	测试样例 : 	[2, 451, 1] => 0
	样例用时(ms):0
待测试类 : 	main.problemAndSolving.leetcode_20240728WeekRankList.TQ1判断是否可以赢得数字游戏
执行方法 : 	canAliceWin(int[])
	测试样例 : 	[1, 2, 3, 4, 10] => false
	样例用时(ms):0
	测试样例 : 	[1, 2, 3, 4, 5, 14] => true
	样例用时(ms):0
	测试样例 : 	[5, 5, 5, 25] => true
	样例用时(ms):0
待测试类 : 	main.problemAndSolving.leetcode_20240818WeekRankList.TQ1统计满足K约束的子字符串数量I
执行方法 : 	countKConstraintSubstrings(java.lang.String, int)
	测试样例 : 	10101, 1 => 12
	样例用时(ms):0
	测试样例 : 	1010101, 2 => 25
	样例用时(ms):0
	测试样例 : 	11111, 1 => 15
	样例用时(ms):0
测试程序已启动,时间戳:1726036055918,总计用时(ms):176

用例不通过的情况(下半错误信息是红色的,第一行标明运行类文件位置,点击可跳转):

测试程序已启动,时间戳:1726123986010
待测试类 : 	main.leetcode.editor.cn.Solution
执行方法 : 	singleNumber(int[])
	测试样例 : 	[2, 451, 1] => 1
	样例用时(ms):Exception in thread "main" java.lang.AssertionError: 
期望值:	1
实际值:	0
main.leetcode.editor.cn.Solution.singleNumber(Solution.java:100)
	at main.customUtil.Task.invoke(Task.java:120)
	at main.customUtil.Task.runMethod(Task.java:90)
	at main.customUtil.Task.runMethod(Task.java:75)
	at main.customUtil.Task.runMethod(Task.java:56)
	at main.customUtil.Task.testClasses(Task.java:35)
	at main.Main.main(Main.java:23)

解决方案

  1. 扫描class文件,获取Class对象
  2. 开发注解及注解容器
  3. 开发数据类型转换工具类
  4. 扫描注解入参并执行其方法,类和文件不同名的需要在包内安插一个 Sentry 类辅助实例化
    其中断言定位错误代码的行数并不准确,因为反射方法已经执行完毕,无法取得其线程栈信息,也无法通过返回值返回行数(原本方法就有返回值,而且那样会破坏了原方法)
  5. 添加IDE的Java虚拟机启动参数,开启断言
  6. 最后把启动类写上:
public class Main {
    public static void main(String[] args) throws Exception {
        long start = Task.getTimeStamp();
        System.out.println("测试程序已启动,时间戳:" + start);
        loadClassFiles();  // 加载类文件,内部文件列表为全局变量,若多次加载文件,需要重新调整其内部结构
        List<Class<?>> classList = getClasses(new Class[]{AssertExample.class, AssertExamples.class});  // 文件转化为class对象,筛选可运行类
        new Task(classList).testClasses();
        long end = Task.getTimeStamp();
        System.out.println("测试程序已启动,时间戳:" + end + ",总计用时(ms):" + (end - start));
    }
}

主要重点在class扫描和方法的入参和执行上,此二者逻辑链较长,其他问题相较起来就简单零碎些,好处理。

使用说明

假设现有一个待测试的类文件 Solution.java 如下:

public class Solution {
    public int singleNumber(int[] nums, String whiteString) {
        return nums.length;
    }
}

样例注解AssertExample
使用格式:@AssertExample(params = {"第一参数数据", "第二参数数据", ...}, expectResult = "返回值")

参数数据和返回值均为原始直观的字符串形式,一般来说,将OJ网站的用例直接粘贴即可,除数组需要前后缀[]以及分隔符,
(无空格),其他类型无需其他符号,测试框架可自动完成类型转换。
(字符串不需要额外再加引号,其他的List类型以及链表、树、Map等,遇到时再在类型转换工具类中另作扩展)

如果加注解的方法只有一个参数,那注解的params属性可以省去大括号,如运行效果中的第一个样例所示。

这个待测试的方法加注解后的代码如下:

public class Solution {  // 这个public关键字没有也可以,但是那就需要一个辅助类,辅助类见上文解决方案的第五步
    @AssertExample(params = {"[2,451,1]", "aStr"}, expectResult = "3")
    public int singleNumber(int[] nums, String whiteString) {
        return nums.length;
    }
}

启动之后,如果要观察不同参数下方法的执行过程,或者调试错误断言,在被测试的代码里直接打断点,然后debug模式运行即可,不再需要转移注意力,这极大方便了代码调试工作。

本来我想要不把参数和返回值合到一个参数里或者分成两个注解来添加,但是那样的话框架封装对注解类的耦合又提高了,而且在使用效率上的提升似乎并不明显,所以还是用了这个初版方案。

标签:Task,Java,int,样例,总篇,测试,main,public,轻量级
From: https://blog.csdn.net/qq_41623592/article/details/142309662

相关文章

  • 一键生成!轻量级 AI 证件照制作工具!
    大家好,我是Java陈序员。在日常生活中,我们需要各式各样的证件照。有时候需要不同的尺寸,一寸、两寸、小二寸...而有的时候需要不同的背景,白底、蓝底、红底...拍摄不同格式的证件照,需要我们往返于摄像馆,十分麻烦,又需要支出一定的花费!今天,给大家介绍一款免费的AI证件照制作工具,......
  • 前端JavaScript面试重难点: 闭包+内存泄漏+垃圾回收机制
    前置知识!!!闭包是Javascript语言的一个重难点,也是它的特色,很多高级应用都要依靠闭包来实现。在各种专业文献上学习"闭包"的时候,就一个感觉–“抽象”!特别是学习内存泄漏的时候,没想明白为什么使用闭包的时候不及时清除函数中的元素会导致内存泄漏,直到我的......
  • Java 如何计算jar包的HASH哈希值
    在做授权系统的时候用到了一个小功能发出来分享一下。全部代码如下:importjava.io.File;importjava.io.FileInputStream;importjava.io.InputStream;importjava.net.URISyntaxException;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmExcepti......
  • Java 性能调优:优化 GC 线程设置
    垃圾回收器使用一组称为GC线程的线程来执行回收工作。有时JVM可能会分配过多或过少的GC线程。本文将讨论JVM为什么会出现这种情况、其影响以及可能的解决方案。1咋查找应用程序的GC线程数量进行线程转储分析来确定应用程序的GC线程数量:从生产服务器捕获threaddump使......
  • Java基础语法-标识符
    标识符关键字Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符标识符注意点所有的标识符都应该以字母(A-Z或者a-z),美元($),或者下划线(_)开始首字符之后可以是字母(A-Z或者a-z),美元($),下划线(_)或数字的任何字符组合不能使用关键字作为......
  • day03 - Java集合和常用类
    第一章Collection集合1.Collection概述集合:java中提供的一种容器,可以用来存储多个数据集合和数组既然都是容器,它们有啥区别呢?数组的长度是固定的。集合的长度是不固定的。集合可以随时增加元素,其大小也随之增加数组中存储的是同一类型的元素,可以存储基本数据类型值。......
  • java线程池编程示例
    程序功能这段代码展示了如何使用Java线程池来并发执行多个任务。通过创建一个固定大小为3的线程池,程序提交了5个任务,并让线程池中的线程并发处理这些任务。每个任务模拟了一个耗时操作,最后程序等待所有任务完成后关闭线程池。代码importjava.util.concurrent.Ex......
  • 如何打造动漫天堂?宇宙动漫网站设计与实现,Java SpringBoot Vue技术揭秘
    ✍✍计算机毕业编程指导师**⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!⚡⚡Java、Python、微信小程序、大数据实战项目集⚡⚡文末......
  • 3.Java高级编程实用类介绍(一)
    三、Java高级编程实用类介绍(一)文章目录三、Java高级编程实用类介绍(一)一、枚举类型二、包装类三、Math一、枚举类型使用enum进行定义publicenum枚举名字{值1,值2....}二、包装类每个基本类型在java.lang包中都有一个相应的包装类 /* *new包......
  • Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
    0前言在页面中,要实现数据更新的视图实时显示,纯JavaScrip需要手动编写代码来处理数据和视图之间的更新。而Vue提供了数据绑定的能力,使得数据和视图保持同步。我们通过一个实例来体验两者的差别。我们设计一个页面,在页面里提供一个文本框,用户可以在文本框输入内容,然后我们在文本框下......