软件测试
查询百度百科:软件测试(英语:Software Testing),描述一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。换句话说,软件测试是一种实际输出与预期输出之间的审核或者比较过程。软件测试的经典定义是:在规定的条件下对程序进行操作,以发现程序错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程。
测试的分类
- 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
- 白盒测试:需要写代码的。关注程序具体的执行流程。Junit测试属于白盒测试中的一种。
现在我们拿到一个需求,需要测试一下Junit包下的Calculator类代码编写是否正确。代码如下
package Junit;
/**
* 计算器类
*/
public class Calculator {
/**
* 加法
* @param a
* @param b
* @return
*/
public int add(int a, int b){
return a +b;
}
/**
*
* @param a
* @param b
* @return
*/
public int sub(int a,int b){
return a -b;
}
}
按照以前的知识,我们需要在测试类中,创建Calculator类的对象,在调用Calculator类中编写的方法,查看方法的运行结果和我们期望的结果一样。但是这么做很多的缺点:如果我们要测试多个方法,必须把前面测试好的代码注释掉,以免造成影响。步骤麻烦等等。下面我们就是要Junit单元测试的知识来测试这个类。
步骤如下
1:创建一个包,存放测试用例。包名建议以.text结尾,并且与要测试的代码存放的包平级。

2:在MyTest包中,创建类(测试用例)。建议类名以Test结尾
3:在测试用例中定义测试方法,建议为test要测试的方法名称,例如要测试add方法,测试方法名称可以为testAdd()。测试方法建议空参和void返回值。

4:给测试方法加@Test,导入junit依赖环境


代码完成之后,方法代码如下
package MyTest;
import org.junit.Test;
public class CalculatorTest {
/**
* 测试add方法
*/
@Test
public void testAdd(){
}
}
5:在测试用例中,创建对象,调用要测试的方法进行测试
package MyTest;
import Junit.Calculator;
import org.junit.Test;
public class CalculatorTest {
/**
* 测试add方法
*/
@Test
public void testAdd(){
//创建对象
Calculator c = new Calculator();
//调用方法
int result = c.add(1, 3);
}
}
6:使用会使用断言操作来处理结果, Assert.assertEquals(期望的结果,运算的结果);
package MyTest;
import Junit.Calculator;
import org.junit.Assert;
import org.junit.Test;
public class CalculatorTest {
/**
* 测试add方法
*/
@Test
public void testAdd(){
//创建对象
Calculator c = new Calculator();
//调用方法
int result = c.add(1, 3);
//断言操作
Assert.assertEquals(4,result);
}
}
7:查看下面,如果是绿色的则代码要测试的内容没有问题,红色则说明代码有错误

常用的注解
@Before
- 修饰的方法会在测试方法之前被自动执行
@After
- 修饰的方法会在测试方法执行之后自动被执行
代码举例
package MyTest;
import Junit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class CalculatorTest {
/**
* 初始化方法:
* 用于资源申请,所有测试方法在执行之前都会先执行该方法
*/
@Before
public void init() {
System.out.println("init...");
}
/**
* 释放资源方法:
* 在所有测试方法执行完后,都会自动执行该方法
*/
@After
public void close() {
System.out.println("close...");
}
/**
* 测试add方法
*/
@Test
public void testAdd() {
//1.创建计算器对象
System.out.println("testAdd...");
Calculator c = new Calculator();
//2.调用add方法
int result = c.add(1, 2);
//System.out.println(result);
//3.断言 我断言这个结果是3
Assert.assertEquals(3, result);
}
@Test
public void testSub() {
//1.创建计算器对象
Calculator c = new Calculator();
int result = c.sub(1, 2);
System.out.println("testSub....");
Assert.assertEquals(-1, result);
}
}
代码执行之后的代码

反射
框架
半成品软件。可以在框架的基础上进行软件开发,简化编码 。反射:框架设计的灵魂
反射的概念
将类的各个组成部分封装为其他对象,这就是反射机制
好处
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
Java代码在计算机中经历的阶段
阶段1:Source源代码阶段
我们编写好代码,经过Javac编译生成字节码文件。字节码文件中保存了Java的成员变量丶构造方法丶成员方法等等等。他分别对应着Java代码中的成员变量丶构造方法,成员方法等等..如下图所示

