java单元测试JUnit框架原理与⽤法实例教程
本⽂实例讲述了java单元测试JUnit框架原理与⽤法。分享给⼤家供⼤家参考,具体如下:
1 简介
JUnit是⼀个Java语⾔的单元测试框架,它由 Kent Beck 和 Erich Gamma 建⽴,逐渐成为 xUnit 家族中最为成功的⼀个。JUnit有它⾃⼰的JUnit扩展⽣态圈,多数Java的开发环境都已经集成了JUnit作为单元测试的⼯具。在这⾥,⼀个单元可以是⼀个⽅法、类、包或者⼦系统。因此,单元测试是指对代码中的最⼩可测试单元进⾏检查和验证,以便确保它们正常⼯作。例如,我们可以给予⼀定的输⼊测试输出是否是所希望得到的结果。在本篇博客中,作者将着重介绍 JUnit 4.X 版本的特性,这也是我们在⽇常开发中使⽤最多的版本。
2 特点
JUnit提供了注释以及确定的测试⽅法;
JUnit提供了断⾔⽤于测试预期的结果;
JUnit测试优雅简洁不需要花费太多的时间;
JUnit测试让⼤家可以更快地编写代码并且提⾼质量;
JUnit测试可以组织成测试套件包含测试案例,甚⾄其他测试套件;
Junit显⽰测试进度,如果测试是没有问题条形是绿⾊的,测试失败则会变成红⾊;
JUnit测试可以⾃动运⾏,检查⾃⼰的结果,并提供即时反馈,没有必要通过测试结果报告来⼿动梳理。
3 内容
3.1 注解
@Test :该注释表⽰,⽤其附着的公共⽆效⽅法(即⽤public修饰的void类型的⽅法)可以作为⼀个测试⽤例;
@Before :该注释表⽰,⽤其附着的⽅法必须在类中的每个测试之前执⾏,以便执⾏测试某些必要的先决条件;
@BeforeClass :该注释表⽰,⽤其附着的静态⽅法必须执⾏⼀次并在类的所有测试之前,发⽣这种情况时⼀般是测试计算共享配置⽅法,如连接到数据库;
@After :该注释表⽰,⽤其附着的⽅法在执⾏每项测试后执⾏,如执⾏每⼀个测试后重置某些变量,删除临时变量等;
@AfterClass :该注释表⽰,当需要执⾏所有的测试在JUnit测试⽤例类后执⾏,AfterClass注解可以使⽤以清理建⽴⽅法,如断开数据库连接,注意:附有此批注(类似于BeforeClass)的⽅法必须定义为静态;
@Ignore :该注释表⽰,当想暂时禁⽤特定的测试执⾏可以使⽤忽略注释,每个被注解为@Ignore的⽅法将不被执⾏。
/**
* JUnit 注解⽰例
*/
@Test
public void testYeepay(){
Syetem.out.println("⽤@Test标⽰测试⽅法!");
}
@AfterClass
public static void paylus(){
Syetem.out.println("⽤@AfterClass标⽰的⽅法在测试⽤例类执⾏完之后!");
}
3.2 断⾔
在这⾥,作者将介绍⼀些断⾔⽅法,所有这些⽅法都来⾃ org.junit.Assert 类,其扩展了 java.lang.Object 类并为它们提供编写测试,以便检测故障。简⽽⾔之,我们就是通过断⾔⽅法来判断实际结果与我们预期的结果是否相同,如果相同,则测试成功,反之,则测试失败。
void assertEquals([String message], expected value, actual value) :断⾔两个值相等,值的类型可以为int、short、long、byte、char 或者
java.lang.Object,其中第⼀个参数是⼀个可选的字符串消息;
void assertTrue([String message], boolean condition) :断⾔⼀个条件为真;
void assertFalse([String message],boolean condition) :断⾔⼀个条件为假;
void assertNotNull([String message], java.lang.Object object) :断⾔⼀个对象不为空(null);
void assertNull([String message], java.lang.Object object) :断⾔⼀个对象为空(null);
void assertSame([String message], java.lang.Object expected, java.lang.Object actual) :断⾔两个对象引⽤相同的对象;void assertNotSame([String message], java.lang.Object unexpected, java.lang.Object actual) :断⾔两个对象不是引⽤同⼀个对象;
4 JUnit 3.X 和 JUnit 4.X 的区别
4.1 JUnit 3.X
(1)使⽤ JUnit 3.X 版本进⾏单元测试时,测试类必须要继承于 TestCase ⽗类;
(2)测试⽅法需要遵循的原则:
① public的;② void的;③⽆⽅法参数;④⽅法名称必须以 test 开头;
(3)不同的测试⽤例之间⼀定要保持完全的独⽴性,不能有任何的关联;
(4)要掌握好测试⽅法的顺序,不能依赖于测试⽅法⾃⼰的执⾏顺序。
/**
* ⽤ JUnit 3.X 进⾏测试
*/
import junit.framework.Assert;
import junit.framework.TestCase;
public class TestOperation extends TestCase {
private Operation operation;
public TestOperation(String name) { // 构造函数
super(name);
}
@Override
public void setUp() throws Exception { // 在每个测试⽅法执⾏ [之前] 都会被调⽤,多⽤于初始化
System.out.println("欢迎使⽤Junit进⾏单元测试...");
operation = new Operation();
}
@Override
public void tearDown() throws Exception { // 在每个测试⽅法执⾏ [之后] 都会被调⽤,多⽤于释放资源
System.out.println("Junit单元测试结束...");
}
public void testDivideByZero() {
Throwable te = null;
try {
operation.divide(6, 0);
Assert.fail("测试失败"); //断⾔失败
} catch (Exception e) {
e.printStackTrace();
te = e;
}
Assert.assertEquals(Exception.class, te.getClass());
Assert.assertEquals("除数不能为 0 ", te.getMessage());
}
}
4.2 JUnit 4.X
(1)使⽤ JUnit 4.X 版本进⾏单元测试时,不⽤测试类继承TestCase⽗类;
(2)JUnit 4.X 版本,引⽤了注解的⽅式进⾏单元测试;
(3)JUnit 4.X 版本我们常⽤的注解包括:
@Before 注解:与JUnit 3.X 中的 setUp() ⽅法功能⼀样,在每个测试⽅法之前执⾏,多⽤于初始化;
@After 注解:与 JUnit 3.X 中的 tearDown() ⽅法功能⼀样,在每个测试⽅法之后执⾏,多⽤于释放资源;
@Test(timeout = xxx) 注解:设置当前测试⽅法在⼀定时间内运⾏完,否则返回错误;
@Test(expected = Exception.class) 注解:设置被测试的⽅法是否有异常抛出。抛出异常类型为:Exception.class;此外,我们可以通过阅读上⾯的第⼆部分“2 注解”了解更多的注解。
/**
* ⽤ JUnit 4.X 进⾏测试
*/
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestOperation {
private Operation operation;
@BeforeClass
public static void globalInit() { // 在所有⽅法执⾏之前执⾏
}
@AfterClass
public static void globalDestory() { // 在所有⽅法执⾏之后执⾏
System.out.println("@AfterClass标注的⽅法,在所有⽅法执⾏之后执⾏...");
}
@Before
public void setUp() { // 在每个测试⽅法之前执⾏
System.out.println("@Before标注的⽅法,在每个测试⽅法之前执⾏...");
operation = new Operation();
}
@After
public void tearDown() { // 在每个测试⽅法之后执⾏
System.out.println("@After标注的⽅法,在每个测试⽅法之后执⾏...");
}
@Test(timeout=600)
public void testAdd() { // 设置限定测试⽅法的运⾏时间如果超出则返回错误
System.out.println("测试 add ⽅法...");
int result = operation.add(2, 3);
assertEquals(5, result);
}
@Test
public void testSubtract() {
System.out.println("测试 subtract ⽅法...");
int result = operation.subtract(1, 2);
assertEquals(-1, result);
}
@Test
public void testMultiply() {
System.out.println("测试 multiply ⽅法...");
int result = operation.multiply(2, 3);
assertEquals(6, result);
}
@Test
public void testDivide() {
System.out.println("测试 divide ⽅法...");
int result = 0;
try {
result = operation.divide(6, 2);
} catch (Exception e) {
fail();
}
assertEquals(3, result);
}
@Test(expected = Exception.class)
public void testDivideAgain() throws Exception {
System.out.println("测试 divide ⽅法,除数为 0 的情况...");
operation.divide(6, 0);
fail("test Error");
}
public static void main(String[] args) {
}
}
4.3 特别提醒
通过以上两个例⼦,我们已经可以⼤致知道 JUnit 3.X 和 JUnit 4.X 两个版本的区别啦!⾸先,如果我们
java arraylist用法使⽤ JUnit 3.X,那么在我们写的测试类的时候,⼀定要继承 TestCase 类,但是如果我们使⽤ JUnit 4.X,则不需继承 TestCase 类,直接使⽤注解就可以啦!在 JUnit 3.X 中,还强制要求测试⽅法的命名为“ testXxxx ”这种格式;在 JUnit 4.X 中,则不要求测试⽅法的命名格式,但作者还是建议测试⽅法统⼀命名为“ testXxxx ”这种格式,简洁明了。
此外,在上⾯的两个⽰例中,我们只给出了测试类,但是在这之前,还应该有⼀个被测试类,也就是我们真正要实现功能的类。现在,作者将给出上⾯⽰例中被测试的类,即 Operation 类:
/**
* 定义了加减乘除的法则
*/
public class Operation {
public static void main(String[] args) {
System.out.println("a + b = " + add(1,2));
System.out.println("a - b = " + subtract(1,2));
System.out.println("a * b = " + multiply(1,2));
System.out.println("a / b = " + divide(4,2));
System.out.println("a / b = " + divide(1,0));
public static int add(int a, int b) {
return a + b;
}
public static int subtract(int a, int b) {
return a - b;
}
public static int multiply(int a, int b) {
return a * b;
}
public static int divide(int a, int b) {
return a / b;
}
}
5 测试⽰例
5.1 ⽰例⼀:简单的 JUnit 3.X 测试
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.util.ArrayList;
import java.util.Collection;
/**
* 1、创建⼀个测试类,继承TestCase类
*/
public class SimpleTestDemo extends TestCase {
public SimpleTestDemo(String name) {
super(name);
}
/**
* 2、写⼀个测试⽅法,断⾔期望的结果
*/
public void testEmptyCollection(){
Collection collection = new ArrayList();
assertTrue(collection.isEmpty());
}
/**
* 3、写⼀个suite()⽅法,它会使⽤反射动态的创建⼀个包含所有的testXxxx⽅法的测试套件
*/
public static Test suit(){
return new TestSuite(SimpleTestDemo.class);
}
/**
* 4、写⼀个main()⽅法,以⽂本运⾏器的⽅式⽅便的运⾏测试
*/
public static void main(String[] args) {
}
}
5.2 ⽰例⼆:套件测试
⾸先,介绍⼀下套件测试,简单来讲,测试套件是指:⼀些测试不同类的⽤例,可以使⽤ @RunWith 和 @Suite 注解把所有的测试类套在⼀起,从⽽形成测试套件。如果有很多测试类,想让它们都运⾏在同⼀时间,⽽不是单⼀地运⾏每个测试,套件测试是⾮常有⽤的。当⼀个类被注解为 @RunWith, JUnit 将调⽤其中的注解,以便运⾏测试类,⽽不使⽤内置的 JUnit 运⾏⽅法。
/**
* 待测试类
*/
import java.util.Arrays;
public class GotoWork {
public String[] prepareSkills() {
String[] skill = { "Java", "MySQL", "JSP" };
System.out.println("My skills include : " + String(skill));
return skill;
}
public String[] addSkills() {
String[] skill = { "Java", "MySQL", "JSP", "JUnit" };
System.out.println("Look, my skills include : " + String(skill));
return skill;
}
/**
* 测试类 1
*/
import org.junit.Test;
import static org.junit.Assert.*;
public class PrepareSkillsTest {
GotoWork gotoWork = new GotoWork();
String[] skill = { "Java", "MySQL", "JSP" };
@Test
public void testPrepareSkills() {
System.out.println("Inside testPrepareSkills()");
assertArrayEquals(skill, gotoWork.prepareSkills());
}
}
/**
* 测试类 2
*/
import org.junit.Test;
import static org.junit.Assert.*;
public class AddSkillsTest {
GotoWork gotoWork = new GotoWork();
String[] skill = { "Java", "MySQL", "JSP", "JUnit" };
@Test
public void testAddSkills() {
System.out.println("Inside testAddPencils()");
assertArrayEquals(skill, gotoWork.addSkills());
}
}
/**
* 套件测试
*/
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({ PrepareSkillsTest.class, AddSkillsTest.class })
public class SuitTest {
}
使⽤ @Suite.SuiteClasses 注解,可以定义测试类,将被列⼊执⾏,并且执⾏的顺序就是在 @Suite.SuiteClasses 注解中定义的顺序。
5.3 ⽰例三:参数化测试
⾸先介绍⼀下参数化测试,⼀个测试类可以被看作是⼀个参数化测试类,当其满⾜下列所有要求:
①该类被注解为 @RunWith(Parameterized.class);
②该类有⼀个构造函数,存储测试数据;
③该类有⼀个静态⽅法⽣成并返回测试数据,并标注 @Parameters 注解;
④该类有⼀个测试⽅法,即⽤注解 @Test 标注的⽅法。
/**
* 待测试类
*/
public class Calculate {
public int sum(int var1, int var2) {
System.out.println("此⽅法的参数值分别为 : " + var1 + " + " + var2);
return var1 + var2;
}
}
/**
* 参数化测试类
*/
import static org.junit.Assert.assertEquals;
import java.util.Arrays;