Junit

本文详细介绍了Java中的Junit单元测试,包括黑盒测试与白盒测试的概念,以及如何使用Junit进行白盒测试。接着,讨论了反射的概念、好处以及如何在运行时操作类的成员。此外,还深入讲解了注解的作用,包括元注解、自定义注解及其应用场景。文中通过实例展示了注解在测试和动态运行代码中的应用。
摘要由CSDN通过智能技术生成

Java之Junit+反射补充+注解

一、Junit单元测试

1.1 测试方式

测试方式:

黑盒:黑盒测试,不知道具体实现逻辑,给输入值,单纯测试代码实现的功能

白盒:白盒测试,知道逻辑,根据逻辑测试,看能否实现功能或者是否有错误。

1.2 Junit使用:白盒测试

  • 步骤:

    1. 定义一个测试类(测试用例)

      ​ 建议:

      • **测试类名:xxxTest **

      • 包名:xxx.xxx.xxx.test

    2. 定义测试方法:可以独立运行

      建议:

      • 方法名:test测试的方法名 testxxx( )

      • 其返回值:void

      • 参数列表:空参

    3. 给方法加 @test

    4. 加org.Junit.Test包

  • 判断结果:

    • 红色:失败
    • 绿色:成功
    • 一般来说我们会使用断言操作来处理结果,而不是输出结果
      • 断言:Assert.assertEquals(期望的结果,运算的结果)
  • 另外两个测试用注解:

    • @Before:修饰的方法会在测试方法前被自动执行。(常用于变量初始化)
    • @After:修饰的方法会在测试方法执行之后自动被执行(常用于关闭资源)

例子:

//Junit单元测试
import org.junit.Before;
import org.junit.Test;

import java.util.Scanner;

public class Junitdemo01 {
    Scanner scanner =new Scanner(System.in);
/*
*测试test1方法
*/
@Test
@Check

public void  test01(){
    
    System.out.println("test01已被执行!");
}

@Before
public void chushihua(){
    int x = 20;
}
public  void close(){
    scanner.close();
}

}

二、反射

反射的概念:什么是反射?

反射是将一个类的各个部分(构造器,成员变量,成员方法)进行封装为其他对象。

  • 好处:可以在程序运行过操中,操作这些对象。
  • 可以解耦,提高程序的可扩展性。

具体的代码可以看java基础之反射

java代码在计算机中的三个阶段:

在这里插入图片描述

对应的,有三种获取Class的方法

  1. 源代码阶段,通过Class.forName(“类名”);///多用于配置文件,读取文件,加载类
  2. 类对象阶段,类名.class ///多用于参数的传递
  3. 运行时阶段,对象.getClass() //多用于对象的获取自己码方式

同一个类,通过三种方式获取得到的是同一个Class对象。

通过Class对象可以获得这个类的三个部分:

  • 成员变量(Field)

  • 构造方法(Constructor)

  • 成员方法(Method)

可以选择获取返回是一个集或单个(须给定参数来确定是哪一个)。

三、注解

1.注解概念

注解:用于给计算机说明程序的。

注释:用于 给程序员说明程序的。

注解的定义:注解(Annotation),也叫元数据,是一种代码级别的说明,它在jdk1.5以后版本引入一个特性,与类,接口,枚举是同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用于对这些元素进行说明,注释。

作用分类:

  • 编写文档:通过代码里标记的元数据生成文档 [生成文档doc文档]
  • 代码分析:通过代码里标识的元数据对代码进行分析 [使用反射]
  • 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查 [@Override]

常见注解:

  1. @Override:检查被注解标注的方法是否继承自父类或接口的。
  2. @Depercated:改注解标注的内容表示已过时。
  3. @SuppressWarnings:压制警告(需要传参:一般传‘all’,即@SuppressWarnings(“all”) )

2.自定义注解:

自定义注解格式:

元注解

public+@interface 注解名{ }


本质:注解本质是接口,该接口默认继承

  • public interface 注解名 extends java.lang.annotation.Annotation{}

属性:接口中的抽象方法

属性的返回值类型:

  • 基础数据类型
  • String
  • 枚举
  • 注解
  • 以及以上类型对应的数组类型。
 枚举类型的定义:
public enum week {
    星期一, 星期二,星期三,星期四,星期五,星期六,星期天
}

**元注解:**元注解是用以描述注解的注解

  • @Target:描述注解能够作用的位置
    • ElementType取值:(可同时取)
      • TYPE:可以作用于类上
      • METHOD:可以作用于方法上
      • FIELD:可以作用于成员变量上
  • @Retention:描述注解被保留的阶段
    • RetentionPolicy取值:
      • SOURCE :源码阶段:表示被描述的注解,不会被Class字节码文件保存
      • CLASS:类加载器阶段:表示被描述的注解,会被保留到Class字节码文件中
      • RUNTIME:运行时阶段:表示被描述的注解,会被保留到Class字节码文件中,并被JVM读取到
  • @Documented:描述注解是否被抽取到API文档中
  • @Inherited:描述注解是否被子类继承
