JUnit4单元测试入门教程

article/2025/10/2 13:01:53

JUnit4单元测试入门教程 - 简书

本文按以下顺序讲解JUnit4的使用

  • 下载jar包
  • 单元测试初体验
  • 自动生成测试类
  • 执行顺序
  • @Test的属性

下载jar包##

下载地址 在github上,把以下两个jar包都下载下来。

下载junit-4.12.jar,junit-4.12-javadoc.jar(文档),junit-4.12-sources.jar(源码)。

下载hamcrest-core-1.3.jar,hamcrest-core-1.3-javadoc.jar(文档),hamcrest-core-1.3-sources.jar(源码)。

最前面那个pom是Maven的配置文件,如果你需要的话也下载下来。

单元测试初体验##

先创建个简单的项目体验下单元测试是怎么一回事吧。

我创建一个项目叫JUnit4Demo,然后创建一个lib文件夹放刚下载的<code>junit-4.12.jar</code>和<code>hamcrest-core-1.3.jar</code>两个jar包并导入到项目里。
创建一个类com.xuhongchuan.util.Math,然后输入一个求阶乘的方法:

package com.xuhongchuan.util;/*** Created by xuhongchuan on 2015/7/18.*/
public class Math {/*** 阶乘* @param n* @return*/public int factorial(int n) throws Exception {if (n < 0) {throw new Exception("负数没有阶乘");} else if (n <= 1) {return 1;} else {return n * factorial(n - 1);}}}

此时的项目结构是这样的:

好了,接下来要创建一个类来对Math类进行单元测试。
创建一个和src同级别的文件夹叫test(逻辑代码放src里,测试代码放test里是个好习惯)。
接着在IntelliJ IDEA里还要把这个test文件夹要设置成测试文件的根目录,右键选中
Mark Directory As - Test Sources Root。

然后创建com.xuhongchuan.util.MathTest类(包名一致,类名在要测试的类名后加上Test也是个好习惯)。
在MathTest里输入以下内容:

package com.xuhongchuan.util;import org.junit.Test;
import static org.junit.Assert.*;/*** Created by xuhongchuan on 2015/7/18.*/
public class MathTest {@Testpublic void testFactorial() throws Exception {assertEquals(120, new Math().factorial(5));}}

然后选中MathTest类ctrl + shift + F10运行一下,结果如下。

右下方一条绿色条说明测试通过,如果把120改成别的数字那么就会测试不通过显色红色条。JUnit4有一句话叫:“keeps the bar green to keep the code clean”。

解释一下MathTest,就六个地方要讲:
第一,导入了org.junit.Test;和org.junit.Assert.*;这两个包,注意后者是静态导入import static。
第二,testFactorial是在要测试的方法名Factorial前加个test(这也是个好习惯)。
第三,所有测试方法返回类型必须为void且无参数。
第四,一个测试方法之所以是个测试方法是因为@Test这个注解。
第五,assertEquals的作用是判断两个参数是否相等,例子中120是预期结果,new Math().factorial(5)是实际结果。但是通常不应该只比较一个值,要测试多几个特殊值,特别是临界值。例如Math().factorial(0)和Math().factorial(-1)等。
第六,assertEquals除了比较两个int,还重载了好多次可以比较很多种类型的参数。而且JUnit4包含一堆assertXX方法,assertEquals只是其中之一,这些assertXX统称为断言。刚不是下载了junit-4.12-javadoc.jar这个文档吗,解压后打开index.html如下图还有一堆断言。

自动生成测试类##

我们把测试类MathTest删掉,回到逻辑代码Math里再添加一个方法求斐波那契数列:

    /*** 斐波那契数列* @param n* @return*/public int fibonacci(int n) throws Exception {if (n <= 0) {throw new Exception("斐波那契数列从第1位开始");} else if (n == 1) {return 0;} else if (n == 2) {return 1;} else {return fibonacci(n - 1) + fibonacci(n - 2);}}

现在的项目结构是这样的(测试类MathTest被删掉了)。

现在Math类有两个方法了,这里假设有十个、二十个甚至更多方法,如果要写测试方法都要自己一个一个写吗?那太累了,IntelliJ IDEA是可以自动生成测试方法的基本结构的。按快捷键ctrl - shift - T。
弹出的对话框点击Create New Test...

选择JUnit4,类名和包名还是默认的已经符合规范了,然后勾选要生成测试方法的方法。点击OK。

点击自动生成的测试类MathTest,可以看到测试方法的基本结构已经自动生成了。我们再自己添加测试代码就行了。
在testFactorial()添加:

