Java自定义测试框架测试对象中相应的成员方法

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Java自定义测试框架测试对象中相应的成员方法


目标:已知有一个Calculators类,该类中包含加减乘除等运算方法。

  1. 编写Check注解对需要进行测试的成员方法进行标记;
  2. 设计一个框架对该类中的任意public修饰的、带有@Check注解标记的成员方法进行测试;
  3. 捕捉该类中各个成员方法的异常并记录到日志文件中;
  4. 要求后续如果要测试其他类中相应的成员方法,无需修改代码,而仅修改配置文件即可直接调用该测试框架。

首先定义一个TestProperty类创建.properties配置文件:

package annotation.testframe;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
/**
 * 创建测试所需的配置文件
 */
public class TestProperty {
    public static void main(String[] args) throws IOException {
        //创建一个properties集合用于存储要写入文件的数据
        Properties prop = new Properties();
        //往prop集合中写入数据
        prop.setProperty("className","annotation.testframe.Calculators");
        //将prop集合中的数据存入配置文件
        prop.store(new FileOutputStream("annotation/testframe/testProp.properties"),
                "该文件存放用于测试的类的名字,目的是测试该类中所有用public修饰的成员方法。");
    }
}

创建的配置文件testProp.properties内容如下:

#该文件存放用于测试的类的名字,目的是测试该类中所有用public修饰的成员方法。
#Tue Jan 10 12:23:03 CST 2023
className=annotation.testframe.Calculators

后续只需修改不同的类名className,即可调用此测试框架对不同的类进行测试。

当然,不同的类的不同的成员方法所需传入的参数不同,实际测试时仍需要在测试类中修改待测试成员方法所需传入的参数。

定义Calculators类如下:

该类中强行造了一个空指针异常。

package annotation.testframe;
public class Calculators {
    public Calculators() {
    }
    @Check
    public int add(int a, int b) {
        //造一个异常
        String str = null;
        str.toString();
        return a + b;
    }
    @Check
    public int sub(int a, int b) {
        return a - b;
    }
    @Check
    public int multiply(int a, int b) {
        return a * b;
    }
    @Check
    public int devide(int a, int b) {
        return a / b;
    }
    public void test(){
        System.out.println("看看我被选中了嘛");
    }
}

定义Check注解:

package annotation.testframe;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}

定义测试类:

package annotation.testframe;
import junit.Calculator;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Properties;
public class TestFrame {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //创建prop集合对象以接收加载的配置文件中的数据
        Properties prop = new Properties();
        //加载配置文件进内存
        prop.load(new FileInputStream("annotation/testframe/testProp.properties"));
        //获取配置文件中所存放的值,即获取要测试的类的名字
        String className = prop.getProperty("className");
        //获取该类的class对象
        Class testClass = Class.forName(className);
        //System.out.println(testClass);//class annotation.testframe.Calculators
        //获取该类所有用public修饰的成员方法
        Method[] allMethods = testClass.getMethods();
        //获取该类所有用public修饰的、带有Check注解的成员方法
        ArrayList<Method> checkedMethod = new ArrayList<>();
        for (Method tm : allMethods) {
            if (tm.isAnnotationPresent(Check.class)) {
                checkedMethod.add(tm);
            }
        }
        //创建该类的对象
        Calculators cal = (Calculators) testClass.getConstructor().newInstance();
        //对需要测试的成员方法挨个进行测试
        int count = 0;//记录异常出现的次数
        PrintWriter pw = new PrintWriter(new FileOutputStream("annotation/testframe/buglog.txt", true));
        pw.println(Check.class.getAnnotation(Retention.class));
        pw.println("本次测试的类:" + className);
        for (Method cm : checkedMethod) {
            try {
                cm.invoke(cal, 2, 3);
                cm.invoke(cal, 0, 2);
                cm.invoke(cal, 2, 0);
                cm.invoke(cal, 2, -3);
                cm.invoke(cal, -2, 3);
            } catch (Exception e) {
                count++;
                pw.println("异常的方法:"+cm.getName());
                pw.println("异常的名称:" + e.getCause().getClass().getName());
                pw.println("异常的原因:" + e.getCause().getMessage());
                pw.println("-------------------------------");
            }
        }
        //记录当时时间
        ZonedDateTime now = ZonedDateTime.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EE a");
        pw.println("本次测试一共出现 " + count + " 次异常。");
        pw.println("测试时间:" + dtf.format(now));
        pw.println("==================================");
        pw.close();
    }
}

运行测试类,输出日志文件buglog.txt内容为:

@java.lang.annotation.Retention(RUNTIME)
本次测试的类:annotation.testframe.Calculators
异常的方法:add
异常的名称:java.lang.NullPointerException
异常的原因:Cannot invoke "String.toString()" because "str" is null
-------------------------------
异常的方法:devide
异常的名称:java.lang.ArithmeticException
异常的原因:/ by zero
-------------------------------
本次测试一共出现 2 次异常。
测试时间:2023-01-10 14:32:32 周二 下午
==================================

该日志文件中记录了2次异常,与实际结果相符合。

再次观察@Check注解,它的元注解@Retention设定了其生命周期,表示@Check注解生效的时间,观察前文日志文件,此处的@Check注解的元注解@Retention参数设置为RetentionPolicy.RUNTIME

