单元测试
介绍
Junit单元测试是做什么的?
- 就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。
Junit单元测试框架
- 可以用来对方法进行测试,它是由Junit公司开源出来的
Junit单元测试的优点是什么?
- 可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
- 不需要程序员去分析测试的结果,会自动生成测试报告出来。
- JUnit可以选择执行哪些测试方法,可以一键执行全部测试方法的测试。
- JUnit可以生测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
- 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
Junit框架快速入门(步骤)
断言Assert(静态类)
需求
- 某个系统,有多个业务方法,请使用Junit单元测试框架,编写测试代码,完成对这些方法的正确性测试。
具体步骤
- 将Junit框架的jar包导入到项目中(资料中已经提供)
- 为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值)
- 测试方法上必须声明@Test注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试;
- 开始测试:选中测试方法,右键选择“JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色
JUnit的注解有哪些?
(Junit 4.xxxx版本)
注解 | 说明 |
---|---|
@Test | 测试方法 |
@Before | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@After | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeClass | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
@AfterClass | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
- 在测试方法执行前执行的方法,常用于:初始化资源。
- 在测试方法执行完后再执行的方法,常用于:释放资源。
(Junit 5.xxxx版本)
注解 | 说明 |
---|---|
@Test | 测试类中的方法必须用它修饰才能成为测试方法,才能启动执行 |
@BeforeEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@AfterEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeAll | 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。 |
@AfterAll | 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。 |
- 开始执行的方法:初始化资源
- 执行完之后的方法:释放资源
反射
认识反射(框架 源码)
反射(Reflection):是加载类,并允许以编程的方式解剖类中的各种成分**(成员变量、方法、构造器等)**。
场景: 写一个方法,接收一个对象,然后打印出对象中的成员属性名
反射的基本作用、关键?
- 反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分。
- 反射的核心思想和关键就是:得到编译以后的class文件对象。
反射学什么
学习获取类的信息、操作它们
1、反射第一步:加载类,获取类的字节码:Class对象, 如此才可以解析类的全部成分
2、获取类的构造器:Constructor对象
3、获取类的成员变量:Field对象
4、获取类的成员方法:Method对象
反射第一步:加载类,获取类的字节码:Class对象
获取Class对象的三种方式
- 直接使用类名.class获取:Class c1 = 类名.class
- 调用Class提供的方法:Class c2 = Class.forName(“全类名”)
- 调用Object提供的方法:Class c3 = 对象.getClass()
获取类的构造器
获取类的构造器、并对其进行操作
Class提供了从类中获取构造器的方法。
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 获取所有的公共构造器(只能获取public修饰的) |
Constructor<?>[] getDeclaredConstructors() | 获取全部构造器(只要存在就能拿到) |
Constructor getConstructor(Class<?>… parameterTypes) | 获取某个公共构造器(只能获取public修饰的) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 获取某个构造器(只要存在就能拿到) |
获取类构造器的作用:依然是初始化对象返回
Constructor提供的方法 | 说明 |
---|---|
T newInstance(Object… initArgs) | 调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回 |
public void setAccessible(boolean flag) | 设置为true,表示禁止检查访问控制(暴力反射) |
反射得到的构造器可以做什么?
- 依然是创建对象的
- public newInstance(Object… initargs)
- 如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象
- setAccessible(boolean)
- 反射可以破坏封装性,私有的也可以执行了。
获取类的成员变量
Class提供了从类中获取成员变量的方法。
方法 | 说明 |
---|---|
public Field[] getFields() | 获取类的所有公共成员变量(只能获取public修饰的) |
public Field[] getDeclaredFields() | 获取类的全部成员变量(只要存在就能拿到) |
public Field getField(String name) | 获取类的某个公共成员变量(只能获取public修饰的) |
public Field getDeclaredField(String name) | 获取类的某个成员变量(只要存在就能拿到) |
获取到成员变量的作用:依然是赋值、取值
Field的方法 | 说明 |
---|---|
public void set(Object obj, Object value) | 赋值 |
public Object get(Object obj) | 取值 |
public void setAccessible(boolean flag) | 设置为true,表示禁止检查访问控制(暴力反射) |
- 如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值
- setAccessible(boolean)
获取类的成员方法
Class提供了从类中获取成员方法的API。
方法 | 说明 |
---|---|
Method[] getMethods() | 获取类的全部公共成员方法(只能获取public修饰的) |
Method[] getDeclaredMethods() | 获取类的全部成员方法(只要存在就能拿到) |
Method getMethod(String name, Class<?>… parameterTypes) | 获取类的某个公共成员方法(只能获取public修饰的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 获取类的某个成员方法(只要存在就能拿到) |
成员方法的作用:依然是执行
Method提供的方法 | 说明 |
---|---|
public Object invoke(Object obj, Object… args) | 触发某个对象的该方法执行。 |
public void setAccessible(boolean flag) | 设置为true,表示禁止检查访问控制(暴力反射) |
反射得到成员方法可以做什么?
-
依然是在某个对象中触发该方法执行。
- Object invoke(Object obj, Object… args)
-
如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行
- setAccessible(boolean)
反射为何可以给约定了泛型的集合存入其他类型的元素?
- 编译成Class文件进入运行阶段的时候,泛型会自动擦除。
- 反射是作用在运行时的技术,此时已经不存在泛型了。
作用、应用场景
反射的作用
- 可以在运行时得到一个类的全部成分然后操作。
- 可以破坏封装性。(很突出)
- 也可以破坏泛型的约束性。(很突出)
- 更重要的用途是适合:做Java高级框架
- 基本上主流框架都会基于反射设计一些通用技术功能。
使用反射做一个简易版的框架(案例)
需求:
- 对于任意一个对象,该框架都可以把对象的字段名和对应的值,然后打印在控制台。
实现步骤
- 定义一个方法,可以接收任意对象
- 每收到一个对象后,使用反射获取该对象的Class对象,然后获取全部的成员变量
- 遍历成员变量,然后提取成员变量在该对象中的具体值
- 把成员变量名、和其值,打印到控制台即可
注解
快速入门
注解(Annotation)
- 就是Java代码里的特殊标记,比如:@Override、@Test等
- 这些标记会被特定的注解解析器所发现,进而决定应该怎样去编译或者执行被注解标记出来的程序
//元注解
public @interface 注解名称 { }
注解本质是一个接口,Java中所有注解都是继承了Annotation接口的。元注解
指的是:修饰注解的注解,可以指定注解的标注位置和保留阶段@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Test { }@Target
作用:声明被修饰的注解只能在哪些位置使用
@Target(ElementType.TYPE)
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE, 局部变量@Retention
作用:声明注解的保留阶段
@Retention(RetentionPolicy.RUNTIME)
1. SOURCE 只作用在源码阶段,字节码文件中不存在。
2. CLASS(默认值)保留到字节码文件阶段,运行阶段不存在.
3. RUNTIME(开发常用)一直保留到运行阶段。
注解的作用
- 对Java中类、方法、成员变量做标记,然后进行特殊处理。
- 例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行
自定义注解
public @interface 注解名称 {public 属性类型 属性名() default 默认值 ;}
注解的解析
什么是注解的解析?
就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。
如何解析注解?
- 指导思想:要解析谁上面的注解,就应该先拿到谁
- 比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解
- 比如要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解
- Class 、 Method 、 Field , Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力
注解解析的方式
AnnotatedElement接口提供了解析注解的方法 | 说明 |
---|---|
public Annotation[] getDeclaredAnnotations() | 获取当前对象上面的注解。 |
public T getDeclaredAnnotation(Class annotationClass) | 获取指定的注解对象 |
public boolean isAnnotationPresent(Class annotationClass) | 判断当前对象上是否存在某个注解 |
解析注解的案例
需求如下
- 定位到指定包中所有带@ClassNameCheck注解的类,然后判断类名是否是以Heima开头,如果不是,就抛异常
下面分析一下步骤
- 定义一个类叫ClassNameCheckParser,在类中定义一个checkClassName方法,用来完成功能
- 获取项目中指定包下的所有类(此功能已经在工具类中提供好了方法,可以直接使用)
- 遍历得到每一个类,然后判断类上是否有@ClassNameCheck注解
- 如果有注解,则获取到类名进行判断,如果不符合规范,则放到一个准备好的集合中
- 等到遍历结束,查看集合中是否有元素;有就说明存在不符合规范的类名;打印结果,抛出异常
注解的属性
在定义注解的时候,还可以通过属性来进一步描述注解的细节
public @interface 注解名称 {public 属性类型 属性名() default 默认值 ;
}
特殊属性名: value
如果注解中只有一个value属性,使用注解时,value名称可以不写!!
解析注解的案例改进
需求如下:
- 在定位到有问题的类名的时候,要打印出类的开发者和作用
下面分析一下步骤:
- 修改@ClassNameLimit注解,添加author和value属性
- 修改解析器的类,解析出代码的开发者和作用并打印出来
Lombok
问题分析
lombok
Lombok是一个实用的Java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率。
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有的属性提供get/set方法 |
@ToString | 会给类自动生成易阅读的 toString 方法 |
@EqualsAndHashCode | 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data | 提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode) |
@NoArgsConstructor | 为实体类生成无参的构造器方法 |
@AllArgsConstructor | 为实体类生成除了static修饰的字段之外带有各参数的构造器方法。 |
注意: Lombok会在编译时,自动生成对应的java代码。我们使用lombok时,还需要安装一个lombok的插件(idea自带)