Junit4 Test 详解

[TOC]

介绍

Junit4 是在 Junit3 基础上改进的单元测试框架

  • junit4 不需要继承 TestCase
  • 测试方法命名没有特定要求,只要在待测方法前加上@Test即可
  • 通过 @befroe 替代 @setUp 方法,@After 替代 @tearDown 方法;

在一个测试类中,甚至可以使用多个 @Before 来注释多个方法,这些方法都是在每个测试之前运行
@Before 是在每个测试方法运行前均初始化一次,同理 @After 是在每个测试方法运行完毕后,均运行一次
也就是说,经过这两个注释的初始化和注销,可以保证各个测试方法之间的独立性而互不干扰,它的缺点是效率低

  • 新增 @BeforeClass@AfterClass

使用这两个注释的方法,在该测试类中,的测试方法之前、后各运行一次,而不是按照各个方法各运行一次
对于一些资源消耗大的项目,可以使用这两个注释,减少 @Before @After 运行效率低的问题

  • 新增测试类型

异常测试 @Test(expected=*.class) 和超时测试 @Test(timeout=xxx)

  • 新增断言方法

新的assert方法 assertEquals(Object[] expected, Object[] actual),用于比较数组数据

测试用例写作注意事项

  • 测试方法上面必须使用@Test注解进行修饰
  • 测试方法必须使用public void进行修饰,不能带有任何参数
  • 测试单元中的每一个方法必须独立测试,每个测试方法之间不能有依赖
  • 新建一个源代码目录用来存放测试代码
  • 测试类的包应该与被测试类的包保持一致
  • 测试类使用Test做为类名的后缀(非必要)
  • 测试方法使用test作为方法名的前缀(非必要)
  • 尽量使用 assert 关键字来断言错误(非必要)

测试分析原则

  • Failure 一般是单元测试使用的断言方法判断失败引起,说明预期结果和程序运行结果不一致
  • Error 是有代码异常引起的,产生于测试代码本身中的Bug
  • 测试用例不是用来证明你是对的,而是用来证明你没有错

测试流程

  @BeforeClass
  public static void setUpBeforeClass() throws Exception {
  }

  @AfterClass
  public static void setUpAfterClass() throws Exception {
  }



  @Before
  public void before() throws Exception {
  }



  @After
  public void after() throws Exception {
  }
  • @BeforeClass 所修饰的方法在所有方法加载前执行,而且他是静态的在类加载后就会执行该方法,在内存中只有一份实例,适合用来加载配置文件

  • @AfterClass所修饰的方法在所有方法执行完毕之后执行,通常用来进行资源清理,例如关闭数据库连接

  • @Before@After每个测试方法执行前都会执行一次

JUnit注解活用

  • @Test(excepted=XX.class) 在运行时忽略某个异常
  • @Test(timeout=[ms]) 允许程序运行的时间单位毫秒
  • @Ignore所修饰的方法被测试器忽略
  • @RunWith 可以修改测试运行器org.junit.runner.Runne

单元测试的执行顺序

默认情况下,单元测试是无序的,单元测试框架的一个出发点是单元性,即每个单元之间互不影响,因此设置单元测试的执行顺序是没有意义的

不过如果涉及到特殊流程,或者非要让测试的执行顺序在自己的控制之下,也是可以做到的

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TempTest {
}

@FixMethodOrder Junit 4.11 里增的指定测试方法执行顺序的特性,支持顺序有三种,这里是按名字排序

默认(MethodSorters.DEFAULT)

默认顺序由方法名hashcode值来决定,如果hash值大小一致,则按名字的字典顺序确定,由于hashcode的生成和操作系统相关,对于不同操作系统,可能会出现不一样的执行顺序,在某一操作系统上,多次执行的顺序不变

排序方法

    /**
     * DEFAULT sort order
     * @type {Comparator}
     */
    public static Comparator<Method> DEFAULT = new Comparator<Method>() {
        public int compare(Method m1, Method m2) {
            int i1 = m1.getName().hashCode();
            int i2 = m2.getName().hashCode();
            if (i1 != i2) {
                return i1 < i2 ? -1 : 1;
            }
            return NAME_ASCENDING.compare(m1, m2);
        }
    };

按方法名(MethodSorters.NAME_ASCENDING)

按方法名称的进行排序,由于是按字符的字典顺序,所以以这种方式指定执行顺序会始终保持一致

需要对测试方法有一定的命名规则,如 测试方法均以 test_nnn 开头, nnn 代表 000-999 的数字

排序方法

    /**
     * Method name ascending lexicographic sort order, with {@link Method#toString()} as a tiebreaker
     * @type {Comparator}
     */
    public static Comparator<Method> NAME_ASCENDING = new Comparator<Method>() {
        public int compare(Method m1, Method m2) {
            final int comparison = m1.getName().compareTo(m2.getName());
            if (comparison != 0) {
                return comparison;
            }
            return m1.toString().compareTo(m2.toString());
        }
    };

JVM(MethodSorters.JVM)

按JVM返回的方法名的顺序执行,此种方式下测试方法的执行顺序是不可预测的,即每次运行的顺序可能都不一样

