文章目录
- 一、 注解概述
- (1) 什么是注解
- (2) 注解与注释
- (3) 注解的重要性
- 二、常见的Annotation作用
- (1)示例1
- (2)示例2
- (3)示例3
- 三、 三个最基本的注解
- (1) @Override
- (2) @Deprecated
- (3) @SuppressWarnings
- 四、元注解
- (1)介绍
- (2)讲解
- 2.1 @Target
- 2.2 @Retention
- 2.3 @Documented
- 2.4 @Inherited
- (3)示例
- 五、 自定义注解的使用
- (1) 声明自定义注解
- (2)举例
- 2.1 举例1
- 2.2 举例2
- (3)读取和处理自定义注解
- 六、JUnit单元测试
- (1)测试分类
- (2)JUnit单元测试介绍
- (3) 引入本地JUnit.jar
- (4)测试
- 4.1 测试1
- 4.2 测试2
- (5)编写和运行@Test单元测试方法
- (6) 设置执行JUnit用例时支持控制台输入
- (7) 定义test测试方法模板
一、 注解概述
(1) 什么是注解
注解(Annotation)是从JDK5.0
开始引入,以“@注解名
”在代码中存在。例如:
①修饰的方法是父类中重写的方法
@Override
②相应结构过时
@Deprecated
③编译器一直警告
@SuppressWarnings(value=”unchecked”)
注解Annotation
可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。
注解可以在类编译、运行时进行加载,体现不同的功能。
框架 = 注解 + 反射 + 设计模式
(2) 注解与注释
注解也可以看做是一种注释,通过使用 Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。但是,注解,不同于单行注释和多行注释。
- 对于单行注释和多行注释是给程序员看的。(编译形成的字节码文件中没有单行、多行注释)
- 而注解是可以被编译器或其他程序读取的。程序还可以根据注解的不同,做出相应的处理。
(3) 注解的重要性
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色
,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码
和XML配置
等。
未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,Struts2有一部分也是基于注解的了。注解是一种趋势
,一定程度上可以说:框架 = 注解 + 反射 + 设计模式
。
二、常见的Annotation作用
(1)示例1
示例1:生成文档相关的注解
@author 标明开发该类模块的作者,多个作者之间使用,分割
@version 标明该类模块的版本
@see 参考转向,也就是相关主题
@since 从哪个版本开始增加的
@param 对方法中某参数的说明,如果没有参数就不能写
@return 对方法返回值的说明,如果方法的返回值类型是void就不能写
@exception 对方法可能抛出的异常进行说明 ,如果方法没有用throws显式抛出的异常就不能写
比如:
package com.annotation.javadoc;
/**
* @author 雨翼轻尘
* @version 1.0
* @see Math.java
*/
public class JavadocTest {
/**
* 程序的主方法,程序的入口
* @param args String[] 命令行参数
*/
public static void main(String[] args) {
}
/**
* 求圆面积的方法
* @param radius double 半径值
* @return double 圆的面积
*/
public static double getArea(double radius){
return Math.PI * radius * radius;
}
}
(2)示例2
示例2:在编译时进行格式检查(JDK内置的三个基本注解)
@Override
: 限定重写父类方法,该注解只能用于方法。
@Deprecated
: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择。
@SuppressWarnings
: 抑制编译器警告。
package com.annotation.javadoc;
public class AnnotationTest{
public static void main(String[] args) {
@SuppressWarnings("unused")
int a = 10;
}
@Deprecated
public void print(){
System.out.println("过时的方法");
}
@Override
public String toString() {
return "重写的toString方法()";
}
}
(3)示例3
示例3:跟踪代码依赖性,实现替代配置文件功能
- Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署。
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) { }
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
doGet(request, response);
}
}
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
- Spring框架中关于“事务”的管理
@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,readOnly=false,timeout=3)
public void buyBook(String username, String isbn) {
//1.查询书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2. 更新库存
bookShopDao.updateBookStock(isbn);
//3. 更新用户的余额
bookShopDao.updateUserAccount(username, price);
}
<!-- 配置事务属性 -->
<tx:advice transaction-manager="dataSourceTransactionManager" id="txAdvice">
<tx:attributes>
<!-- 配置每个方法使用的事务属性 -->
<tx:method name="buyBook" propagation="REQUIRES_NEW"
isolation="READ_COMMITTED" read-only="false" timeout="3" />
</tx:attributes>
</tx:advice>
三、 三个最基本的注解
Java基础涉及到的三个常用注解:
@Override
: 限定重写父类方法,该注解只能用于方法。@Deprecated
: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择。@SuppressWarnings
: 抑制编译器警告。
(1) @Override
- 用于检测被标记的方法为有效的重写方法,如果不是,则报编译错误!
- 只能标记在方法上。
- 它会被编译器程序读取。
【案例】
class Person{
String name;
int age;
public void eat(){
System.out.println("人吃饭");
}
}
class Student extends Person{
@Override
public void eat(){
System.out.println("学生吃饭");
}
}
如果将@Override
去掉,他其实也可以。
如果写了这个注解,它就会显示地去校验一下这个方法是不是重写。
比如现在将注解删除,然后将重写的方法写错了,此时不会报错,如下:
此时它和父类中的eat()
就是两个不同的方法了,若此时试图加上注解,就会报错,如下:
所以这里就相当于一个保护机制,如果写错了就会报错。
(2) @Deprecated
- 用于表示被标记的数据已经过时,不推荐使用,但仍可以使用。
- 可以用于修饰 属性、方法、构造、类、包、局部变量、参数。
- 它会被编译器程序读取。
【案例】
package yuyi07;
import java.util.Date;
public class AnnotationTest {
public static void main(String[] args) {
//日期Date的API
Date date =new Date(); //util下的Date表示的是一个日期类
System.out.println(date); //获取当前时间
}
}
输出结果:
按住Ctrl点击进入Date中,然后Ctrl+F12,如下:
这些加了横线的就是加了@Deprecated
标识,表示已经过时了,就是不推荐使用它,但是还是可以用它的。如下:
比如此时想用一下上面这个构造器:
打印一下:
可以看到这里是3924年,这里有一个偏移量,在1900的基础上加上2023,如下:(为啥我的编译器是3924,搞不明白)
API中可以看到(将鼠标光标放在画横线的Date上即可),这个Date被它们替换了,如下:
其实我们自己也可以用。
比如在Person构造器上写一个@Deprecated
,表示这个构造器不推荐使用。
class Person{
String name;
int age;
public Person(){
}
@Deprecated
public Person(String name){
this.name=name;
}
}
但是仍然可以使用,比如:
public class AnnotationTest {
public static void main(String[] args) {
Person p2=new Person();
Person p3=new Person("Tom"); //此构造器已经过时,不建议使用,但仍然可以使用
System.out.println(p3);
}
}
class Person{
String name;
int age;
public Person(){
}
@Deprecated
public Person(String name){
this.name=name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
输出结果:
这个不推荐的意思就是,它在以后的某个版本中可能会被删除。
这个注解的意义就在于过渡jdk版本的更新,防止更新版本之后,之前的代码不能用了,所以以后写代码的时候尽量避开这些过时的。
(3) @SuppressWarnings
- 抑制编译警告。当我们不希望看到警告信息的时候,可以使用 SuppressWarnings 注解来抑制警告信息 。
- 可以用于修饰类、属性、方法、构造、局部变量、参数
- 它会被编译器程序读取。
- 可以指定的警告类型有(了解)
- all,抑制所有警告
- unchecked,抑制与未检查的作业相关的警告
- unused,抑制与未用的程式码及停用的程式码相关的警告
- deprecation,抑制与淘汰的相关警告
- nls,抑制与非 nls 字串文字相关的警告
- null,抑制与空值分析相关的警告
- rawtypes,抑制与使用 raw 类型相关的警告
- static-access,抑制与静态存取不正确相关的警告
- static-method,抑制与可能宣告为 static 的方法相关的警告
- super,抑制与置换方法相关但不含 super 呼叫的警告
- …
示例代码:
package com.atguigu.annotation;
import java.util.ArrayList;
public class TestAnnotation {
@SuppressWarnings("all")
public static void main(String[] args) {
int i;
ArrayList list = new ArrayList();
list.add("hello");
list.add(123);
list.add("world");
Father f = new Son();
f.show();
f.methodOl();
}
}
class Father{
@Deprecated
void show() {
System.out.println("Father.show");
}
void methodOl() {
System.out.println("Father Method");
}
}
class Son extends Father{
/* @Override
void method01() {
System.out.println("Son Method");
}*/
}
比如编译器右边就会有橙色的警告信息,可以让它不显示。
比如现在写个语句,右边会有橙色警告:
点击“更多操作”:
放在语句上面:
系统就会自动加上:
@SuppressWarnings("unused") int num=10;
表示当前对象定义了没有使用:
四、元注解
(1)介绍
【元注解的理解】
JDK1.5在java.lang.annotation
包定义了4个标准的meta-annotation
类型,它们被用来提供对其它 annotation
类型作说明。
元注解:对现有的注解进行解释说明的注解
。
比如这两个就是元注解:(修饰现有注解SuppressWarnings
的注解)
(2)讲解
【讲4个元注解】
2.1 @Target
@Target:用于描述注解的使用范围
- 可以通过枚举类型ElementType的10个常量对象来指定
- TYPE,METHOD,CONSTRUCTOR,PACKAGE…
可以看到Target里面有数组,可以存多个值,比如:@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
。
ElementType[] value();
:Target里面有一个ElementType
类型的数组,点进去看一下:
可以看到,ElementType
是一个枚举类,里面有很多对象,比如TYPE
就可以表示这么多意思:
还有这些:
所以在SupperWarnings里面,它的Target注解就表示SupperWarnings注解可以用来修饰哪些结构。
Target注解用来表明当前注解可以修饰的结构。
2.2 @Retention
@Retention:用于描述注解的生命周期
- 可以通过枚举类型RetentionPolicy的3个常量对象来指定
- SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
- 唯有RUNTIME阶段才能被反射读取到。
可以看到Retention
里面有一个RetentionPolicy
类型,按住Ctrl将它点开,如下:
可以看到,它也是一个枚举类。有3个对象。
①SOURCE
,在编译器编译的时候就被处理了。
如果RetentionPolicy修饰的注解是SOURCE的话,对应修饰的注解(比如SuppressWarnings)生成的字节码文件中是没有它的。
②CLASS
,就是字节码文件。这个被修饰的注解会被保留在字节码文件中,但是不会在虚拟机运行的时候保留(往内存中加载的时候就不考虑它了),这是一个默认的行为。
③RUNTIME
。在CLASS基础上还希望在运行的时候也保留,就得用它。
其实就是指明注解的生命周期。
2.3 @Documented
@Documented:表明这个注解应该被javadoc工具记录。
按住Ctrl键点击@Deprecated
:
可以看到它上面有@Documented
。如下:
这就意味着,@Deprecated
这个注解修饰的结构Person类,用javadoc解析之后的说明文档html文件,在文档里面能看到@Deprecated
这个注解。因为它用@Documented
修饰了。
再比如Date这个文档里面有显示:
再比如SuppressWarnings
没有用@Documented修饰,所以它就不会保留在javadoc里面,如下:
2.4 @Inherited
@Inherited:允许子类继承父类中的注解
若是用它去修饰,比如修饰我们自己写的MyAnnotation注解,那么在Person里面使用了@MyAnnotation
,它就具备了继承性,言外之意就是Person它的子类Student上面相当于也有一个MyAnnotation注解。
直接被子类所继承了。
(3)示例
示例代码:
package java.lang;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
拓展:元数据
String name = “Tom”; 最核心的是值“Tom”,前面两个是用来修饰值的结构,一个指定类型,一个指定名称。这时候String和name就可以看作Tom数据的元数据。
五、 自定义注解的使用
一个完整的注解应该包含三个部分:
(1)声明
(2)使用
(3)读取
(1) 声明自定义注解
【元注解】
【修饰符】 @interface 注解名{
【成员列表】
}
- 自定义注解可以通过四个元注解@Retention,@Target,@Inherited,@Documented,分别说明它的声明周期,使用位置,是否被继承,是否被生成到API文档中。
- Annotation 的成员在 Annotation 定义中以无参数有返回值的抽象方法的形式来声明,我们又称为配置参数。返回值类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组
- 可以使用 default 关键字为抽象方法指定默认返回值
- 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值。格式是“方法名 = 返回值”,如果只有一个抽象方法需要赋值,且方法名为value,可以省略“value=”,所以如果注解只有一个抽象方法成员,建议使用方法名value。
比如:
package com.atguigu.annotation;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
package com.atguigu.annotation;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String columnName();
String columnType();
}
(2)举例
2.1 举例1
再看一下这个例子:
package yuyi07;
import java.util.Date;
/**
* ClassName: AnnotationTest
* Package: yuyi07
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/5 0005 16:00
*/
public class AnnotationTest {
public static void main(String[] args) {
//日期Date的API
Date date =new Date(); //util下的Date表示的是一个日期类
System.out.println(date); //获取当前时间
Date date1=new Date(2023,12,5);
System.out.println(date1);
Person p2=new Person();
Person p3=new Person("Tom"); //此构造器已经过时,不建议使用,但仍然可以使用
System.out.println(p3);
@SuppressWarnings({"unused"}) int num=10;
}
}
class Person{
String name;
int age;
public Person(){
}
@Deprecated
public Person(String name){
this.name=name;
}
public void eat(){
System.out.println("人吃饭");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Student extends Person{
@Override
public void eat(){
System.out.println("学生吃饭");
}
}
以@SuppressWarnings
为参照,如下:
注解使用@interface
这种结构来定义的。(注意与接口没有关系)
下面我们自己写一个。
public @interface MyAnnotation {
}
参照@SuppressWarnings
,它里面写的String[] value();
其实是一个属性,这个属性是一个String型的数组,可以有多个值的意思。
就比如刚才只赋了一个值,如果还有可以继续赋值。如下:
现在我们自己写,也可以提供属性。
比如String value();
,这里咱们就不写数组了,直接就是String类型的,注意value后面有个小括号,按照格式来就行。
属性名就叫value
。
定义结束。
在哪里用呢?注解使用的位置很灵活。
比如现在想在Person类上使用。
直接输入@MyAnnotation
,如下:
然后传入一个属性值,比如value="class"
,表示这是一个类。如下:
其实我们也可以给注解的属性提供一个默认值,比如“hello”:
public @interface MyAnnotation {
String value() default "hello";
}
所以就有了这两种情况:
定义注解是后期你想让它干什么,然后配合“反射”的方式去用它,光写在那里就只是一个标识而已。
现在感受一下就行,后期讲反射的时候再用。
<1> Target
现在将这个Target注解粘贴过来:
如下:
package yuyi07;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
/**
* ClassName: MyAnnotation
* Package: yuyi07
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/6 0006 10:21
*/
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
public @interface MyAnnotation {
String value() default "hello";
}
这里表示MyAnnotation注解可以修饰上面Target注解里面的结构。
此时咱们将Target注解改造一下,删除一些可以修饰的结构:@Target({FIELD, METHOD,CONSTRUCTOR})
。
然后就有一些不能修饰的结构,就会报错,如下:
Target注解用来表明当前注解可以修饰的结构。
<2> Retention
然后我们再将Retention粘过来:
如下:
若是CLASS
,那么在字节码文件中有MyAnnotation注解,而在运行时没有它(不会把它加载到内存中)。
后边会说到“反射”,在程序运行的过程中,使用反射的API调用注解,所以必须要保证运行的时候有。就是要将注解改成RUNTIME
,让它运行的时候也要在,才能通过反射拿到它。
Retention注解就是表示当前注解的生命周期。
2.2 举例2
注解:
package com.atguigu.annotation;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
package com.atguigu.annotation;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String columnName();
String columnType();
}
【Student.java】
package com.atguigu.annotation;
@Table("t_stu")
public class Student {
@Column(columnName = "sid",columnType = "int")
private int id;
@Column(columnName = "sname",columnType = "varchar(20)")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
(3)读取和处理自定义注解
自定义注解必须配上注解的信息处理流程才有意义。
我们自己定义的注解,只能使用反射的代码读取。所以自定义注解的声明周期必须是RetentionPolicy.RUNTIME。
具体的使用后边讲反射
再说。
六、JUnit单元测试
(1)测试分类
黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。(一般用来测试功能)
白盒测试:需要写代码的。关注程序具体的执行流程。
(2)JUnit单元测试介绍
JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个测试框架(regression testing framework),供Java开发人员编写单元测试之用。
JUnit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。
以前我们写代码都是在main
方法中测试的,但是在main方法中测试有一些不方便,比如想在后面测试其他的,与前面的代码无关,就要注释前面的代码,很麻烦。
这个时候我们就可以引用单元测试来替换原有的main方法。
要使用JUnit
,必须在项目的编译路径中引入JUnit的库
,即相关的.class
文件组成的jar包。
jar就是一个压缩包,压缩包都是开发好的第三方(Oracle公司第一方,我们自己第二方,其他都是第三方)工具类,都是以class文件形式存在的。
(3) 引入本地JUnit.jar
需要导入的jar包
:
- junit-4.12.jar
- hamcrest-core-1.3.jar
①第1步
在项目结构File-Project Structure中操作:添加Libraries库
其中,junit-libs包内容如下:
junit使用需要依赖上面的
②第2步
选择要在哪些module中应用JUnit库
③第3步
检查是否应用成功
注意Scope:选择Compile(默认就是),否则编译时,无法使用JUnit。
④第4步
下次如果有新的模块要使用该libs库,这样操作即可
在编译器里面直接写@Test,编译器报错,选择直接下载即可。
(4)测试
4.1 测试1
可以看到,导入之后这里有刚才导入的jar包:
然后我们需要的是junit里面的Test
结构。(具体细节不用关注)
然后我们在刚才关联的包下面创建java类,比如:
public class JUnitTest {
@Test
public void test1(){
System.out.println("hello");
}
}
可以看到左边有一个“运行”符号,点击它:
然后就可以看到输出:
这有什么特别的呢?
我们可以写多个,比如:
public class JUnitTest {
@Test
public void test1(){
System.out.println("hello");
}
@Test
public void test2(){
System.out.println("world");
}
}
在当前类里面还可以定义变量,这个变量还可以在方法里面调用,比如:
public class JUnitTest {
int number=10;
@Test
public void test1(){
System.out.println("hello");
}
@Test
public void test2(){
System.out.println("world");
System.out.println("number= "+number);
}
}
这时候再点击左边的运行:
输出结果:
我们在执行这个单元测试方法的时候,跟上面那个测试没有关系,上面写的那个就不执行。
只会测试test2。
所以我们可以写很多单元测试方法。
还可以写一个普通的方法,这个方法就可以在单元测试里面使用,比如:
public class JUnitTest {
int number=10;
@Test
public void test1(){
System.out.println("hello");
}
@Test
public void test2(){
System.out.println("world");
System.out.println("number= "+number);
method();
}
public void method(){
System.out.println("method()...");
}
}
test2()是非静态的,method()也是非静态的,非静态里面可以调非静态的,如下:
输出:
若是以前,number是一个非静态变量,method()是一个非静态的方法,如果想要测试,需要写在main方法里面。
又因为main方法是静态的,静态里面要是想调非静态方法,就需要造对象。
如下:
public static void main(String[] args) {
JUnitTest test=new JUnitTest();
System.out.println(test.number);
test.method();
}
而现在test2是非静态的方法,就可以直接调用非静态方法method()了。
如下:
@Test
public void test2(){
System.out.println("world");
System.out.println("number= "+number);
method();
}
所以很方便,而且想测试什么代码,就定义一个单独的单元测试即可。
这时候我们使用的注解@Test
就是在刚才导入的jar包里面。
🎲为啥用Main方法的时候需要造对象,而在单元测试类test2()里面就不需要了呢?
因为单元测试,在调用它的时候,它就会自动帮我们造,所以只需要写核心逻辑即可。
既然可以帮我们造对象,那么前提它要能造对象,所以所在类需要是非抽象的。
如果想在其他Module下面使用,比如在模块face06下面新建一个java类,可以看到报错了:
这是因为这个face06模块根本没有使用jar包。
怎么解决呢?
①如果现在你联网了,可以这样操作:
将鼠标放在报错的地方,然后按Alt+Enter,点击添加JUnit:
然后就点击“Ok”,就可以联网下载了。
②如果现在你没有联网,可以这样操作:
还是回到“项目结构”:
因为当前工程已经下载过jar包一次了,所以当前Module用一下即可。
具体操作如下:
然后添加:
然后就可以在这里看见Junit:
只是后面的是Test–“测试”,必须要改为Compile–“编译”。如下:
然后OK即可。
再次回到刚才的代码,可以看到可以使用了,如下:
单元测试方法,就是写代码不需要再用main方法了
4.2 测试2
package com.atguigu.junit;
import org.junit.Test;
public class TestJUnit {
@Test
public void test01(){
System.out.println("TestJUnit.test01");
}
@Test
public void test02(){
System.out.println("TestJUnit.test02");
}
@Test
public void test03(){
System.out.println("TestJUnit.test03");
}
}
(5)编写和运行@Test单元测试方法
(重点关注)
JUnit4版本,要想能正确的编写单元测试方法,要求@Test
标记的方法必须满足如下要求:
- 所在的类(单元测试类)必须是
public
的,非抽象的(这个类可以造对象),包含唯一的无参构造器。 @Test
标记的方法(单元测试方法)本身必须是public
,非抽象的(肯定要是非抽象的,若是抽象的那就没有方法体,要它没用),非静态的(不能加static,直接拿对象去调用),void无返回值(和main方法一样,是程序的入口,不需要返回什么了),()无参数的。
【举例】
public class JUnitTest { //单元测试类(包含单元测试方法)
public static void main(String[] args) {
JUnitTest test=new JUnitTest();
System.out.println(test.number);
test.method();
}
int number=10;
@Test
public void test1(){ //单元测试方法
System.out.println("hello");
}
@Test
public void test2(){
System.out.println("world");
System.out.println("number= "+number);
method();
}
public void method(){
System.out.println("method()...");
}
}
🎲为啥用Main方法的时候需要造对象,而在单元测试类test2()里面就不需要了呢?
因为单元测试,在调用它的时候,它就会自动帮我们造,所以只需要写核心逻辑即可。
既然可以帮我们造对象,那么前提它要能造对象,所以所在类需要是非抽象的。
同时只能有一个无参构造器。
若是有多余的构造器,比如:
package yuyi08;
import org.junit.Test;
public class JUnitTest { //单元测试类(包含单元测试方法)
int number=10;
public JUnitTest(){
}
public JUnitTest(int number){
this.number=number;
}
@Test
public void test1(){ //单元测试方法
System.out.println("hello");
}
}
运行会报错:
而无参构造器可以不写,所以干脆都别写了。
对于单元测试方法不要有参数,返回值,若是有参数也没有办法传。
但是普通方法是可以有的。
比如:
package yuyi08;
import org.junit.Test;
public class JUnitTest { //单元测试类(包含单元测试方法)
int number=10;
public JUnitTest(){
}
@Test
public void test2(){
System.out.println("world");
System.out.println("number= "+number);
method();
int num=showInfo("China");
System.out.println(num);
}
public void method(){
System.out.println("method()...");
}
public int showInfo(String info){ //可以有返回值类型,有形参,也可以有多个
System.out.println(info);
return 10;
}
}
要测试的方法什么样的都有可能,看一下输出:
对于单元测试方法来说,不要有参数,若是有也没有办法给它传实参。
这里只是调用执行一下而已。
这些要求不必要都记住,只需要造完一个类,然后在里面写单元测试方法的时候,直接用public void 名字(){ }即可。关于类也不用提供构造器,直接使用无参的即可。
(6) 设置执行JUnit用例时支持控制台输入
默认情况下,单元测试方法中使用Scanner失效。
比如,现在有一个单元测试方法test3()
,如下:
package yuyi08;
import org.junit.Test;
import java.util.Scanner;
public class JUnitTest { //单元测试类(包含单元测试方法)
@Test
public void test3(){
Scanner scan=new Scanner(System.in);
System.out.println("请输入一个值");
int num=scan.nextInt();
System.out.println(num);
}
}
运行test3()
的时候,可以发现没有办法从键盘输入数据进去:
可以看到一直在转圈,没有办法输入数据。
☕如何解决?
1. 设置数据
默认情况下,在单元测试方法中使用Scanner时,并不能实现控制台数据的输入。需要做如下设置:
在idea64.exe.vmoptions配置文件
中加入下面一行设置,重启idea后生效。
-Deditable.java.test.console=true
2. 配置文件位置
添加完成之后,重启IDEA即可。
重启:
3. 如果上述位置设置不成功,需要继续修改如下位置
修改位置1:IDEA安装目录的bin目录(例如:D:\develop_tools\IDEA\IntelliJ IDEA 2022.1.2\bin
)下的idea64.exe.vmoptions文件。
修改位置2:C盘的用户目录C:\Users\用户名\AppData\Roaming\JetBrains\IntelliJIdea2022.1
下的idea64.exe.vmoptions`件。
再回到最初的代码,就可以输入啦。如下:
按下回车之后:
(7) 定义test测试方法模板
以后写代码会经常写“单元测试方法”,大家可以将单元测试方法设置为一个模板
。
步骤:
①“文件”–> “设置”
②“实时模板”–> “模板组”
建议先创建一个模板组,如下:
③给组取个名字,比如“用户自定义组”–CustomDefine,如下:
④在组里面定义一个“模板”,如下:
⑤写模板
比如模板是这样:
写完一回车,希望光标在哪里,就写两个$符号,在中间写var2即可,若是最后,可以写end,比如:
@Test
public void test$var1$(){
$end$
}
注意写完之后,一定要选择“在Java相关逻辑中使用”,如下:
OK,那么以后想写单元测试方法,可以直接用刚才我们写的快捷方式,输入“test”,然后就会自动显示我们写的模板,如下:
然后点击它即可,自动写出模板:
【总结】
选中自定义的模板组,点击”+”(1.Live Template)来定义模板。