@Retention:用于修饰注解,用于指定修饰的那个注解的生命周期,@Rentention包含一个RetentionPolicy枚举类型的成员变量,使用@Rentention时必须为该value成员变量指定值。

  • RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释,在.class文件中不会保留注解信息;
  • RetentionPolicy.CLASS:在.class文件中有效(即class保留),保留在.class文件中,但是当运行Java程序时,他就不会继续加载了,不会保留在内存中,JVM不会保留注解。如果注解没有加Retention元注解,那么相当于默认的注解就是这种状态
  • RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行 Java程序时,JVM会保留注释,加载在内存中了,那么程序可以通过反射获取该注释。

尝试将@Retention元注解的参数依次设置为RetentionPolicy.SOURCERetentionPolicy.CLASS,再次运行,观察日志文件,均未能记录日志信息,说明**@Check注解在主程序运行阶段并未生效,只有将其设置为RetentionPolicy.RUNTIME,注解才生效,才会记录日志信息**。

  • @Retention元注解的参数设置为RetentionPolicy.SOURCE输出的日志文件:
@java.lang.annotation.Retention(SOURCE)
本次测试的类:annotation.testframe.Calculators
本次测试一共出现 0 次异常。
测试时间:2023-01-10 14:24:29 周二 下午
==================================
  • @Retention元注解的参数设置为RetentionPolicy.CLASS输出的日志文件:
@java.lang.annotation.Retention(CLASS)
本次测试的类:annotation.testframe.Calculators
本次测试一共出现 0 次异常。
测试时间:2023-01-10 14:24:46 周二 下午
==================================


相关实践学习
日志服务之数据清洗与入湖
本教程介绍如何使用日志服务接入NGINX模拟数据,通过数据加工对数据进行清洗并归档至OSS中进行存储。
目录
相关文章
|
3天前
|
设计模式 测试技术 Python
《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
【7月更文挑战第10天】Page Object Model (POM)是Selenium自动化测试中的设计模式,用于提高代码的可读性和维护性。POM将每个页面表示为一个类,封装元素定位和交互操作,使得测试脚本与页面元素分离。当页面元素改变时,只需更新对应页面类,减少了脚本的重复工作和维护复杂度,有利于团队协作。POM通过创建页面对象,管理页面元素集合,将业务逻辑与元素定位解耦合,增强了代码的复用性。示例展示了不使用POM时,脚本直接混杂了元素定位和业务逻辑,而POM则能解决这一问题。
21 6
|
1天前
|
监控 Java 开发者
Spring Boot框架在java领域的优势
随着云计算、微服务架构的兴起,Java开发领域迫切需要一套高效、灵活且易于上手的框架来应对日益复杂的业务需求。正是在这样的背景下,Spring Boot应运而生,以其独特的魅力迅速成为了Java开发者手中的利器。
9 3
|
1天前
|
敏捷开发 存储 数据管理
自动化测试框架设计:从理论到实践
【7月更文挑战第13天】本文将深入探讨自动化测试框架的设计原理与实现方法。通过分析自动化测试的必要性和框架设计的基本原则,结合具体案例,展示如何从零开始构建一个高效、可维护的自动化测试系统。文章不仅涵盖框架的结构设计,还包括最佳实践和常见问题的解决策略,为读者提供一套完整的解决方案和实操指南。
|
20小时前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
【7月更文挑战第12天】在本文中,作者宏哥介绍了如何在不使用PageFactory的情况下,用Java和Selenium实现Page Object Model (POM)。文章通过一个百度首页登录的实战例子来说明。首先,创建了一个名为`BaiduHomePage1`的页面对象类,其中包含了页面元素的定位和相关操作方法。接着,创建了测试类`TestWithPOM1`,在测试类中初始化WebDriver,设置驱动路径,最大化窗口,并调用页面对象类的方法进行登录操作。这样,测试脚本保持简洁,遵循了POM模式的高可读性和可维护性原则。
11 2
|
2天前
|
安全 前端开发 Java
Java技术栈中的核心组件:Spring框架
Java作为一门成熟的编程语言,其生态系统拥有众多强大的组件和框架,其中Spring框架无疑是Java技术栈中最闪耀的明星之一。Spring框架为Java开发者提供了一套全面的编程和配置模型,极大地简化了企业级应用的开发流程。
8 1
|
3天前
|
敏捷开发 Devops 测试技术
自动化测试框架的演进与实践
【7月更文挑战第11天】在软件开发的历程中,自动化测试始终扮演着不可或缺的角色。本文将通过探讨自动化测试框架的发展脉络,揭示其在现代软件工程中的应用与挑战。从早期的线性脚本到今日的模块化框架,我们将一窥自动化测试技术的演进之路,并分享实践中的经验和策略,旨在为读者提供一套实用的自动化测试解决方案。
4 1
|
2天前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
【7月更文挑战第11天】页面对象模型(POM)通过Page Factory在Java Selenium测试中被应用,简化了代码维护。在POM中,每个网页对应一个Page Class,其中包含页面元素和相关操作。对比之下,非POM实现直接在测试脚本中处理元素定位和交互,代码可读性和可维护性较低。
|
3天前
|
Java 调度
Java线程的六种状态
Java线程有六种状态: 初始(NEW)、运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)、终止(TERMINATED)。
13 1
|
3天前
|
存储 安全 Java
Java面试题:请解释Java内存模型(JMM)是什么,它如何保证线程安全?
Java面试题:请解释Java内存模型(JMM)是什么,它如何保证线程安全?
35 13
|
3天前
|
缓存 安全 Java
Java中线程池如何管理?
【7月更文挑战第11天】Java中线程池如何管理?
8 2