测试一般有两种,根据测试代码是否可见分为以下两种测试。
黑盒测试就如上图一样,像一个黑色的盒子,测试人员在测试时无需关注代码内部的实现逻辑,只需要向黑盒中输入一个值,观察输出的值是否符合预期输出结果的情况。
白盒测试则相反,没有像黑盒测试一样对代码进行隐藏,测试人员会利用程序内部的逻辑结构及有关信息,通过在不同点检查程序状态,检验程序中的每条通路是否都能按预定要求进行正确工作。
IDEA 中默认安装了 JUnit 插件,在 IDEA 的 File(文件) --> Setting(设置) --> Plugins(插件) 中搜索 JUnit 即可看到,如果自己的 IDEA没有,可以搜索 JUnit 并安装。
Eclipse 中配置 JUnit 就会稍微麻烦一点了。
① 找到自己的项目,右击点击项目文件 --> 点击 Build Path
--> 点击 Add Libraries
② 点击 JUnit
③ 选择自己需要的 JUnit 版本,并点击 Finish
即可。
在学习 JUnit 之前,我们测试的时候,一般都是定义一个测试类来对我们的程序进行测试。
public class Demo {
public void printHelloWorld(){
System.out.println("Hello World");
}
public int add(int a, int b){
return a+b;
}
public int sub(int a, int b){
return a-b;
}
}
public class DemoTest {
public static void main(String[] args) {
// 创建对象
Demo demo = new Demo();
// 调用
//demo.printHelloWorld();
/*
int num = demo.add(1,2);
System.out.println(num);
*/
int num = demo.sub(1,2);
System.out.println(num);
}
}
但是当我们需要测试多个方法的时候,我们就需要把测试类中调用的方法注释掉,才能测试下一个方法,这样测试起来会很麻烦,于是下面便介绍 JUnit测试。
@Test
Add 'JUnit' to classpath
即可。
import org.junit.Test;
public class DemoTest {
@Test
public void testPrintHelloWorld(){
// 创建对象
Demo demo = new Demo();
// 调用方法
demo.printHelloWorld();
}
@Test
public void testAdd(){
// 创建对象
Demo demo = new Demo();
// 调用方法
System.out.println(demo.add(1,3));
}
@Test
public void testSub(){
// 创建对象
Demo demo = new Demo();
// 调用方法
System.out.println(demo.sub(1,3));
}
}
测试结果正确的方法就会出现绿色对号,出现异常的方法就会出现红色灯泡。
一般我们使用断言(Assert)
来测试异常的结果。Assert可以帮助我们假定预期输出结果与真实输出结果是否相同。
// 格式
Assert.assertEquals(expected,actual);
这里我们假定输出结果为3。
@Test
public void testAdd(){
// 创建对象
Demo demo = new Demo();
// 调用方法
int num = demo.add(1,3);
//System.out.println(num);
// 断言处理
Assert.assertEquals(3,num); //假定输出结果为3
}
可以看到,当真实输出结果和预期输出结果不同时,输出预期值和真实值,并报错。此时我们就需要对我们的代码进行处理
@Before
:初始化方法,修饰的方法会在测试方法之前被自动执行@Before
public void init(){
System.out.println("init...");
};
@After
:释放资源方法,修饰的方法会在测试方法执行之后自动被执行@After
public void close(){
System.out.println("close...");
}
结果:
通过反射,我们可以更容易的对 类对象阶段更好地操作成员变量,构造方法,成员方法等。
① Field:成员变量
void set(Object obj, Object value)
get(Object obj)
setAccessible(true) //暴力反射
② Constructor:构造方法
T newInstance(Object... initargs)
③ Method:方法对象
Object invoke(Object obj, Object... args)
String getName:获取方法名
Class.forName("全类名")
:将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中。读取文件,加载类类名.class
:通过类名的属性class获取。多用于参数的传递对象.getClass()
:getClass()
方法在Object类中定义。多用于对象的获取字节码的方式同一个字节码文件(.class
)在一次程序运行中,只会被加载一次,不论通过哪种方式获取的Class对象都是同一个。
public class Demo {
public int a;
public char b;
public Demo(){ }
public void function() {}
}
class ReflectDemo{
public static void main(String[] args) {
// 获取Class对象
Class cls = Demo.class;
System.out.println(cls);
}
}
方法名 | 说明 |
---|---|
Field[] getFields() | 获取所有public修饰的成员变量,包括从父类继承的成员变量 |
Field getField(String name) | 获取指定名称的 public修饰的成员变量,包括从父类继承的成员变量 |
Field[] getDeclaredFields() | 获取所有的成员变量,不考虑修饰符,不包括从父类继承的成员变量 |
Field getDeclaredField(String name) | 获取指定名称的成员变量,不考虑修饰符,不包括从父类继承的成员变量 |
方法名 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有的构造方法,考虑修饰符 |
Constructor<T> getConstructor(类<?>... parameterTypes) | 返回指定参数类型的构造方法,考虑修饰符 |
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) | 返回指定参数类型的构造方法,不考虑修饰符 |
Constructor<?>[] getDeclaredConstructors() | 返回所有的构造方法,不考虑修饰符 |
方法名 | 说明 |
---|---|
Method[] getMethods() | 获取所有的成员方法,考虑修饰符 |
Method getMethod(String name, 类<?>... parameterTypes) | 获取指定的成员方法,考虑修饰符 |
Method[] getDeclaredMethods() | 获取所有的成员方法,不考虑修饰符 |
Method getDeclaredMethod(String name, 类<?>... parameterTypes) | 获取的指定的成员方法,不考虑修饰符 |
方法名 | 说明 |
---|---|
String getName() | 获取类的全类名 |
Declared
的是返回所有的方法或变量,不加 Declared
的只返回 public 访问权限的方法或变量注解和注释差不多,只不过所面向的对象不一样。
元数据
。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
注解一般有以下三种作用:
注解一般分为预定义注解和自定义注解。
预定义注解也就是 JDK 中事先定义好的一些注解。常见的一些注解如下:
class Demo_father {
public void show(){
System.out.println("1");
}
}
public class Demo extends Demo_father{
public int a;
public char b;
public Demo(){ }
public void function() {}
@Override
public void show() { //继承父类的方法
super.show();
}
}
class Demo_father {
public void show(){
System.out.println("1");
}
}
public class Demo extends Demo_father{
public int a;
public char b;
public Demo(){ }
public void function() {}
@Override
public void show() {
super.show();
}
@Deprecated
public void print(){
System.out.println("Hello World");
}
public void test(){
print();
}
}
被 @Deprecated
注解的方法在调用时被画横线,并不意味着不能用,但不推荐使用。
@SuppressWarnings("all")
。使用前,出现警告
使用后,警告消失
@Override
代码
① 格式:
元注解
public @interface 注解名称{
属性列表;
}
示例:MyAnnotation就是一个注解,并能使用,没有报错。
② 本质:注解本质上就是一个接口,该接口默认继承Annotation
接口
public interface MyAnnotation extends java.lang.annotation.Annotation {}
③ 属性:接口中的抽象方法
{}
包裹。如果数组中只有一个值,则{}
可以省略
元注解用来描述注解的注解,主要有以下几个:
@Retention(RetentionPolicy.RUNTIME)
:当前被描述的注解,会保留到class字节码文件中,并被JVM读取到① 获取注解定义位置的对象(Class,Method,Field) ② 获取指定的注解 ③ 调用注解中的抽象方法获取配置的属性值
import java.lang.annotation.*;
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String show();
}
@MyAnnotation(show = "Hello World")
class Demo_father {
}
public class Demo {
public static void main(String[] args) {
// 获取字节码文件
Class<Demo_father> demo_fatherClass = Demo_father.class;
// 获取注解对象
MyAnnotation myAnnotation = demo_fatherClass.getAnnotation(MyAnnotation.class);
// 调用注解中的抽象方法,获取返回值
System.out.println(myAnnotation.show());
}
}
结果: