转载

单元测试指南

一、必要性

在我们公司中要做单元测试,确实比较难,因为公司缺少这种氛围,有也只是局部的,大多数工程师没有这方面的习惯和素养,很多人都是有一定的抵触的心理,经过我私下的了解大概有以下几种原因吧。

  • 写单元测试太 耗费时间 了,项目要赶进度,编写单元测试会导致不能按时完成开发任务, 导致项目延期
  • 传统xx管理系统 的项目,业务逻辑比较简单,主要就是对业务数据做 增删改查 ,单元测试意义和价值不高;
  • 公司有专门的测试人员,很多问题在集成测试时一定能发现。
  • 以前项目上从没写过单元测试,没有经验,不知道怎么编写单元测试;

这其中对单元测试就有些误解了,单元测试有几个比较常见的典型场景:

  • 开发前写单元测试,通过测试描述需求,即 测试驱动开发 。
  • 在开发过程中及时得到反馈,提前规避隐患和发现问题。
  • 应用于自动化构建或持续集成流程,对每次代码修改做回归测试。
  • 作为重构的基础,验证重构是否可靠。

还有最重要的一点: 编写单元测试的难易程度能够直接反应出代码的设计水平,能写出单元测试和写不出单元测试之间体现了编程能力上的巨大的鸿沟。无论是什么样的程序员,坚持编写一段时间的单元测试之后,都会明显感受到代码设计能力的巨大提升

公司开发人员的代码质量往往不是很高,尤其是对代码的拆分和逻辑的抽象还处于懵懂阶段。要对这类代码写单测,即使是工作了3,4年的高级码农也是一个挑战,对新人来说几乎是不可能完成的任务。这也让很多开发人员有了 写单元测试很难 的感觉。所以, 写单元测试的难易程度跟代码的质量关系最大,并且是决定性的 。项目里无论用了哪个测试框架都不能解决代码本身难以测试的问题。

诚然,写单元测试在开发期间的确是会耗费更多时间的,尤其是要追求很高(超过 80% ,甚至 100% )的代码覆盖率,更是需要耗费大量心血才能达到的。对于一些只需一次交付,很少维护的项目来说,意义和价值确实不是很大。但这本质上是属于为了赚快钱,不负责任的行为了,毕竟谁都无法保障自己写的程序,真的没有丝毫问题。这个问题的出现并不是个人的问题,而是反映了公司项目管理中的问题。当然,个人的原因也存在,就是如何在有限的时间里,提高效率。

目前公司的大多数项目其实都有着至少两年的维护时间的,很多开发人员都不愿意把自己的时间耗在一个代码很烂、没有单元测试保障且经常变更需求的项目里面。总之,包括我本人在内,都是有项目 维护恐惧症 的,更愿意投入到新项目的开发中。但是新项目里面还是没有单元测试的保障,代码质量逐渐低劣,如此就又形成了一个不断的循环之中。无法挣脱这个循环的人员就只能选择离职了,也许不慎又到了新的漩涡里面。

一个 bug 被隐藏的时间越长,修复这个 bug 的代价就越大。

单元测试能帮助我们在早期就规避、发现和修复很多不易察觉的 bug 和漏洞,而且更能保障后期的需求变动和代码重构时所带来的隐患,减少测试成本和维护成本。所以,在新项目中逐步推广和编写单元测试是有必要的,这将大大提高项目中代码的质量和可靠性,有些老项目中就算了吧,往往维护人员的负面情绪可能会更多,一些新的功能特性倒是可以试试。虽然写好单元测试很难,但 写单元测试的难度其实是小于决定写单元测试的勇气的

二、基本概念

单元测试:单元测试又称模块测试,属于白盒测试,是最小单位的测试。模块分为程序模块和功能模块。功能模块指实现了一个完整功能的模块(单元),一个完整的程序单元具备输入、加工和输出三个环节。而且每个程序单元都应该有正规的规格说明,使之对其输入、加工和输出的关系做出名明确的描述。

驱动测试:驱动被测试模块正常运行起来的实体。通俗的说法就是你负责测试模块/方法是中间的,没有 main() 方法入口,怎么编译,怎么启动呢?就需要写一个带 main() 的方法来调用你的模块/方法,这个就是驱动测试。

测试桩:代替被测模块调用的子模块的实体,该实体一般为桩函数(stub)。通俗的说法就是你负责测试的模块/方法所调用的模块/方法,所以你需要模仿他们做一个返回值(假的,但符合设计)。

测试覆盖:评测测试过程中已经执行的代码的多少。

测试覆盖率:代码的覆盖程度,一种度量方式。针对代码的测试覆盖率有很多种度量方式,常见的有以下几种:

  • 语句覆盖
  • 判定覆盖
  • 路径覆盖

测试覆盖率数据到底有多大意义。主要有以下几个观点:

  • 路径覆盖率 > 判定覆盖 > 语句覆盖
  • 覆盖率数据只能代表你测试过哪些代码,不能代表你是否测试好这些代码。
  • 不要过于相信覆盖率数据,100%的测试覆盖率并不能保证bug的不出现。
  • 代码覆盖率只是一个最基本的前提,一定要保证,但不是意味着达到指标就代表测试的完成
  • 测试人员不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有。

三、单元测试工具

在Java中有非常多的单元测试的工具或框架可供选择,我这里只选择一些常用的、主流的单元测试框架或者工具来作介绍和使用。

  • JUnit :Java中最有名、使用最广泛的单元测试框架
  • Mockito :模拟框架,可以让你用干净而简单的API编写测试
  • Spring Test : 使用 Spring Test 来对Spring相关的项目做单元测试,其中会结合或者集成其他测试框架和工具
  • spring-boot-starter-test : SpringBoot项目中的单元测试
  • JaCoCo : 使用离线和运行时字节码工具来收集代码覆盖率指标的框架。

1. JUnit4

JUnit 是使用 Java 语言编写的用于编写和运行可重复的自动化测试的开源测试框架。除了 Junit 之外, TestNg 也是Java中非常受欢迎的单元测试框架。两种框架在功能上看起来非常相似,这里有一篇关于 JUnit 4 与 TestNG 的对比 ,总体来说,TestNG 比 Junit4 功能更强大一些,但是相比 Junit5 而言,TestNG 又落后了一代。开源的轮子滚滚向前,都是一代新的轮子超越一代老的轮子。所以,我们这里就只选择 Junit 来作单元测试框架的介绍了吧。

单元测试指南

目前最新版本是 JUnit5.2.0,相比 JUnit4 而言有很大的改变,这里主要讲解 JUnit4 的使用(目前的新老项目中应该使用的更多),并对 JUnit5 做简要介绍。学习了 Junit4 的主要使用方式之后,大家再去看 JUnit5 用户指南 在将来逐渐使用起来更好些。

(1). 简单示例

import static org.junit.Assert.*;

import org.junit.Test;

public class CalculateTest {

    @Test
    public void testSum() {
        Calculate calculation = new Calculate();
        int sum = calculation.sum(2, 5);
        int testSum = 7;

        System.out.println("@Test sum(): " + sum + " = " + testSum);
        assertEquals(sum, testSum);
    }

}

(2). 注解

  • @Test : 测试方法,在这里还可以测试 原文  http://blinkfox.com/dan-yuan-ce-shi-zhi-nan/
正文到此结束
Loading...