Junit 单元测试
单元测试概述
单元测试就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。
只能在 main 方法编写测试代码,去调用其他方法进行测试。
无法实现自动化测试,一个方法测试失败,可能影响其他方法的测试。
无法得到测试的报告,需要程序员自己去观察测试是否成功。
Junit 框架
JUnit 是一个 Java 语言的单元测试框架。
可以用来对方法进行测试,多数 Java 的开发环境都已经集成了 JUnit 作为单元测试的工具。
可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
Junit 框架入门
开始测试
需求
某个系统,有多个业务方法,请使用 Junit 单元测试框架,编写测试代码,完成对这些方法的正确性测试。
具体步骤
① 将 Junit 框架的 jar 包导入到项目中(注意:IDEA 集成了 Junit 框架,不需要我们自己手动导入了)。
② 为需要测试的业务类,定义对应的测试类,并为每个业务方法编写对应的测试方法(测试方法必须是 公共的、无参的、无返回值的 )。
③ 测试方法上必须声明 @Test 注解 ,然后在测试方法中,编写代码调用被测试的业务方法进行测试。
④ 选中测试方法,右键选择 " Junit 运行 “,如果 测试通过 则是 绿色 ,如果 测试失败 ,则是 红色 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package mytest;
public class StringUtil {
public static void main(String[] args) {
}
// 获取字符串长度
public static void printNumber(String name) {
System.out.println(name + "的长度是:" + name.length());
}
// 获取字符串最大索引
public static int getMaxIndex(String data){
if (data == null){
return -1;
}
return data.length();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package mytest;
import org.junit.Test;
public class StringUtilTest {
@Test
public void testPrintNumber(){
StringUtil.printNumber("test");
StringUtil.printNumber(null);
}
@Test
public void testGetMaxIndex(){
System.out.println(StringUtil.getMaxIndex("test"));
System.out.println(StringUtil.getMaxIndex(null));
}
}
|
我们发现,在上述案例中,System.out.println(StringUtil.getMaxIndex("test"))
; 返回的结果为 4,实际结果应该为 3,但测试结果中却显示测试用例通过,此时可以使用断言机制。
Assert 断言
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。
断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
Junit 的各种断言介绍如下:
函数 |
介绍 |
assertEquals(a, b) |
测试 a 是否等于 b,a 和 b 必须是原始数据类型 或者是实现了比较方法而且具有 equals 方法 |
assertFalse(a) |
测试 a 是否为 false,a 是一个布尔类型值 |
assertTrue(a) |
测试 a 是否为 true,a 是一个布尔类型值 |
assertNotNull(a) |
测试 a 是否为非空,a 是一个对象或者 NULL |
assertNull(a) |
测试 a 是否为空,a 是一个对象或者 NULL |
assertNotSame(a, b) |
测试 a 和 b 是否没有引用同一个对象 |
assertSame(a, b) |
测试 a 和 b 是否引用自同一个对象 |
assertEquals(a, b, c) |
测试实际结果 c 是否与期望结果 b 一致,若不一致报错 a |
1
2
3
4
5
6
7
8
9
10
|
// 使用方法
Assert.assertEquals(String message, long expected, long actual);
@Test
public void testGetMaxIndex() {
System.out.println(StringUtil.getMaxIndex("test"));
System.out.println(StringUtil.getMaxIndex(null));
Assert.assertEquals
("与正确结果不一致", 3, StringUtil.getMaxIndex("test"));
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package mytest;
public class StringUtil {
public static void main(String[] args) {
}
// 获取字符串长度
public static void printNumber(String name) {
if (name == null) {
System.out.println(0);
return;
}
System.out.println(name + "的长度是:" + name.length());
}
// 获取字符串最大索引
public static int getMaxIndex(String data) {
if (data == null) {
return -1;
}
return data.length() - 1;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package mytest;
import org.junit.Assert;
import org.junit.Test;
public class StringUtilTest {
@Test
public void testPrintNumber() {
StringUtil.printNumber("test");
StringUtil.printNumber("helloWorld");
StringUtil.printNumber("你好");
StringUtil.printNumber("printNumber");
StringUtil.printNumber("length");
StringUtil.printNumber(null);
}
@Test
public void testGetMaxIndex() {
System.out.println(StringUtil.getMaxIndex("test"));
System.out.println(StringUtil.getMaxIndex(null));
System.out.println(StringUtil.getMaxIndex("helloWorld"));
System.out.println(StringUtil.getMaxIndex("ohhhh"));
Assert.assertEquals
("与正确结果不一致", 3, StringUtil.getMaxIndex("test"));
}
}
|
Junit 框架的常见注解
Junit 4 版本
注解 |
说明 |
@Test |
测试类中的方法必须用它修饰才能成为测试方法,才能启动执行 |
@Before |
用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次 |
@After |
用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次 |
@BeforeClass |
用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次 |
@AfterClass |
用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次 |
在测试方法执行前执行的方法,常用于:初始化资源。
在测试方法执行完后再执行的方法,常用于:释放资源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package mytest;
import org.junit.*;
import java.io.IOException;
import java.net.Socket;
public class StringUtilTest {
private Socket socket1;
private static Socket socket2;
@Before
public void before(){
System.out.println("-------- before --------");
socket1 = new Socket();
}
@After
public void after() throws IOException {
System.out.println("--------- after --------");
socket1.close();
}
@BeforeClass
public static void beforeClass(){
System.out.println("----- beforeClass ------");
socket2 = new Socket();
}
@AfterClass
public static void afterClass() throws IOException {
System.out.println("------ afterClass ------");
socket2.close();
}
@Test
public void testPrintNumber() {
StringUtil.printNumber("test");
StringUtil.printNumber("helloWorld");
StringUtil.printNumber("你好");
StringUtil.printNumber("printNumber");
StringUtil.printNumber("length");
StringUtil.printNumber(null);
}
@Test
public void testGetMaxIndex() {
System.out.println(StringUtil.getMaxIndex("test"));
System.out.println(StringUtil.getMaxIndex(null));
System.out.println(StringUtil.getMaxIndex("helloWorld"));
System.out.println(StringUtil.getMaxIndex("ohhhh"));
Assert.assertEquals
("与正确结果不一致", 3, StringUtil.getMaxIndex("test"));
}
}
|
Junit 5 版本
注解 |
说明 |
@Test |
测试类中的方法必须用它修饰才能成为测试方法,才能启动执行 |
@BeforeEach |
用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次 |
@AfterEach |
用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次 |
@BeforeAll |
用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次 |
@AfterAll |
用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次 |
Junit 使用
Calculator 类的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
package mytest;
public class Calculator {
public static int result;
public void calculator(int n) {
result = n;
}
public void add(int n) {
result += n;
}
public void substract(int n) {
// 期望实现 result -= n;
result -= 1;
}
public void multiply(int n) {
// 此方法未实现
}
public void divide(int n) {
result /= n;
}
public void square(int n) {
result *= n;
}
public void squareRoot(int n) {
for (; ; ) {
result = n;
}
}
public void clear() {
result = 0;
}
public int getResult() {
return result;
}
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.calculator(100);
System.out.println(c1.getResult());
System.out.println("===== test =====");
c1.clear();
if (result == 0){
System.out.println("clear方法正确");
}else {
System.out.println("clear方法有bug");
}
c1.add(100);
if (result == 100){
System.out.println("add方法正确");
}else {
System.out.println("add方法有bug");
}
c1.divide(0);
System.out.println(result);
}
}
|
Calculator 类测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package mytest;
import org.junit.Assert;
import org.junit.Test;
public class CalculatorTest {
Calculator c1 = new Calculator();
@Test
public void testAdd() {
c1.calculator(100);
c1.add(100);
}
@Test
public void testDevide(){
c1.calculator(100);
c1.divide(0);
}
@Test
public void testSubstract(){
c1.calculator(100);
c1.substract(50);
Assert.assertEquals("substract方法有bug", 50, c1.getResult());
}
}
|
Junit 练习
三角形类型判断
请为三角形类型判断问题编写相应 java 代码,并利用 junit 完成代码的测试。
编写方法,传入 3 个 int 类型的整数,判断输入的数字能否构成三角形,构成什么三角形?
使用语句覆盖设计测试用例。
求第二天的日期
请为求第二天的日期问题编写相应 java 代码,并利用 junit 完成代码的测试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
switch (month) {
case 1, 3, 5, 7, 8, 10:
if (day == 31) {
return year + "年" + (month + 1) + "月" + 1 + "日";
} else {
return year + "年" + month + "月" + (day + 1) + "日";
}
case 2:
if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)) {
if (day >= 30) {
return year + "年" + month + "月没有" + day + "天";
} else if (day == 29) {
return year + "年" + (month + 1) + "月" + 1 + "日";)
} else {
return year + "年" + month + "月" + (day + 1) + "日";
}
} else {
if (day >= 29) {
return year + "年" + month + "月没有" + day + "天";
} else if (day == 28) {
return year + "年" + (month + 1) + "月" + 1 + "日";
} else {
return year + "年" + month + "月" + (day + 1) + "日";
}
}
case 4, 6, 9, 11:
if (day >= 31) {
return year + "年" + month + "月没有" + day + "天";
} else if (day == 30) {
return year + "年" + (month + 1) + "月" + 1 + "日";
} else {
return year + "年" + month + "月" + (day + 1) + "日";
}
case 12:
if (day == 31) {
return (year + 1) + "年" + 1 + "月" + 1 + "日";
} else {
return year + "年" + month + "月" + (day + 1) + "日";
}
default:
return "输入错误";
}
|
Calendar
类是一个抽象类,它为特定瞬间与 YEAR
、 MONTH
、 DAY_OF_MONTH
、 HOUR
等日历字段之间的转换提供了一些方法,并为操作日历字段(如获得下星期的日期) 提供了一些方法。
创建 Calendar
对象不能使用 new
关键字,因为 Calendar
类是一个抽象类,但是它提供了一个 getInstance()
方法来获得 Calendar
类的对象。 getInstance()
方法返回一个 Calendar
对象,其日历字段已由当前日期和时间初始化。
Calendar
对象实例化演示:
1
|
Calendar c = Calendar.getInstance();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class NextDayCalculator {
public static String getNextDay(int year, int month, int day) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, month - 1, day);
calendar.add(Calendar.DAY_OF_MONTH, 1);
int nextYear = calendar.get(Calendar.YEAR);
int nextMonth = calendar.get(Calendar.MONTH) + 1;
int nextDay = calendar.get(Calendar.DAY_OF_MONTH);
return nextYear + "-" + nextMonth + "-" + nextDay;
}
public static void main(String[] args) {
System.out.println(NextDayCalculator.getNextDay(2024, 2, 28));
}
}
|