JavaWeb - Junit白盒测试、反射、注解

1.Junit白盒测试

  *步骤:

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

      *建议:

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

        *包名:xxx.xxx.xxx.test     cn.itcast.test

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

      *建议:

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

        *返回值:void 

        *参数列表:建议空参

    3.给方法加@Test

    4.导入Junit依赖环境 

    判定结果:

      *红色:失败

      *绿色:成功

      *一般我们会使用assert断言操作来处理结果

        Assert.assertEquals(期望值,真实值)进行比对

public class CalculatorTest {
    /*
        测试add方法
    */

    @Test
    public void testAdd(){
        //System.out.println("我被执行了");
        /*
        * 1.创建计算器对象
        * 2.调用add方法
        * */
        Calculator c = new Calculator();
        int result = c.add(1, 2);
        System.out.println(result);  //无意义,如果是写成了减法依旧测试成功

        //3.断言 结果为3
        Assert.assertEquals(4,result);
    }
}

 资源申请与资源释放:(@Before 和 @After)

public class CalculatorTest {
    /*
    * 初始化方法
    *  用于资源申请,所有测试方法在执行之前都会先执行该方法
    * */
    @Before
    public void init(){
        System.out.println("init...已被执行");
    }
    /*
    * 释放资源的方法
    *   在所有测试方法执行完后都会自动执行该方法
    * */
    @After
    public void close(){
        System.out.println("close");
    }
}

 

 2.反射:框架设计的灵魂

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

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

    *好处:

      1.可以在程序运行过程中,操作这些对象。

      2.可以解耦,来提高程序的可扩展性

 

获取Class对象的方式:  

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

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

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

    *多用于参数的传递

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

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

public class DemoReflect {
    public static void main(String[] args) throws Exception {
        //第一种方式 Class.forName  
        Class cls1 = Class.forName("basicpart.day01.FaceObj.Zi");
        System.out.println(cls1);

        //第二种方式 类名.class
        Class cls2 = Zi.class;
        System.out.println(cls2);

        //第三种方式 对象.getClass
        Zi zi = new Zi();
        Class cls3 = zi.getClass();
        System.out.println(cls3);

        System.out.println(cls1==cls2);
    }
}

 

  结论:

    同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过那一种方式获取的class文件只有一个

3.Class对象的功能

  获取功能:

    1.获取成员变量们

    操作:Field成员变量

          1.设置值 void set(Object obj,Object value)

          2.获取值 get(Object obj)

      *Field【】  getFields():获取所有public修饰的成员变量

      *Field【】  getField(String name):获取指定名称的public修饰的成员变量

public class DemoReflect {
    public static void main(String[] args) throws Exception {
        //获取一个字节码对象
        Class<Person> personClass = Person.class;
        //获取指定的成员变量对象
        Field field = personClass.getField("name");

        //创建一个对象
        Person p  = new Person();
        //获取值
        Object value = field.get(p);
        System.out.println(value);

        //设置值
        field.set(p,"chris");
        System.out.println(p);

    }
}

      *Field【】  getDeclaredFields():获取所有的成员变量,不考虑权限修饰符

      *Field【】  getDeclaredField(String name)在访问之前要先忽略访问权限修饰符 暴力反射 setAccessible(true)

 

        Field age = personClass.getDeclaredField("age");
        //在访问之前需要先忽略权限访问修饰符
        age.setAccessible(true);//暴力反射
        Object value2 = age.get(p);
        System.out.println(value2);            

 

 

 

         

    2.获取构造方法们

      *Constructor<?>【】 getConstructors()

      *Constructor<T>【】 getConstructor(类<?>...parameterTypes)

 

      *Constructor<T>【】 getDeclaredConstructor(类<?>...parameterTypes)

      *Constructor<?>【】 getDeclaredConstructors()

      *Constructor用于创造对象  newInstance()方法 

        如果使用空参数构造方法创建对象,操作可以简化:Class对象有个newInstance()方法

public class Demo2 {
    public static void main(String[] args) throws Exception {
        //构造器的使用
        Class<Person> personClass = Person.class;
        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);

        //创建对象
        Person chris = constructor.newInstance("chris", 21);
        System.out.println(chris);
        
        //空参创建
        Person person = personClass.newInstance();
    }
}

 

    3.获取成员方法们

      *Method【】 getMethods()

      *Method【】 getMethod(String name,类<?>...parameterTypes)

 

      *Method【】 getDeclaredMethods()

      *Method【】 getDeclaredMethod(String name,类<?>...parameterTypes)

      *方法对象的功能:*执行方法 Object invoke(Object obj,Object...args)

               *获取方法名  String getName();

public class Demo2 {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        //获取方法对象
        Method eat_method = personClass.getMethod("eat");
        //执行无参方法
        //先创建确定的对象
        Person p = new Person();
        eat_method.invoke(p);