阶段2:Class类对象阶段
我们通过类加载器(ClassLoader对象)把字节码文件加载进入内存中去,此时就进入Java代码的第二个阶段。在Java中"万物皆对象",因此有一个对象专门描述进入内存中的字节码文件,这个对象的名字是Class类对象。他是用来描述所有字节码文件共同的特征和行为

阶段3 Runtime运行时阶段
当我们通过类对象的一些行为就创建真正的对象,此时就是进入到了运行时阶段

获取字节码class对象的三种方式
如果Java代码处于阶段一的时候,我们可以使用方式一获取Class对象
方式1:
- Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
如果Java代码处于阶段二,我们除了使用方式1获取字节码Class对象还可以使用方式2获取
方式2
- 类名.class:通过类名的属性class获取 多用于参数的传递
如果Java代码处于阶段三,我们除了使用方式1丶2获取还可以使用方式3获取Class对象
方式3
- 对象.getClass():getClass()方法在Object类中定义着。 多用于对象的获取字节码的方式
举例:
定义Person类
package Demo;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", 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;
}
}
定义测试类
package reflect;
import Demo.Person;
public class DemoReflect {
public static void main(String[] args) throws Exception {
/*
方式1:Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
全类名就是包名.类名全路径
*/
Class personClass1 = Class.forName("Demo.Person");
System.out.println(personClass1);//class Demo.Person
//方式2:类名.class:通过类名的属性class获取 多用于参数的传递
Class personClass2 = Person.class;
System.out.println(personClass2);//class Demo.Person
// 方式3:对象.getClass():getClass()方法在Object类中定义着。 多用于对象的获取字节码的方式
Person p = new Person();
Class personClass3 = p.getClass();
System.out.println(personClass3);//class Demo.Person
// == 比较对象
System.out.println(personClass1 == personClass2 && personClass1 == personClass3);//true
// 因此我们知道不论通过哪一种方式获取的Class对象都是同一个。
}
}
结论:
- 同一个字节码文件(.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
Class对象的功能(方法)
获取成员变量的功能
方法中带有Declared单词的就是私有的,需要暴力反射
- Field[] getFields() :获取所有public修饰的成员变量
- Field getField(String name) 获取指定名称的 public修饰的成员变量
- Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
- Field getDeclaredField(String name) 获取指定名称的成员变量,不考虑修饰符
操作获取到的成员变量
设置值
- void set(Object obj, Object value)
获取值
- get(Object obj)
如果要操作私有的成员变量则必须使用暴力反射
- setAccessible(true):暴力反射。忽略访问权限修饰符的安全检查
举例:
定义TestField类
package Demo;
public class TestField {
private String a;
String b;
protected String c;
public String d;
@Override
public String toString() {
return "TestField{" +
"a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
}
定义测试类
package reflect;
import Demo.TestField;
import java.lang.reflect.Field;
public class Demo01Reflect {
public static void main(String[] args) throws Exception {
//方式1 获取class对象
/*
方式1:Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
全类名就是包名.类名全路径
*/
Class DemoClass = Class.forName("Demo.TestField");
//Field[] getFields() :获取所有public修饰的成员变量
Field[] fields = DemoClass.getFields();
//遍历数组
for (Field field : fields) {
System.out.println(field);//public java.lang.String Demo.Test.d
}
//Field getField(String name) 获取指定名称的 public修饰的成员变量
Field d = DemoClass.getField("d");
System.out.println(d);//public java.lang.String Demo.Test.d
//我们拿到了成员变量之后可以获取其值
TestField t = new TestField();
Object o = d.get(t);// get 参数为获取哪个对象的成员变量值
System.out.println(o);//null
//我们也可以设置值,set的第一个参数是为哪个对象,第二个参数设置什么值
d.set(t, "张三");
System.out.println(t);//TestField{a='null', b='null', c='null', d='张三'}
//Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field[] declaredFields = DemoClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
/*
遍历后的结果
----------------------------------
private java.lang.String Demo.Test.a
java.lang.String Demo.Test.b
protected java.lang.String Demo.Test.c
public java.lang.String Demo.Test.d
*/
}
//Field getDeclaredField(String name)获取指定名称的成员变量,不考虑修饰符
Field a = DemoClass.getDeclaredField("a");
System.out.println(a);//private java.lang.String Demo.Test.a
//要想获取和设置私有的必须暴力反射
a.setAccessible(true);
//获取私有变量
System.out.println(a.get(t));//null
//设置私有变量的值
a.set(t, "李四");
System.out.println(t);//TestField{a='李四', b='null', c='null', d='张三'}
}
}
获取构造方法的功能
构造方法的名字都是一样,我们通过参数来区分到底使用哪个构造方法,方法中带有Declared单词的就是私有的,需要暴力反射
- Constructor<?>[ ] getConstructors() 获取所有的被public修饰的构造方法
- Constructor<T> getConstructor(class<?>... parameterTypes) 获取指定参数被public修饰的构造方法
- Constructor<T> getDeclaredConstructor(class<?>... parameterTypes)
- Constructor<?>[] getDeclaredConstructors()
使用获取到的构造方法,创建对象
- T newInstance(Object... initargs)
举例
定义TestConstructor 类
package Demo;
public class TestConstructor {
private String name;
private int age;
public TestConstructor() {
}
public TestConstructor(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "TestConstructor{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
定义测试类
package reflect;
import Demo.TestConstructor;
import java.lang.reflect.Constructor;
public class Demo02Reflect {
public static void main(String[] args) throws Exception {
//方式2:类名.class:通过类名的属性class获取 多用于参数的传递
Class testConstructorClass = TestConstructor.class;
// 获取指定参数构造方法,
Constructor c1 = testConstructorClass.getConstructor(String.class, int.class);
//使用指定参数的构造方法创建对象
Object o1 = c1.newInstance("早上", 18);
System.out.println(o1);//TestConstructor{name='早上', age=18}
//获取无参的构造方法
Constructor c2 = testConstructorClass.getConstructor();
//使用指定参数的构造方法创建对象
Object o2 = c2.newInstance();
System.out.println(o2);//TestConstructor{name='null', age=0}
//不推荐下面这种方式创建空参对象
System.out.println(testConstructorClass.newInstance());//TestConstructor{name='null', age=0}
}
}
获取成员方法的功能:
方法中带有Declared单词的就是私有的,需要暴力反射
- Method[] getMethods()
- Method getMethod(String name, class<?>... parameterTypes)
- Method[ ] getDeclaredMethods()
- Method getDeclaredMethod(String name, class<?>... parameterTypes)
获取到方法,执行方法:
- Object invoke(Object obj, Object... args): 传递实际对象,和方法的参数
获取方法名称:
- String getName:获取方法名
自定义类
package reflect;
public class Person {
private String name;
private int age;
public String a;
protected String b;
String c;
private String d;
public Person() {
}
public Person(String name, int age) {
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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
public void eat() {
System.out.println("eat...");
}
public void eat(String food) {
System.out.println("eat..." + food);
}
}
定义测试类
package reflect;
import java.lang.reflect.Method;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
//获取指定名称的方法,参数为方法名称eat
Method eat_method = personClass.getMethod("eat");
Person p = new Person();
//执行方法,参数为哪个对象的方法
eat_method.invoke(p);//eat...
//区分方法,可以通过方法名称和参数列表来区分
Method eat_method2 = personClass.getMethod("eat", String.class);
//执行方法
eat_method2.invoke(p, "饭");//eat...饭
//获取所有public修饰的方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
//获取方法的名字
String name = method.getName();
System.out.println(name);
}
//获取类名
String className = personClass.getName();
System.out.println(className);//reflect.Person
}
}
注解
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
概念描述:
- JDK1.5之后的新特性
- 说明程序的
- 使用注解:@注解名称
作用分类:
- ①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
- ②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
- ③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
JDK中预定义的一些注解
@Override :检测被该注解标注的方法是否是继承自父类(接口)的

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


@SuppressWarnings:压制警告 一般传递参数all ,压制所有的警告
压制类中所有的警告信息
自定义注解
格式:
元注解
public @interface 注解名称{
属性列表;
}
什么是元注解
元注解:用于描述注解的注解
- 描述注解能够作用的位置
- 描述注解被保留的阶段
- 描述注解是否被抽取到api文档中
- 描述注解是否被子类继承
本质:
- 注解本质上就是一个接口,该接口默认继承Annotation接口 。注解中的属性就是接口中的抽象方法
要求:
属性的返回值类型有下列取值
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
定义了属性(抽象方法),在使用时需要给属性赋值
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
- 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
- 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
小结:
- 以后大多数时候,我们会使用注解,而不是自定义注解
注解给谁用?
- 编译器
- 给解析程序用
- 注解不是程序的一部分,可以理解为注解就是一个标签
来源:https://www.cnblogs.com/wurengen/p/12256725.html