import java.lang.annotation.*;

//**元注解:**元注解是用以描述注解的注解
//
//* @Target:描述注解能够作用的位置
//* @Retention:描述注解被保留的阶段
//* @Documented:描述注解是否被抽取到API文档中
//* @Inherited:描述注解是否被子类继承
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
//被描述方法能作用的位置
@Retention(RetentionPolicy.RUNTIME)     //被保留的阶段
@Deprecated       //被保存到api文档中
@Inherited       //可以自动被子类继承
public @interface MyAnno {
/*    int  show1();   //使用时是 @Myanno(show1=10),这样的表达方式来使用。
    int value();    //这个value很特殊,若一个注解类里只要它要赋值的时候就不需要用键值对的方式表示,直接括号给值就ok了
    String  show2() default "lys";//默认赋值,有默认赋值可以不进行赋值。
    Override anno2();
    enum show3{};
    String[] show4();   //枚举和数组在赋值时需要用大括号{},若只有一个,则可以用小括号()*/

}

简单来说注解作用:更多是作为一个”标签“来使用。

  1. 标记某个东西(类,方法,变量),被解析程序使用 //比如以下两个例子
  2. 编译器 //比如被javadoc利用

例子1:

使用自定义注解标记方法,检查方法是否异常。(简单方法测试框架)

//注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
    
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;

//Check注解的解析程序
public class CheckTest {
    public static void main(String[] args) throws IOException {
        //先获取check属性值
        Junitdemo01 j1 = new Junitdemo01();
        //对应的字节码文件
        Class<? extends Junitdemo01> c1 = j1.getClass();
        //通过字节码文件获取其方法集
        Method[] methods = c1.getMethods();

        int count=0;
        BufferedWriter bf = new BufferedWriter(new FileWriter("bug.txt"));
        //方法集解集
        for (Method m:methods){
            //获取被check注解标记的方法
            if (m.isAnnotationPresent(Check.class)){//isAnnotationPresent是用于判断是否被注解标记,参数是注解.class
                try {
                    //调用方法
                    m.invoke(j1);
                    //捕抓方法异常
                } catch (Exception e) {
                    count++;
                    bf.write(m.getName()+"方法发送异常");
                    bf.newLine();
                    bf.flush();
                    bf.write("异常名称:"+e.getCause().getClass().getSimpleName());
                    bf.newLine();
                    bf.flush();
                    bf.write("异常原因:"+e.getCause().getMessage());
                    bf.newLine();
                    bf.flush();
                    bf.write("-------------------------");
                    bf.newLine();
                }
            }
        }
        bf.write("共有"+count+"个方法发送了异常");
        bf.close();
        if (count!=0){
            System.out.println("请前往bug.txt文件查看发生的异常");
        }
    }
}

只要运行checktest程序即可检查被chcek注解标记的程序是否异常,并把异常记录到bug.txt文件中。

例子2:

反射配合注解完成能运行任意方法的程序。(简单方法运行框架)

//注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CNameAndMName {
    String className();
    //其实就等同于
    /* pubilc String className( String  className){
            return  ClassName
        }*/
    String methodName();
}
//测试类
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//通过给注解的字段赋值,从而获取注解对象进而获取字段的信息
//就不用通过IO流获取Class名和方法名了
@CNameAndMName(className = "com.lys.heima.junitdemo.Junitdemo01",methodName = "test01")
public class ALLCanRun {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        //获取我现在这个文件的字节码文件
        Class<ALLCanRun> c1 = ALLCanRun.class;
        //把这个注解对象拿过来创建
        CNameAndMName anno = c1.getAnnotation(CNameAndMName.class);
        //拿这个注解的的两个字段的内容
        String className = anno.className();
        String methodName = anno.methodName();
        //通过字段获取class文件:
        Class<?> appclass = Class.forName(className);
        //创建进程对象
        Object appobj = appclass.newInstance();
        //指定给定方法
          Method appclassMethod = appclass.getMethod(methodName);
       // Method appclassMethod = appclass.getDeclaredMethod(methodName);//把私有方法也拿了+上取消访问检查,私有方法也能被运行了。
       // appclassMethod.setAccessible(true);//取消访问检查
         //运行方法
        appclassMethod.invoke(appobj);
    }
}


 = appclass.newInstance();
        //指定给定方法
          Method appclassMethod = appclass.getMethod(methodName);
       // Method appclassMethod = appclass.getDeclaredMethod(methodName);//把私有方法也拿了+上取消访问检查,私有方法也能被运行了。
       // appclassMethod.setAccessible(true);//取消访问检查
         //运行方法
        appclassMethod.invoke(appobj);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YeungSLee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值