Java的单元测试和反射
# 单元测试和注解
# 单元测试
# 单元测试概述
单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。
单元测试是编写测试代码,应该准确、快速地保证程序基本模块的正确性。 JUnit是Java单元测试框架,已经在Eclipse中默认安装,如果使用IDEA可以使用@Test
的时候自动下载导入包。
单元测试是编写测试代码 (opens new window),应该准确、快速 (opens new window)地保证程序基本模块的正确性。
Junit单元测试框架
JUnit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用JUnit编写单元测试。
此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试,JUnit目前最新版本是5。
JUnit优点
- JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
- Junit可以生成全部方法的测试报告。
- 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
# 单元测试快速入门
需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
分析:
①将JUnit的jar包导入到项目中
- IDEA通常整合好了Junit框架,一般不需要导入。
- 如果IDEA没有整合好,需要自己手工导入如下2个JUnit的jar包到模块
①编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
②在测试方法上使用@Test注解:标注该方法是一个测试方法
③在测试方法中完成被测试方法的预期正确性测试。
④选中测试方法,选择“JUnit运行” ,如果 测试良好
则是 绿色
;如果 测试失败
,则是 红色
# 单元测试常用注解
JUnit4常用注解
JUnit4通过注解的方式来识别测试方法 (opens new window)。目前支持 (opens new window)的主要 (opens new window)注解有:
- @BeforeClass 全局只会执行一次,而且是第一个 (opens new window)运行
- @Before 在测试方法 (opens new window)运行之前运行
- @Test 测试方法 (opens new window)
- @After 在测试方法 (opens new window)运行之后允许
- @AfterClass 全局只会执行一次,而且是最后一个 (opens new window)运行
- @Ignor (opens new window)e 忽略此方法 (opens new window)
- 开始执行的方法:初始化资源。
- 执行完之后的方法:释放资源。
随便写一个用户的服务
public class UserService {
public String loginName(String loginName,String passWord){
if ("admin2".equals(loginName) && "123456".equals(passWord)){
return "登录成功";
}else
{
return "用户名或者密码有问题";
}
}
public void selectName(){
System.out.println(10/0);
System.out.println("查询全部用户名称成功");
}
}
使用单元测试类方法去测试用户服务检查是否有问题
package com.example.d1_junit;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TestUserService {
@Before
public void before(){
System.out.println("====执行之前执行====");
}
@After
public void after(){
System.out.println("====执行之后执行====");
}
// 添加注解
@Test
public void testLoginName(){
UserService userService = new UserService();
String rs = userService.loginName("admin2","123456");
// 进预期结果的正确性测试: 断言
Assert.assertEquals("您的功能业务可能出现问题","登录成功",rs);
}
}
# 反射
# 反射概述
- 反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
- 在运行时,可以直接得到这个类的构造器对象:Constructor
- 在运行时,可以直接得到这个类的成员变量对象:Field
- 在运行时,可以直接得到这个类的成员方法对象:Method
- 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
反射的关键
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。
HelloWorld.java -> javac -> HelloWorld.class
Class c = HelloWorld.class;
# 反射获取类对象
反射的第一步:获取Class类的对象
1.反射的第一步是什么?
- 获取Class类对象,如此才可以解析类的全部成分
2.获取Class类的对象的三种方式
- 方式一:
Class c1 = Class.forName("全类名");
- 方式二:
Class c2 = 类名.class
- 方式三:
Class c3 = 对象.getClass();
package com.example.d1_junit.demo2;
public class TestPreson {
public static void main(String[] args) throws Exception {
// Class类中的一个静态方法: forName(全限名: 包名 + 类名)
Class c = Class.forName("com.example.d1_junit.demo2.Person");
System.out.println(c);
// 类名.class
Class c1 = Person.class;
System.out.println(c1);
// 对象.getClass() 获取对象对应类的Class对象
Person p = new Person();
Class c2 = p.getClass();
System.out.println(c2);
}
}
运行结果:
class com.example.d1_junit.demo2.Person
class com.example.d1_junit.demo2.Person
class com.example.d1_junit.demo2.Person
# 反射获取构造器对象
使用反射技术获取构造器对象并使用
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
- Class类中用于获取构造器的方法
定义对象
package com.example.d1_junit.demo1;
public class Student {
private String name;
private int age;
private Student() {
System.out.println("无参构造器");
}
public Student(String name, int age) {
System.out.println("有参构造器");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
使用反射技术获取构造器的四种方法:
package com.example.d1_junit.demo1;
import org.junit.Test;
import java.lang.reflect.Constructor;
public class TestStudent1 {
// getConstructors
// 1.获取全部的构造器:只能获取public修饰的构造器
// Constructor[] getConstructors()
@Test
public void getConstructors(){
// 获取类对象
Class c = Student.class;
// 提取类中的全部构造器对象
Constructor[] constructors = c.getConstructors();
// 遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}
//
// 2.获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及
@Test
public void getDeclaredConstructors(){
// 获取类对象
Class c = Student.class;
// 提取类中的全部构造器对象
Constructor[] constructors = c.getDeclaredConstructors();
// 遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}
// 3.获取某个构造器:只能拿public修饰的某个构造器
@Test
public void getConstructor() throws Exception {
// 获取类对象
Class c = Student.class;
// 定位单个构造器对象 (按照参数定位无参构造器 只能获取public的构造器)
Constructor cons = c.getConstructor();
System.out.println(cons.getName() + "===>" + cons.getParameterCount());
}
// 4.获取某个构造器:只要你敢填写,这里就能拿到
@Test
public void getDeclaredConstructor() throws Exception {
// 获取类对象
Class c = Student.class;
// 定位单个构造器对象 (按照参数定位无参构造器)
Constructor cons = c.getDeclaredConstructor();
System.out.println(cons.getName() + "===>" + cons.getParameterCount());
// 定位某个有参构造器
Constructor cons1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(cons1.getName() + "===>" + cons1.getParameterCount());
}
}
使用反射技术获取构造器对象并使用
获取构造器的作用依然是初始化一个对象返回。
Constructor类中用于创建对象的方法
package com.example.d1_junit.demo1;
import org.junit.Test;
import java.lang.reflect.Constructor;
public class TestStudent2 {
@Test
public void getDeclaredConstructor() throws Exception {
// 获取类对象
Class c = Student.class;
// 定位单个构造器对象 (按照参数定位无参构造器)
Constructor cons = c.getDeclaredConstructor();
System.out.println(cons.getName() + "===>" + cons.getParameterCount());
// 如果遇到了私有的构造器,可以暴力反射
cons.setAccessible(true); // 权限被打开
Student s = (Student) cons.newInstance();
System.out.println(s);
Constructor cons1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(cons1.getName() + "===>" + cons1.getParameterCount());
Student s1 = (Student) cons1.newInstance("孙悟空",1000);
System.out.println(s1);
}
}
运行结果:
com.example.d1_junit.demo1.Student===>0
无参构造器
Student{name='null', age=0}
com.example.d1_junit.demo1.Student===>2
有参构造器
Student{name='孙悟空', age=1000}
1.利用反射技术获取构造器对象的方式
ngetDeclaredConstructors()
ngetDeclaredConstructor (Class<?>... parameterTypes)
2.反射得到的构造器可以做什么?
- 依然是创建对象的
- public newInstance(Object... initargs)
- 如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象
- setAccessible(boolean)
- 反射可以破坏封装性,私有的也可以执行了。
# 反射获取成员变量对象
使用反射技术获取成员变量对象并使用
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
- Class类中用于获取成员变量的方法
方法 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
使用反射技术获取成员变量对象并使用
获取成员变量的作用依然是在某个对象中取值、赋值
符号 | 说明 |
---|---|
void set(Object obj, Object value): | 赋值 |
Object get(Object obj) | 获取值。 |
package com.example.d1_junit.demo1;
import org.junit.Test;
import java.lang.reflect.Field;
public class FiledDemo1 {
/*
1.获取全部的成员变量
Field[] getDeclaredFields();
获得所有成员变量对应的Field对象、只要申明了就可以得到
*/
@Test
public void getDeclaredFields(){
// 定位Class对象
Class c = Student.class;
// 定位全部成员变量
Field[] fields = c.getDeclaredFields();
// 遍历一下
for (Field field : fields) {
System.out.println(field.getName() + "==>" + field.getType());
}
}
// 获取某个成员变量的对象 Field getDeclaredField(String name);
@Test
public void getDeclaredField() throws Exception{
// 定位Class对象
Class c = Student.class;
// 根据名称定位某一个成员变量
Field field = c.getDeclaredField("age");
field.setAccessible(true); // 暴力反射
System.out.println(field.getName() + "==>" + field.getType());
// 赋值
Student s = new Student();
field.set(s,18);
System.out.println(s);
// 取值
int age = (int) field.get(s);
System.out.println(age);
}
}
运行结果:
name==>class java.lang.String
age==>int
schoolName==>class java.lang.String
COUNTY==>class java.lang.String
age==>int
无参构造器
Student{name='null', age=18}
18
# 反射获取方法对象
使用反射技术获取方法对象并使用
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
- Class类中用于获取成员方法的方法
使用反射技术获取方法对象并使用
- 获取成员方法的作用依然是在某个对象中进行执行此方法
package com.example.d1_junit.demo1;
import com.example.d1_junit.demo2.Person;
import org.junit.Test;
import java.lang.reflect.Method;
public class MethodDemo {
// 获取所有方法对象
@Test
public void getDeclaredMethods(){
// 获取类对象
Class c = Student.class;
// 提取全部方法 包括私有的
Method[] methods = c.getDeclaredMethods();
// 遍历全部方法
for (Method method : methods) {
System.out.println(method.getName() + "返回值类型:" + method.getReturnType() + "参数名称:" + method.getParameterCount());
}
}
// 获取某个方法对象
@Test
public void getDeclaredMethod() throws Exception{
// 获取类对象
Class c = Student.class;
// 提取单个方法
Method method1 = c.getDeclaredMethod("eat",String.class);
Method method2 = c.getDeclaredMethod("eat");
// 暴力打开权限
method1.setAccessible(true);
method2.setAccessible(true);
// 触发方法的执行
Student s = new Student();
Object result1 = method1.invoke(s,"骨头");
System.out.println(result1);
Object result2 = method2.invoke(s);
System.out.println(result2);
}
}
运行结果:
无参构造器
在吃骨头
null
在吃饭
null
总结:
1.利用反射技术获取成员方法对象的方式
- 获取类中成员方法对象
- getDeclaredMethods()
getDeclaredMethod (String name, Class<?>... parameterTypes)
2.反射得到成员方法可以做什么?
- 依然是在某个对象中触发该方法执行。
- Object invoke(Object obj, Object... args)
- 如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行setAccessible(boolean)
反射的作用-绕过编译阶段为集合添加数据
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素
泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段
的时候,其真实类型都是ArrayList了,泛型相当于被擦除了。
反射为何可以给约定了泛型的集合存入其他类型的元素?
- 编译成Class文件进入运行阶段的时候,泛型会自动擦除。
- 反射是作用在运行时的技术,此时已经不存在泛型了。
package com.example.d1_junit.demo1;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ReflectDemo {
public static void main(String[] args) throws Exception{
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(12);
list1.add(13);
// 此时的list只支持Integer的泛类型
System.out.println(list1);
Class c = list1.getClass();
// 定位c类中的add方法
Method add = c.getDeclaredMethod("add",Object.class);
boolean rs = (boolean) add.invoke(list1,"黑马");
System.out.println(rs);
// 此时的泛型类型已经自动擦除
System.out.println(list1);
}
}
运行如下:
[12, 13]
true
[12, 13, 黑马]