IntelliJ IDEA单元测试入门

Stella981
• 阅读 710

单元测试初体验

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

1、创建一个类com.hera.util.Math,然后输入一个求阶乘的方法:

IntelliJ IDEA单元测试入门

2、创建一个队Math方法的单元测试:

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

IntelliJ IDEA单元测试入门                  
           创建一个测试类:

            IntelliJ IDEA提供了一个快捷操作Cmd + Shift + T作为类和测试之间的导航。同时允许用户在那里创建一个测试类。IntelliJ IDEA提供了一个快捷操作Cmd + Shift + T作为类和测试之间的导航。同时允许用户在那里创建一个测试类。

          为测试类MathTest添加测试方法:

IntelliJ IDEA单元测试入门

IntelliJ IDEA单元测试入门

                运行: run MathTest 。右下方一条绿色条说明测试通过,如果把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如下图还有一堆断言。

                   IntelliJ IDEA单元测试入门

执行顺序

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("构造方法");
    }

    @BeforeClass
    public static void setUpBeforeClass() {
        System.out.println("BeforeClass");
    }

    @AfterClass
    public static void tearDownAfterClass() {
        System.out.println("AfterClass");
    }

    @Before
    public void setUp() {
        System.out.println("Before");
    }

    @After
    public void tearDown() {
        System.out.println("After");
    }

    @Test
    public void test1() {
        System.out.println("test1");
    }

    @Test
    public void test2() {
        System.out.println("test2");
    }

    @Ignore
    public void test3() {
        System.out.println("test3");
    }

}

运行结果如下:

BeforeClass 构造方法 Before test1 After 构造方法 Before test2 After AfterClass

        解释一下:@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);

执行结果:

BeforeClass 构造方法 Before test1的i为1 After 构造方法 Before test2的i为1 After AfterClass

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

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

@Test的属性

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

  • excepted
  • timeout
    excepted属性是用来测试异常的,我们回到Math类,拿其中的求阶乘方法factorial()来说。如果传进来一个负数我们是希望抛出异常的,那要测试会不会抛异常怎么办呢?
    我在测试类MathTest添加一个测试方法:                 IntelliJ IDEA单元测试入门

          这个方法就是(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]; //数组长度为50000
    int 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,绿色条出现了,测试通过妥妥的。

编辑测试设置

       我们可以通过Run → Edit Configurations或工具栏上的标签来调整我们的测试运行配置。

        IntelliJ IDEA单元测试入门

       在Configuration选项卡,用户可以选择需要运行的测试。例如,您可以从一个类、程序包、测试套件或甚至模式中运行所有的测试。这里的Fork模式让用户在一个单独的进程运行每个测试。

        在代码覆盖标签你可以调整覆盖率设置。目前IntelliJ IDEA支持两种测量覆盖率引擎。默认情况下它使用自己的引擎,当然用户也可以选择JaCoCo引擎。用户也可以在这里选择覆盖率模式。Tracing{span{ mode模式会增加消耗,但测量会更精确。

             IntelliJ IDEA单元测试入门     

运行覆盖

         收集覆盖率,用户需要通过Run → Run 'MyClassTest' with Coverage或工具栏上的选项运行特定模式的测试。

        当覆盖模式运行至少一个测试之后,IDE将会在Project工具窗口显示每个程序包、类的覆盖率数据,同时在Coverage工具窗和编辑器中也会显示。

         IntelliJ IDEA单元测试入门

编辑器中的覆盖率

如果用户添加另一个方法到MyClass,并运行覆盖率测MyClass,就会发现,没有被测试覆盖到的代码都将高亮显示为红色。覆盖的代码颜色则是绿色。如果一些代码是只覆盖部分,那没将显示为黄色。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这