注意,JVM 返回的方法名在不同 jdk 上表现可能不一致,不过在 1.7 及以后的 jdk 返回的顺序肯定不一样

Junit4 测试方法顺序原理

实际上 Junit里是通过反射机制得到某个Junit里的所有测试方法,并生成一个方法的数组,然后依次执行数组里的这些测试方法;
而当用annotation指定了执行顺序,Junit在得到测试方法的数组后,会根据指定的顺序对数组里的方法进行排序

    public static Method[] getDeclaredMethods(Class<?> clazz) {
        Comparator<Method> comparator = getSorter(clazz.getAnnotation(FixMethodOrder.class)); //获取测试类指定的执行顺序
        Method[] methods = clazz.getDeclaredMethods();
        if (comparator != null) {
            Arrays.sort(methods, comparator); //根据指定顺序排序
        }
        return methods;
    }

定义的顺序为

package org.junit.runners;

/**
 * Sort the methods into a specified execution order.
 * Defines common {@link MethodSorter} implementations.
 *
 * @since 4.11
 */
public enum MethodSorters {
    /**
     * Sorts the test methods by the method name, in lexicographic order,
     * with {@link Method#toString()} used as a tiebreaker
     */
    NAME_ASCENDING(MethodSorter.NAME_ASCENDING),

    /**
     * Leaves the test methods in the order returned by the JVM.
     * Note that the order from the JVM may vary from run to run
     */
    JVM(null),

    /**
     * Sorts the test methods in a deterministic, but not predictable, order
     */
    DEFAULT(MethodSorter.DEFAULT);
}

当设置为MethodSorters.JVM时,其并没有提供一个Comparator的实现
所以执行方法的顺序实际上就是 clazz.getDeclaredMethods(); 得到的数组里方法的顺序
而由于java里对getDeclaredMethods返回的方法没有指定任何顺序,所以最终导致Junit测试方法的执行顺序也不是确定的

测试套件

测试套件是组织测试类一起运行的测试类

@RunWith(Suite.class)
@Suite.SuiteClasses({UserTest1,UserTest2,UserTest3})
public class SuiteTest{
}

测试套件注意事项

  • 作为测试套件的入口类,类中不能包含任何方法
  • 更改测试运行器Suite.class
  • 将需要运行的测试类放入Suite.SuiteClasses({})的数组中

参数化设置

需要测试的仅仅是测试数据,代码结构是不变的,只需要更改测试入参

@RunWith(Parameterized.class)
public class ParameterTest {

    int expected = 0;
    int input1 = 0;
    int input2 = 0;

    @Parameters
    public static Collection<Object[]> t() {
        return Arrays.asList(new Object[][]{
                {3,1,2},
                {5,2,3}
        });
    }

    public ParameterTest(int expected,int input1,int input2) {
        this.expected = expected;
        this.input1 = input1;
        this.input2 = input2;
    }

    @Test
    public void testAdd() {
        assertEquals(expected, UserDao.add(input1,input2));
    }

}

代码流程为

  • 更改默认的测试运行器为@RunWith(Parameterized.class)
  • 声明变量来存放预期值和测试值例子中为 expected input1 input2
  • 声明一个返回值为Collection的公共静态方法,并用@Parameters修饰
  • 为测试类声明一个带有参数的公共构造函数ParameterTest,并在其中为他声明变量赋值

单元测试统计与评测

单元测试代码覆盖介绍

首先,代码覆盖属于单元测试,集成测试不计算在其中

需要理解被测程序的逻辑,需要考虑到每个函数的输入与输出,逻辑分支代码的执行情况,这个时候我们的测试执行情况就以代码覆盖率来衡量,可以理解为白盒覆盖

这里介绍 java 代码的覆盖分类

  • 行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行
  • 类覆盖率:度量计算class类文件是否被执行
  • 分支覆盖率:度量if和switch语句的分支覆盖情况,计算一个方法里面的总分支数,确定执行和不执行的 分支数量
  • 方法覆盖率:度量被测程序的方法执行情况,是否执行取决于方法中是否有至少一个指令被执行
  • 指令覆盖:计数单元是单个java二进制代码指令,指令覆盖率提供了代码是否被执行的信息,度量完全 独立源码格式
  • 圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目,缺失的复杂度同样表示测试案例没有完全覆盖到这个模块

java 代码覆盖测试方法

代码生成二进制时使用的ASM技术修改字节码方法,可以修改Jar文件、class文件字节码文件,来注入源码,注入的过程做插桩,然后执行测试用例,统计是否调用

目前 java 使用的UAT和自动统计覆盖测试的技术

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,559评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,525评论 18 399
  • JUnit Intro Android基于JUnit Framework来书写测试代码。JUnit是基于Java语...
    chandarlee阅读 2,237评论 0 50
  • 周末,高芬独自一人在家。 昨晚看电视到很晚,后来把手机关了静音,准备睡个懒觉,但今天一早还是醒来了。头疼,明显没睡...
    暂且这样吧阅读 315评论 0 1
  • 几天一直折磨着蹊跷,换了方式,有了实质进展。非常具体,非常真实,物理的世界。 珍惜已有的,也许不经意间灰飞烟灭。渴...
    NemoX阅读 138评论 0 0