assertEquals(120, new Math().factorial(5));

在testFibonacci()方法添加:

assertEquals(21, new Math().fibonacci(9));

运行后,绿条又出现了,测试成功。

执行顺序##

JUnit4利用JDK5的新特性Annotation,使用注解来定义测试规则。
这里讲一下以下几个常用的注解:

  • @Test:把一个方法标记为测试方法
  • @Before:每一个测试方法执行前自动调用一次
  • @After:每一个测试方法执行完自动调用一次
  • @BeforeClass:所有测试方法执行前执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
  • @AfterClass:所有测试方法执行完执行一次,在测试类还没有实例化就已经被加载,所以用static修饰
  • @Ignore:暂不执行该测试方法

我们来试验一下,我新建一个测试类AnnotationTest,然后每个注解都用了,其中有两个用@Test标记的方法分别是test1和test2,还有一个用@Ignore标记的方法test3。然后我还创建了一个构造方法,这个构造方法很重要一会会引出一个问题。
具体代码如下:

package com.xuhongchuan.util;import org.junit.*;
import static org.junit.Assert.*;/*** Created by xuhongchuan on 2015/7/18.*/
public class AnnotationTest {public AnnotationTest() {System.out.println("构造方法");}@BeforeClasspublic static void setUpBeforeClass() {System.out.println("BeforeClass");}@AfterClasspublic static void tearDownAfterClass() {System.out.println("AfterClass");}@Beforepublic void setUp() {System.out.println("Before");}@Afterpublic void tearDown() {System.out.println("After");}@Testpublic void test1() {System.out.println("test1");}@Testpublic void test2() {System.out.println("test2");}@Ignorepublic void test3() {System.out.println("test3");}}

运行结果如下:
<pre>
BeforeClass
构造方法
Before
test1
After
构造方法
Before
test2
After
AfterClass
</pre>

解释一下:@BeforeClass和@AfterClass在类被实例化前(构造方法执行前)就被调用了,而且只执行一次,通常用来初始化和关闭资源。@Before和@After和在每个@Test执行前后都会被执行一次。@Test标记一个方法为测试方法没什么好说的,被@Ignore标记的测试方法不会被执行,例如这个模块还没完成或者现在想测试别的不想测试这一块。
以上有一个问题,构造方法居然被执行了两次。所以我这里要说明一下,JUnit4为了保证每个测试方法都是单元测试,是独立的互不影响。所以每个测试方法执行前都会重新实例化测试类。

我再给你看一个实验:
添加一个成员变量

int i = 0;

然后把test1改为:

    i++;System.out.println("test1的i为" + i);

test2改为:

    i++;System.out.println("test2的i为" + i);

执行结果:
<pre>
BeforeClass
构造方法
Before
test1的i为1
After
构造方法
Before
test2的i为1
After
AfterClass
</pre>

可以看到test1和test2的i都只自增了一次,所以test1的执行不会影响test2,因为执行test2时又把测试类重新实例化了一遍。如果你希望test2的执行受test1的影响怎么办呢?把int i改为static的呗。

最后关于这些注解还有一个要说明的就是,你可以把多个方法标记为@BeforeClass、@AfterClass、@Before、@After。他们都会在相应阶段被执行。

@Test的属性##

最后来说一下@Test的两个属性

  • excepted
  • timeout
    excepted属性是用来测试异常的,我们回到Math类,拿其中的求阶乘方法factorial()来说。

    public int factorial(int n) throws Exception {if (n < 0) {throw new Exception("负数没有阶乘");} else if (n <= 1) {return 1;} else {return n * factorial(n - 1);}}

如果传进来一个负数我们是希望抛出异常的,那要测试会不会抛异常怎么办呢?
我在测试类MathTest添加一个测试方法:

    @Test(expected = Exception.class)public void testFactorialException() throws Exception {new Math().factorial(-1);fail("factorial参数为负数没有抛出异常");}

这个方法就是(expected = Exception.class)和fail("factorial参数为负数没有抛出异常");之间的配合。就是这个测试方法会检查是否抛出Exception异常(当然也可以检测是否抛出其它异常),如果抛出了异常那么测试通过(因为你的预期就是传进负数会抛出异常)。没有抛出异常则测试不通过执行fail("factorial参数为负数没有抛出异常");

