Featured image of post 第七章 单元测试

第七章 单元测试

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 类是一个抽象类,它为特定瞬间与 YEARMONTHDAY_OF_MONTHHOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段(如获得下星期的日期) 提供了一些方法。 ​ 创建 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));
    }
}
Blog for Sandy Memories