Java 注解与单元测试
注解
Java注解是在JDK1.5 之后出现的新特性,用来说明程序的,注解的主要作用体现在以下几个方面:
- 编译检查,例如 @Override
- 编写文档,java doc 会根据注解生成对应的文档
- 代码分析,通过注解对代码进行分析[利用反射机制]
JDK 中有一些常用的内置注解,例如:
- Override:检查被该注解修饰的方法是否是重写父类的方法
- Deprecatedd:被该注解标注的内容已过时
- SuppressWarnning: 压制警告,传入参数all表示压制所有警告
自定义注解
JDK中虽然内置了大量注解,但是它也允许我们自定义注解,这样就为程序编写带来了很大的便利,像有些框架就大量使用注解。
java注解本质上是一个继承了 java.lang.annotation.Annotation
接口的一个接口,但是如果只是简单的使用关键字 interface
来定义接口,仍然不是注解,仅仅是一个普通的接口,在定义注解时需要使用关键字 @interface
, 该关键字会默认继承 Annotation
接口,并将定义的接口作为注解使用
注解中可以定义方法,这些方法的返回值只能是基本类型、String、枚举类型、注解以及这些类型的数组,我们称这些方法叫做属性。
在使用注解时需要注意以下几个事情
- 必须给注解的属性赋值,如果不想赋值可以使用default来设置默认值
- 如果属性列表中只有一个名为value的属性,那么在赋值时可以不用指定属性名称
- 多个属性值之间使用逗号隔开
- 数组属性的赋值使用
{}
, 而当数组属性中只有一个值时,{}
可以省略不写
元注解
元注解是用来描述注解的注解,Java中提供的元注解有下列几个
Target
描述注解能够作用的位置,即哪些Java代码元素能够使用该注解,注解的源代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
这个注解只有一个value属性,属性需要传入一个 ElementType枚举类型的数组,该枚举类型可以取下列几个值
ElementType | 含义 |
---|---|
TYPE | 接口、类(包括注解)、枚举类型上使用 |
FIELD | 字段声明(包括枚举常量) |
METHOD | 方法 |
PARAMETER | 参数声明 |
CONSTRUCTOR | 构造函数 |
LOCAL_VARIABLE | 局部变量声明 |
ANNOTATION_TYPE | 注解类型声明 |
PACKAGE | 包声明 |
Retention
表示该注解类型的注解保留的时长,主要有3个阶段: 源码阶段,类对象阶段,运行阶段;源码阶段是只只存在与源代码中,类对象阶段是指被编译进 .class 文件中,类对象阶段是指执行时被加载到内存.则默认保留策略为RetentionPolicy.CLASS。
它的源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
Documented
表示拥有该注解的元素可通过javadoc此类的工具进行文档化。源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
Inherited
表示该注解类型被自动继承
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
内置注解解读
下面通过几个JDK内置注解的解读来说明注解相关使用
Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
该注解用于编译时检查,被该注解注释的方法是否是重写父类的方法。
从源码上看,它只能在方法上使用,并且它仅仅存在于源码阶段不会被编译进 .class 文件中
Deprecatedd
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
用于告知编译器,某一程序元素(例如类、方法、属性等等)不建议使用
从源码上看,几乎所有的Java程序元素都可以使用它,而且会被加载到内存中
SuppressWarnning
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
告知编译器忽略特定类型的警告
它需要传入一个字符串的数组,取值如下:
参数 | 含义 |
---|---|
deprecation | 使用了过时的类或方法时的警告 |
unchecked | 执行了未检查的转换时的警告 |
fallthrough | 当Switch程序块进入进入下一个case而没有Break时的警告 |
path | 在类路径、源文件路径等有不存在路径时的警告 |
serial | 当可序列化的类缺少serialVersionUID定义时的警告 |
finally | 任意finally子句不能正常完成时的警告 |
all | 以上所有情况的警告 |
在程序中解析注解
一般通过反射技术来解析自定义注解,要通过反射技术来识别注解,前提条件就是注解要在内存中被加载也就是要使它的范围为 RUNTIME;
JDK提供了以下常用API方便我们使用
返回值 | 方法 | 解释 |
---|---|---|
T | getAnnotation(Class annotationClass) | 当存在该元素的指定类型注解,则返回相应注释,否则返回null |
Annotation[] | getAnnotations() | 返回此元素上存在的所有注解 |
Annotation[] | getDeclaredAnnotations() | 返回直接存在于此元素上的所有注解。 |
boolean | isAnnotationPresent(Class<? extends Annotation> annotationClass) | 当存在该元素的指定类型注解,则返回true,否则返回false |
实战
下面使用一个完整的例子来说明自定义注解以及在程序中使用注解的例子,现在来模仿JUnit 定义一个MyTest的注解,只要被这个注解修饰的方法将来都会被自动执行
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
首先定义一个注解,后续来执行用这个注解修饰了的所有方法,通过Target来修饰标明注解只能用于方法上,通过Retention修饰标明注解会被保留到运行期
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
@MyTest
public void test1(){
System.out.println("this is test1");
}
@MyTest
public void test2(){
System.out.println("this is test2");
}
public static void main(String[] args) {
Method[] methods = Test.class.getMethods();
for (Method method:methods){
if (method.isAnnotationPresent(MyTest.class)){
try {
method.invoke(new Test());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
在测试类中定义了两个测试函数都使用 @MyTest
修饰,在主方法中,首先通过反射机制获取该类中所有方法,然后调用方法的 isAnnotationPresent
函数判断该方法是否被 @Test
修饰,如果是则执行该方法。这样以后即使再添加方法,只要被 @MyTest
修饰就会被调用。
Junit框架
在软件开发中为了保证软件质量单元测试是必不可少的一个环节,Java中提供了Junit 测试框架来进行单元测试
一般一个Java项目每一个类都会对应一个test类用来做单元测试,例如有一个Person类,为了测试Person类会定义一个PersonTest类来测试所有代码
JUnit 中定义了一些注解来方便我们编写单元测试
- @Test:测试方法,被该注解修饰的方法就是一个测试方法
- @Before:在测试方法被执行前会执行该注解修饰的方法
- @After:在测试方法被执行后会执行该注解修饰的方法
除了注解JUnit定义了一些断言函数来实现自动化测试,常用的有如下几个:
- void assertEquals(boolean expected, boolean actual):检查两个变量或者等式是否平衡
- void assertTrue(boolean expected, boolean actual):检查条件为真
- void assertFalse(boolean condition):检查条件为假
- void assertNotNull(Object object):检查对象不为空
- void assertNull(Object object):检查对象为空
- void assertSame(boolean condition):assertSame() 方法检查两个相关对象是否指向同一个对象
- void assertNotSame(boolean condition):assertNotSame() 方法检查两个相关对象是否不指向同一个对象
- void assertArrayEquals(expectedArray, resultArray):assertArrayEquals() 方法检查两个数组是否相等
这些函数在断言失败后会抛出异常,后续只要查看异常就可以哪些测试没有通过
假设先定义一个计算器类,来进行两个数的算数运算
public class Calc {
public int add(int a, int b){
return a + b;
}
public int sub(int a, int b){
return a - b;
}
public int mul(int a, int b){
return a * b;
}
public float div(int a, int b){
return a / b;
}
}
为了测试这些方法是否正确,我们来定义一个测试类
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class CalcTest {
@Test
public void addTest(){
int result = new Calc().add(1,2);
assertEquals(result, 3);
}
@Test
public void subTest(){
int result = new Calc().sub(1,2);
assertEquals(result, -1);
}
@Test
public void mulTest(){
int result = new Calc().mul(1,2);
assertEquals(result, 2);
}
@Test
public void divTest(){
float result = new Calc().div(1,2);
assertEquals(result, 0.5, 0.001); //会报异常
}
}
经过测试发现,最后一个divTest方法 会报异常,实际值是0,因为我们使用 /
来计算两个int时只会保留整数位,也就是得到的是0,与预期的0.5不匹配,因此会报异常
Java 注解与单元测试的更多相关文章
- Java 工具 JUnit单元测试
Java 工具 JUnit单元测试 @author ixenos 1.1. JUnit单元测试框架的基本使用 一.搭建环境: 导入junit.jar包(junit4) 二.写测试类: 0,一般一个 ...
- Java Spring Boot VS .NetCore (八) Java 注解 vs .NetCore Attribute
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
- Java 注解指导手册 – 终极向导
原文链接 原文作者:Dani Buiza 译者:Toien Liu 校对:深海 编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它. 我们已经在Java Code Gee ...
- Java注解处理器
Java注解处理器 2015/03/03 | 分类: 基础技术 | 0 条评论 | 标签: 注解 分享到:1 译文出处: race604.com 原文出处:Hannes Dorfmann Java ...
- java注解总结(1)
1.什么是注解 注解,主要提供一种机制,这种机制允许程序员在编写代码的同时可以直接编写元数据. 2.介绍 何为注解?--->元数据:描述数据自身的数据. 注解就是代码的元数据,他们包含了代码自身 ...
- java注解使用总结
2005年,sun公司推出了jdk1.5,同时推出的注解功能吸引了很多人的目光,使用注解编写代码,能够减轻java程序员繁琐配置的痛苦. 使用注解可以编写出更加易于维护,bug更少的代码. 注解是什么 ...
- Java 注解指导手册(下)
9. 自定义注解 正如我们之前多次提及的,可以定义和实现自定义注解.本章我们即将探讨. 首先,定义一个注解: public @interface CustomAnnotationClass ...
- Java 注解指导手册(上)
编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它. 我们已经在Java Code Geeks提供了丰富的教程, 如Creating Your Own Java A ...
- 【Java】Junit单元测试
什么是单元测试? 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证. 对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Ja ...
随机推荐
- HDU - 1875_畅通工程再续
畅通工程再续 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Desc ...
- <Mysql必知必会> ---- 笔记
转载自 https://www.jianshu.com/p/294502893128 挺基础的mysql的书籍,基本上都是如何操作的语法. 第1章 了解SQL 主键(primary key):能够唯 ...
- C# —— 访问修饰符
1.public 公有的,任何代码均可以访问,应用于所有类或成员. 2.internal 内部的,只能在当前程序集中使用,应用于所有类或成员. 3.protected internal 受保护的内部成 ...
- laravel重定向到上一个页面怎么带参数返回 withsucess 成功提示信息
//控制器中 return back()->with('success','操作成功'); //with的参数1是一个session变量名,参数2为该session变量值,在视图直接这样获取 @ ...
- 16-1 djanjo介绍
一 web框架的本质 1用户的浏览器(socket客户端) 和 网站的服务器(socket服务端)之间 2 HTTP协议: 1.1 请求(request) 1.2. 响应(response) 3 we ...
- oracle 共享SQL语句
为了不重复解析相同的SQL语句,在第一次解析之后, ORACLE将SQL语句存放在内存中.这块位于系统全局区域SGA(system global area)的共享池(shared buffer poo ...
- Libev源码分析06:异步信号同步化--sigwait、sigwaitinfo、sigtimedwait和signalfd
一:信号简述 信号是典型的异步事件.内核在某个信号出现时有三种处理方式: a:忽略信号,除了SIGKILL和SIGSTOP信号不能忽略外,其他大部分信号都可以被忽略: b:捕捉信号,也就是在信号发生时 ...
- SLS机器学习最佳实战:日志聚类+异常告警
1.手中的锤子都有啥? 围绕日志,挖掘其中更大价值,一直是我们团队所关注.在原有日志实时查询基础上,今年SLS在DevOps领域完善了如下功能: 上下文查询 实时Tail和智能聚类,以提高问题调查效率 ...
- The Top 50 Proprietary Programs that Drive You Crazy — and Their Open Source Alternatives
The Top 50 Proprietary Programs that Drive You Crazy — and Their Open Source Alternatives 01 / 22 / ...
- apply、call、bind方法调用
---恢复内容开始--- 首先这三个方法的作用都是用来改变this的值,而this的值一般有几种情况. 1.函数作为一个对象的一个方法来调用,此时this的值指向对象. var a={ v:0; f: ...