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 . . .");
}
}