然后说下timeout属性,这个是用来测试性能的,就是测试一个方法能不能在规定时间内完成。
回到Math类,我创建一个数组排序的方法,用的是冒泡排序。

    public void sort(int[] arr) {//冒泡排序for (int i = 0; i < arr.length - 1; i++) { //控制比较轮数for (int j = 0; j < arr.length - i - 1; j++) { //控制每轮的两两比较次数if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}}

然后偶在测试类MathTest创建测试方法,随机生成一个长度为50000的数组然后测试排序所用时间。timeout的值为2000,单位和毫秒,也就是说超出2秒将视为测试不通过。

    @Test(timeout = 2000)public void testSort() throws Exception {int[] arr = new int[50000]; //数组长度为50000int arrLength = arr.length;//随机生成数组元素Random r = new Random();for (int i = 0; i < arrLength; i++) {arr[i] = r.nextInt(arrLength);}new Math().sort(arr);}

运行结果测试不通过,且提示TestTimedOutException。

那怎么办,修改代码提升性能呗。回到Math方法改为下sort()。这次我用快速排序,代码如下:

    public void sort(int[] arr) {//快速排序if (arr.length <= 1) {return;} else {partition(arr, 0, arr.length - 1);}}static void partition(int[] arr, int left, int right) {int i = left;int j = right;int pivotKey = arr[left]; //基准数while (i < j) {while (i < j && arr[j] >= pivotKey) {j--;}while (i < j && arr[i] <= pivotKey) {i++;}if (i < j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}if (i != left) {arr[left] = arr[i];arr[i] = pivotKey;}if (i - left > 1) {partition(arr, left, i - 1);}if (right - j > 1) {partition(arr, j + 1, right);}}

然后再运行一下测试类MathTest,绿色条出现了,测试通过妥妥的。

本文代码下载:百度网盘


http://chatgpt.dhexx.cn/article/hNQAPuQe.shtml

相关文章

JUnit4的使用和配置

JUnit4是JUnit框架有史以来的最大改进&#xff0c;其主要目标便是利用Java5的Annotation特性简化测试用例的编写。 先简单解释一下什么是Annotation&#xff0c;这个单词一般是翻译成元数据。元数据是什么&#xff1f;元数据就是描述数据的数据。也就是说&#xff0c;这个东西在…

浅谈java单元测试框架junit4/5

0 前言 junit是一个开源的Java语言的单元测试框架。目前junit主要有版本junit3&#xff0c;junit4和junit5。因在junit3中&#xff0c;是通过对测试类和测试方法的命名来确定是否是测试&#xff0c;且所有的测试类必须继承junit的测试基类TestCase&#xff0c;所以本文不再讨论…

IDEA中使用JUnit4(单元测试框架)超详细!

IDEA中使用JUnit4教程 超详细&#xff01;(单元测试框架) 导语&#xff1a;自动化测试的必经之路–Selenium 作者&#xff1a;变优秀的小白 Github&#xff1a;YX-XiaoBai QQ交流群(new): 811792998 爱好&#xff1a;Americano More Ice ! 话不多说&#xff0c;实战为主&…

Junit 4 的使用

一、什么是 Junit 我们来百度一波&#xff0c;什么是 Junit 可以看到哈&#xff0c;Junit 是一个 Java 语言的单元测试框架&#xff0c;这个东西是程序员自测所需要的一个东西&#xff0c;这个测试也被称为白盒测试。&#xff08;下面会去说什么是白盒测试&#xff09; 我们之…

JUnit4

1.JUnit4全面引入Annotation来执行我们编写的测试 2.JUnit4并不要求测试类继承TestCase父类 3.在一个测试类中&#xff0c;所有被Test注解所修饰的public,void方法都是test case,可以被JUnit所执行。 4.虽然JUnit4并不要求测试方法名以test开头&#xff0c;但我们最好还是按照 …

Junit 4详解

Java单元机测试框架 --- Junit4 1、什么是Junit4 JUnit4是一个易学易用的Java单元测试框架,一般我们在写完一段代码或一个方法的时候,都要测试一下这段代码和这个方法的逻辑是不是正确,输入一定的数据,返回的数据是不是我们想要的结果,即我们在写单个业务代码针对结果进行…

IOS UIBUtton

Type 第二个是Customer 常用 按钮的阴影效果只能左右 这是区别于标签的地方 阴影设置没有负值 按钮的代码使用 按钮点击方法 代码设置 传参

UIButton设置图片位置

设置小图片image的位置 image默认图片保持原大小可以通过设置contentVerticalAlignment和contentHorizontalAlignment&#xff0c;修改位置&#xff0c;甚至填充满按钮 // 修改图片位置 图2的效果[button setImage:image forState:UIControlStateNormal];button.contentVerti…

UIButton基础总结

1、UIButton简介 UIButton继承自UIControl。 2、UIButton的四种状态 UIButton的四种状态分别为Normal、Highlighted、Disabled和Selected。 **&#xff08;1&#xff09;Normal&#xff1a;**按钮的普通状态&#xff0c;即为按钮的初始状态 **&#xff08;2&#xff09;Highlig…

[Swift]代码触发UIButton的点击事件

使用极光的手机号码一键登录功能&#xff0c;要求点击按钮后先弹出协议同意&#xff0c;同意协议后自动改变底部协议状态再自动代码触发登录按钮的点击事件。 OC: [but sendActionsForControlEvents:UIControlEventTouchUpInside];Swift: but.sendActions(for: .touchUpInsid…

iOS UIButton控件

UIButton是UIControl的子类&#xff0c;实现了按钮功能&#xff0c;交互事件和控件状态可查看iOS UIControl控件。 1. 初始化 通过指定按钮类型来创建UIButton对象 (instancetype)buttonWithType:(UIButtonType)buttonType;UIButtonType是一个枚举类型 值说明UIButtonTypeCu…

UIButton基础

一、UIButton基础 与UILabel相同&#xff0c;UIButton对象也需要在ViewController中写一个创建函数来建立 UIButton对象的建立如下&#xff1a; //创建普通按钮函数 - (void) createUIRectButton {//创建一个btn对象&#xff0c;更具类型来创建btn//圆角类型btn:UIButtonType…

UIButton 基础

创建一个button 注意button只能通过类方法创建&#xff0c;不能使用alloc 该段代码添加在函数- (void)viewDidLoad 中 //通过类方法创建一个UIbuttonUIButton* btn [UIButton buttonWithType:UIButtonTypeRoundedRect] ;//设置按钮的位置btn.frame CGRectMake(100, 100, 100…

UIButton基础知识和自定义详解

UIButton是我们经常用的UI控件&#xff0c;继承UIControl。这里将对UIButton的基本使用方法和自定义UIButton进行详细介绍。 一、UIBUtton基本知识介绍 对于我们学习一个新的控件、无外乎两种方法。第一种是在xcode中的.m文件查看该控件的属性和相关方法&#xff0c;第二种直…

UI基本控件(二):UIButton

UIButton——按钮 作用&#xff1a;用户交互的主要控件&#xff0c;有六种类型&#xff0c;其中自定义类型使用最为普遍 属性&#xff1a; title属性&#xff1a;是按钮的文字 titleColor属性&#xff1a;是按钮的颜色 image属性&#xff1a;是按钮显示的图像 提示&#…

oracle 用impdp 导入dmp文件

百度整理如下 /*分为7步 */ /*第1步&#xff1a;创建临时表空间(注意&#xff1a;D:\Project\OracleTableSpace\FHADMIN\ 手动创建路径) */ create temporary tablespace C##FHADMIN_TEMP tempfile D:\Project\OracleTableSpace\FHADMIN\C##FHADMIN_TEMP.dbf size 50m a…

★Oracle imp/impdp 导入dmp文件到数据库

使用EXPDP和IMPDP时应该注意的事项&#xff1a; EXP和IMP是客户端工具程序&#xff0c;它们既可以在客户端使用&#xff0c;也可以在服务端使用。 EXPDP和IMPDP是服务端的工具程序&#xff0c;他们只能在ORACLE服务端使用&#xff0c;不能在客户端使用。 IMP只适用于EX…

Oracle:使用Impdp导入dmp文件的详细过程

完全转载自&#xff1a;https://www.cnblogs.com/afei1013/p/13123784.html 这一天为了导入这个Oracle的dmp文件&#xff0c;简直就是血泪史&#xff0c;因本人对Oracle并不是很会&#xff0c;随意踩了很多小白会踩的坑&#xff0c;因此特意记录一下过程&#xff0c;防备下次的…

Oracle expdp/impdp工具使用

Oracle数据泵 一、数据泵的作用&#xff1a; 1.实现逻辑备份和逻辑恢复 2.在数据库用户之间移动对象 3.在数据库之间移动对象 4.实现表空间转移 二 、数据泵的特点与传统导出导入的区别 1.EXP和IMP是客户段工具程序&#xff0c; EXPDP和IMPDP是服务端的工具程序 2.EXP和IMP效率…

impdp导入dmp文件

impdp命令在cmd下直接用&#xff0c;不必登录oracle。只能导入expdp导出的dmp文件。 expdp导出的时候&#xff0c;需要创建 DIRECTORY 导出什么表空间&#xff0c;导入也要什么表空间。 导出什么用户&#xff0c;导入也要什么用户。 如果没有要新建。 从杭州服务器expdp导出…