六、@Test测试与Assert断言步骤
首先,我们先去按照Junit单元测试规范来书写测试代码,如下:
然后我们会发现每一个需要测试的方法左边都有一个绿色的小三角,这是用来单元测试运行的。也就是说,我们可以只运行某一个方法去测试。现在我们去运行add()方法,结果如下:
这时候,我们发现控制台是绿色的并输出的打印结果,这说明我们的程序没有问题。如果我再其中加入一个算数异常会有怎么样的结果呢?如下:
在这里我们会发现,控制台变为了红色,并给出来报错信息。这证明了我们的程序测试后出现了问题。这仅是程序正确与失败的关系。
如果我们需要一个预期值呢?那么测试的结果不是我想要的预期值,而程序还是绿色的,证明程序没有问题怎么办呢?有的小伙伴会说,我们已经查看了打印控制台的信息,打印结果不是预期值就说明程序有问题,需要去修改呗。对,其实这样说是没有任何毛病的。但是,我们在开发中,如果由于你的疏忽或者疲劳看到了绿色就觉得程序没有问题怎么办呢?所以面对这个问题,我们在单元测试的时候,尽量不要去打印预期值,需要注重观察是绿色和红色比较好,它可以直观的反映程序的是否准确性和达到预期值。
这时候,我们就需要引入一个对象的静态方法来断言是否为预期值。
Assert.assertEquals(预期值, 结果);
这时候我们发现Assert句点出来的方法可以既可以断言数组,也可以断言普通数据类型。所以这时候我们就来使用它断言预期值。如下:
这时候,我们就断言result结果的预期值为1。断言后,发现未达到预期值就会报错!
注意: 我们使用断言的时候尽量不要去断言Double对象。对于双精度数,绝对有必要使用增量进行比较,以避免浮点舍入的问题。如果您使用assertEquals带有double参数的3参数版本。
assertEquals(double expected, double actual, double delta);
这样以来Double将被自动取消装箱,double并且一切正常,这样测试结果就不会失败。否则使用两个参数的来断言double类型,会有如下报错信息:
七、@Before和@After注解
我们在上述,你是否会发现有一些重复操作呢?比如,我们每一个方法都需要去new对象。有些聪明的小伙伴会说,我们可以把它提到类的里面与方法同级。对,这个处理方式也是一个正解。
但是我们在Junit单元测试中,有一个@Before注解,是用作资源的申请。也就是被@Before注解修饰的的方法会在测试方法之前自动执行。所以,我们可以去定义一个init方法,去初始化这个创建对象的过程。这就是@Before注解的作用!
有些应用场景,比如IO流的读写操作。如果我们要测试此代码,是需要一个关闭流的过程,通过我们关闭流使用finally块来保证最后流的关闭操作。这时,我们在Junit单元测试中,有一个@After注解,是用作资源的关闭。也就是说被@After注解修饰的方法会在测试方法之后自定执行。所以,我们在特定需要保证最后关闭、销毁资源的时候,可以去定义一个close方法,去关闭或销毁资源。这就是@After注解的作用!
注意: @Before和@After注解在程序报错的时候,仍然可以保证数据的初始化和关闭销毁,两个方法是依旧执行的。这里有点像我们tomact服务器的初始阶段和销毁阶段,它们的执行不受任何影响。
八、自定义@MyTest注解实现单元测试
目的: 完成自定义注解@MyTest,并实现标有注解的方法并启动它。(模拟@Test注解做单元测试)
步骤:
新建一个注解类(annotation),命名为MyTest
创建一个TestJunit单元测试类,写几个方法,比如:public void test1()
创建一个MyTestDemo测试类(主功能实现类),该类主要利用反射机制来实现对TestJunit单元测试类中加@MyTest注解方法的启动
给予注解类生命周期与反射机制吻合,也就是定义的注解可以保留到运行时,通过反射机制可以获取注解信息
编写MyTestDemo测试类,利用反射获取TestJunit单元测试类的Class对象,并获取单元测试类中所有的方法对象,遍历所有方法对象,只要加@MyTest的注解的方法把他执行起来,不加注解的不给予任何处理操作
启动测试类,查看结果(执行结果,在最后面!)
注意:
自定义注解类中,没有编写注解体,也就是没有给默认value值。因为该注解只是起到了标识的作用,标识需要启动的方法
注解类编译后也是.class文件
通过反射机制来完成自定义注解操作,一定要给与注解和反射同样的生命周期
你要知道我们是不能完成Junit4、Junit5这样类型的插件功能的,可以选择性的执行加了注解的方法,而且我们有实力写出插件IDEA也是不承认的。不会给你生成run方法启动项
MyTest注解类
package com.mylifes1110.java.anno;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 此自定义注解@MyTest只是作为需要单元测试的标记,不需要做默认值
* @Retention注解表示给与@MyTest注解生命周期
* 当前定义的注解可以保留到运行时,通过反射机制可以获取注解信息
* 否则反射将对注解没有任何作用,失去了该意义和自定义单元测试的初衷
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
// String value() default "";
}
TestJunit单元测试类
package com.mylifes1110.java.test.junit;
import com.mylifes1110.java.anno.MyTest;
import org.junit.Test;
public class TestJunit {
@Test
public void test1() {
System.out.println("---test1---");
}
@MyTest
public void test2() {
System.out.println("---test2---");
}
@MyTest
public void test3() {
System.out.println("---test3---");
}
public void test4() {
System.out.println("---test4---");
}
}
MyTestDemo测试类(主要实现功能并测试)
package com.mylifes1110.java.test.junit;
import com.mylifes1110.java.anno.MyTest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 让自定义的MyTest注解起作用
* 通过反射,扫描TestJunit类中有哪些方法上方加了MyTest注解
* 如果加@MyTest注解的则执行它
* 如果没有加@MyTest注解的则不做任何处理
*/
public class MyTestDemo {
public static void main(String[] args) {
/**
* 1.获取TestJunit类对应的Class对象
*/
Class<TestJunit> junitClass = TestJunit.class;
/**
* 获取TestJunit类中所有的方法对象
*/
Method[] methods = junitClass.getMethods();
/**
* 遍历所有方法对象查找有与没有@MyTest注解,并做出响应处理
*/
for (Method method : methods) {
boolean present = method.isAnnotationPresent(MyTest.class);
/**
* TestJunit类中有@MyTest注解的执行该方法
*/
if (present) {
try {
method.invoke(junitClass.newInstance());
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
} else {
/**
* TestJunit类中没有@MyTest注解的不做任何操作
* 此else分支冗余,只是为了做标记,让你们好理解
*/
}
}
}
}
执行结果图
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理