Junit单元测试&反射&注解

本文详细介绍了Java中的Junit单元测试,包括测试分类、白盒测试的步骤、结果判定和补充内容。接着讲解了反射的概念、获取Class对象的方式及其功能,如获取成员变量、构造方法和方法。最后,文章阐述了注解的基本概念,包括元注解和自定义注解的使用,强调了注解在程序分析和编译检查中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Junit单元测试

1.1测试的分类

  1. 黑盒测试:不需要写代码,给输入值,看程序是否能输出期望的值
  2. 白盒测试:需要写代码的。关注程序具体的执行流程

1.2 Junit的使用:白盒测试

步骤:

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

    建议:

    测试类名:被测试的类名+Test CalculateTest

    包名:xxx.xxx.xxx.test com.fun.test

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

    建议:

    方法名:test测试的方法 testAdd()

    返回值:void

    参数列表:建议无参

  3. 给方法加@Test

  4. 导入Junit的依赖环境

package com.fun.junit;

/**
 * @ClassName Calculate
 * @Description 计算器类
 * @Author 23389
 * @Date 2021/7/24 10:28
 * @Version 1.0
 */

public class Calculate {
    /*
         * @Author 23389
         * @Description //加法
         * @Date 10:38 2021/7/24
         * @Param [a, b]
         * @return int
         **/

    public int add(int a,int b){
        //int i = 3/0;
        return a + b;
    }

    /*
         * @Author 23389
         * @Description //减法
         * @Date 10:40 2021/7/24
         * @Param [a, b]
         * @return int
         **/

    public int sub(int a,int b){
        return a - b;
    }
}

package com.fun.test;

import com.fun.junit.Calculate;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * @ClassName CalculateTest
 * @Description TODO
 * @Author 23389
 * @Date 2021/7/24 10:50
 * @Version 1.0
 */
public class CalculateTest {
    //初始化方法,用于资源的申请,所有测试方法在执行前都会先执行此方法

    @Before
    public void init(){
        System.out.println("init...");
    }

    @Test
    public void testAdd(){
        //System.out.println("我被执行了");
        System.out.println("testAdd...");
        //1.创建计算器对象
        Calculate c = new Calculate();

        //2.调用add方法
        int result = c.add(1, 2);
        //System.out.println(result);

        //3.断言操作 我断言这个结果是3
        Assert.assertEquals(3,result);

    }

    @Test
    public void testSub(){
        System.out.println("testAdd...");
        //创建计算器对象
        Calculate c = new Calculate();
        //调用方法
        int result = c.sub(3, 5);

        //断言操作
        Assert.assertEquals(-2,result);
    }

    //释放资源,在所有测试方法执行完毕后,都会自动执行此方法

    @After
    public void close(){
        System.out.println("close.....");
    }

}

1.3判定结果

红色代表失败,绿色代表成功

一般会使用断言操作类处理结果

Assert.assertEquals(期望的结果,实际得到的结果);

1.4补充

@Before:修饰的方法会在测试方法之前自动执行

@After:修饰的方法会在测试方法之后自动执行

2.反射

2.1相关概念及图解

反射:框架的灵魂

框架:半成品软件。可以在框架的基础上进行软件的开发,简化代码

反射:将类的各个组成部分封装为其他对象,这就是反射机制

反射的好处:

  1. 可以在程序运行过程中,操作这些对象。
  2. 可以解耦,提高程序的可扩展性

java代码的三个阶段:
Java代码的三个阶段

2.2 获取Class对象的方式

  1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象

    多用于配置文件,将类名定义在配置文件中。读取文件,加载类

  2. 类名.class:通过类名的属性class来获取

    多用于参数的传递

  3. 对象.getClass():getClass()方法在Object类中定义

    多用于对象的获取字节码的方式

结论:同一个字节码文件(*.class)在同一个程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

2.3 Class对象的功能:

获取功能:

  1. 获取成员变量们

    Field[] getFields() :获取所有public修饰的成员变量

    Filed getFiled(String name):获取指定名称的public修饰的成员变量

    Field[] getDeclaredFields() :获取所有的成员变量,不考虑修饰符

    Filed getDeclaredField(String name):

  2. 获取构造方法们

    Constructor<?>[] getConstructors()

    Constructor getConstructor(类<?>… parameterTypes)

    Constructor<?>[] getDeclaredConstructors()

    Constructor getDeclaredConstructor(类<?>… parameterTypes)

  3. 获取成员方法们

    Method[] getMethods()

    Method getMethod(String name, 类<?>… parameterTypes)

    Method[] getDeclaredMethods()

    Method getDeclaredMethod(String name, 类<?>… parameterTypes)

  4. 获取类名

    String getName()

Field:成员变量

操作:

  1. 设置值

    void set(Object object,Object value)

  2. 获取值

    get(Object obj)

  3. 忽略访问权限修饰符的安全检查

    setAccessible(true) :暴力反射

Constructor:构造方法

创建对象:

  • T newInstance(Object… initargs)
  • 如果使用空参数构造方法来创建对象,操作可以简化:Class对象的newInstance方法

Method:方法对象

执行方法:

  • Object invoke(Object obj, Object… args)

获取方法的名称:

  • String getName()

2.4案例

需求:写一个“框架“,可以帮我们取创建任意类的框架,并执行其中任意方法

实现:

  1. 配置文件
  2. 反射

步骤:

  1. 将需要创建的对象的全类名以及需要执行的方法定义在配置文件中
  2. 在程序中去加载读取文件
  3. 使用反射技术来加载类文件进内存
  4. 创建对象
  5. 执行方法

学生类:

package com.fun.domain;

/**
 * @ClassName Student
 * @Description TODO
 * @Author 23389
 * @Date 2021/7/24 15:36
 * @Version 1.0
 */
public class Student {
    public void sleep(){
        System.out.println("早睡早起身体好");
    }
}

配置文件:

className=com.fun.domain.Student
methodName=sleep
package com.fun.reflect;

import com.fun.domain.Person;
import com.fun.domain.Student;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * @ClassName ReflectTest
 * @Description 案例实现
 * @Author 23389
 * @Date 2021/7/24 17:00
 * @Version 1.0
 */
public class ReflectTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //可以创建任意类的对象,可以执行任意方法
        
        /*
             不能改变该类的任何代码
             **/

//        Person p = new Person();
//        p.eat();
//        Student s = new Student();
//        s.sleep();

        //1.1创建Properties对象
        Properties properties = new Properties();
        //1.2加载配置文件,转化为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        properties.load(is);

        //2.获取配置文件中定义的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //3.加载该类进内存
        Class<?> aClass = Class.forName(className);

        //4.创建对象
        Object obj = aClass.newInstance();

        //5.获取方法对象
        Method declaredMethod = aClass.getDeclaredMethod(methodName);

        //6.方法执行
        declaredMethod.invoke(obj);
    }
}

3.注解