        //执行有参的fangfa
        Method eat_method2 = personClass.getMethod("eat", String.class);
        eat_method2.invoke(p,"苹果");

    }
}

    4.获取类名

      *String getName()

 

案例:不能改变该类的任何代码,可以创建任意类的对象,执行任意方法

/*
* 框架类
* */
public class DemoStruct {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法

        /*
            不能改变该类的任何代码,可以创建任意类的对象,执行任意方法
        */
        //1.加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = DemoStruct.class.getClassLoader();//获取该字节码文件的类加载器
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);

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

        //3.加载该类进内存
        Class cls = Class.forName(className);

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

        //5.获取方法对象
        Method method = cls.getMethod(methodName);

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

 

4.注解

/**
 *  javadoc文档    
 * @author chris
 * @since jdk1.5
 * @version 1.0
 */
public class annotation {

    /**
     * 文档注释
     * @param a 整数
     * @param b 整数
     * @return 两数的和
     */
    public int add(int a , int b){
        return a + b ;
    }
}

 

JDK内置注解

  1.限定父类重写方法:@Override  - 检测该注解标注的方法是否是继承自父类(接口)的

  2.标示已过时:@Deprecated - 将该注解标注的内容,表示已过时

  3.抑制编译器警告:@SuppressWarnings - 压制警告的  一般传递参数all

@SuppressWarnings("all")  //压制了该类右侧的所有警告
public class annoDemo2 {
    @Override
    public String toString() {
        return super.toString();
    }

    @Deprecated
    public void show1(){
        //有缺陷
    }
    public void show2(){
        //替代show1方法
    }
}

 

自定义注解:

  (1)*格式:

    *元注解  -  用于描述注解的注解

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

          ElementType取值:1.TYPE 类上  2. METHOD 方法上 3.FIELD 成员变量上 

          @Target(value = {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})

      (2)@Retention:描述注解被保留的阶段  (源码,class,runtime)

          @Retention(RetentionPolicy.RUNTIME) 该注解会保留到class字节码文件中,并被JVM读取到

      (3)@Documented:描述注解是否被抽取到api文档

          @Documented

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

    *public @interface 注解名称{

          属性列表;  

          }

  (2)*本质:注解本质上就是一个接口,该接口默认继承Annotation接口

    *public interface MyAnno extends java.lang.annotation.Annotation { }

  (3)*属性:接口中的抽象方法

    *要求:

      1.属性的返回值类型

        *基本数据类型

        *字符串String

        *枚举

        *注解

        *以上类型的数组

      2.定义了属性,在使用时需要给属性赋值 @MyAnno(show = "cc")   也可在注解里 default 设置默认值 

        如果只有一个属性需要赋值,而且属性的名称是value,则value可以省略

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

 

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

  1.获取注解定义位置的对象,定义在类上就获取类,定义在方法上就获取方法 (Class,Method,Field)

  2.获取指定的注解对象  getAnnotation(xxx.class)

    其实就是在内存中生成了该注解接口的子类实现对象,方法中的返回值就是刚刚定义的值

  3.调用注解中的抽象方法获取配置的属性值

/*
* 框架类
* */
@ProAnno(ClassName = "JavaWeb.reflect.Person", MethodName = "eat")
public class AnnoComple {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法

        /*
            不能改变该类的任何代码,可以创建任意类的对象,执行任意方法
        */

        //1.解析注解
            //1.1获取注解定义的该类的字节码文件对象 本类
        Class<AnnoComple> annoCompleClass = AnnoComple.class;

        //2.获取上边的注解对象
        //其实就是在内存中生成了该注解接口的子类实现对象,方法中的返回值就是刚刚定义的值
        ProAnno an = annoCompleClass.getAnnotation(ProAnno.class);

        //3.调用注解对象定义的抽象方法来获取返回值
        String className = an.ClassName();
        String methodName = an.MethodName();

        System.out.println(className);
        System.out.println(methodName);

        //3.加载该类进内存
        Class cls = Class.forName(className);

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

        //5.获取方法对象
        Method method = cls.getMethod(methodName);

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

 

测试框架案例:

  check注解:

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

 

  测试框架:

/*
    简单的测试框架
    当主方法执行后,会自动执行被检测的所有方法(加了check注解的方法),判断方法是否有异常,记录到文件中
*/
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.write("本次测试一共出现了"+number+"次异常");

        bw.flush();
        bw.close();
    }
}

 

 

被测试的方法:

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 div(){
        System.out.println("1 / 0 = " + (1 / 0));
    }
    public void show(){
        System.out.println("永无bug . . .");
    }
}

 

posted @ 2020-05-24 10:17  五号世界  阅读(552)  评论(0编辑  收藏  举报