3.1 相关概念

  • 注解:说明程序的,给计算机看的

  • 注释:用文字去描述程序的。给程序员看的

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

  • 概念描述:

    • JDK1.5之后的新特性
    • 说明程序的
    • 使用注解:@注解名称
  • 作用分类:
    ①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
    ② 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
    ③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

  • JDK中预定义的ixie注解

    @Override:检测被该注解标记的方法是否继承自父类(接口)的

    @Deprecated:该注解标注的内容,已过时

    @SuppressWarnings:压制警告,一般传递参数all,@SuppressWarnings(“all”)

  • 自定义注解

    • 格式:

    ​ 元注解

    ​ public @interface 注解名称 {

    99 属性列表;

    ​ }

    • 本质:注解本质上是就是一个接口,该接口默认继承自Annotation接口

    ​ public interface MyAnnotation extends java.lang.annotation.Annotation {
    ​ }

    • 属性:接口中可以定义的成员方法

      要求:

      1.属性的返回值类型有下列取值

      ​ 基本数据类型

      ​ 字符串

      ​ 枚举

      ​ 注解

      ​ 以上类型的数组

      2.定义了属性,在使用时需要给属性赋值

      自定义注解:

      package com.fun.annotation;
      
      public @interface MyAnnotation {
          int value();
      //    String name() default "张三";
          Person per();
          MyAnno2 anno2();
          String[] strs();
      
      }
      
      package com.fun.annotation;
      
      /**
       * @ClassName Worker
       * @Description 注解使用格式
       * @Author 23389
       * @Date 2021/7/24 19:20
       * @Version 1.0
       */
      @MyAnnotation(value = 12,per = Person.p1,anno2 = @MyAnno2(),strs = {"abc","bcd","efg"})
      public class Worker {
      }
      
      

      1.如果定义属性时,使用default关键字给属性默认初始值,则使用注解时可以不进行属性的赋值

      2.如果只有一个属性需要赋值,并且属性的名称是value,并且属性的名称是value,则value可以省略,直接定义值即可

      3.数组赋值时,值使用{}包裹。如果数组只有一个值,{}可以省略

    • 元注解:用于描述注解的注解

      @Target:描述注解能够作用的位置

      ElementType取值:

      ​ TYPE:可以作用于类上

      ​ Method:可以作用于方法上

      ​ Field:可以作用于变量上

      @Retention:描述注解能被保留的阶段

      ​ @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到

      @Document:描述注解是否被抽取到api文档中

      @Inherited:描述注解是否被子类继承

  • 在程序中使用(解析)注解:获取注解中定义的属性值

    1. 获取注解定义的位置的对象(Class,Method,Field)

    2. 获取指定的注解

      getAnnotation(class)

      //其实就是在内存中生成了一个该注解接口的子类实现对象

        public class ProImpl implements Pro{
          public String className(){
          return "con.fun.annotation.Demo1";
          }
          public String methodName(){
          return "show";
          }
       }
      
    3. 调用注解中的抽象方法获取配置的属性值

    3.2 实例

    简单的测试框架

    package com.fun.annotation.demo;
    
    /**
     * @ClassName Calculator
     * @Description 计算器类
     * @Author 23389
     * @Date 2021/7/25 10:52
     * @Version 1.0
     */
    public class Calculator {
        //加法
        @Check
        public void add() {
            System.out.println("1 + 0 =" + (1 + 0));
        }
        //减法
        @Check
        public void sub() {
            System.out.println("1 - 0 = " + (1 - 0));
        }
        //乘法
        @Check
        public void mul(){
            System.out.println("1 * 0 = " + (1 * 0));
        }
        //除法
        @Check
        public void div(){
            System.out.println("1 / 0 =" + (1 / 0)  );
        }
    
        public void show(){
            System.out.println("永无bug");
        }
    
    }
    
    
    package com.fun.annotation.demo;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Check {
    }
    
    
    package com.fun.annotation.demo;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.lang.reflect.Method;
    
    /**
     * @ClassName TestCheck
     * @Description 简单测试框架,检测方法是否有异常并记录
     * @Author 23389
     * @Date 2021/7/25 11:00
     * @Version 1.0
     */
    public class TestCheck {
        public static void main(String[] args) throws IOException {
            //1.创建计算器对象
            Calculator c = new Calculator();
            //2.获取字节码文件对象
            Class  cls = c.getClass();
    
            //3.获取所有的方法
            Method[] methods = cls.getMethods();
            int number = 0;//出现异常的次数
            BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
    
            for (Method method : methods) {
                //4.判断方法上是否有Check注解
                if(method.isAnnotationPresent(Check.class)){
                    //5.有则执行
                    try{
                        method.invoke(c);
                    }catch (Exception e){
                        //6.捕获异常,记录到文件
                        number ++;
                        bw.write(method.getName() + "方法出异常了");
                        bw.newLine();
                        bw.write("异常的名称:"  +e.getCause().getClass().getSimpleName() );
                        bw.newLine();
                        bw.write("异常的原因:" + e.getCause().getMessage());
                        bw.newLine();
                        bw.write("---------");
                        bw.newLine();
                        bw.flush();
                    }
                }
            }
            bw.write("本次测试一共出现"+number + "次异常");
            bw.flush();
            bw.close();
    
        }
    
    }
    
    

    3.3 小结

    1. 以后大多数时候,我们会使用注解,而不是自定义注解
    2. 注解给谁用?
      • 给编译器用
      • 给解析程序用
    3. 注解不是程序的一部分,可以理解为注解就是一个标签

    ``

    3.3 小结

    1. 以后大多数时候,我们会使用注解,而不是自定义注解
    2. 注解给谁用?
      • 给编译器用
      • 给解析程序用
    3. 注解不是程序的一部分,可以理解为注解就是一个标签
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值