前言:

​ 首先在此给大家拜个晚年,大家新年好。这也有近半个月没有进行跟新了,是时候将自己最新的学习成果进行总结分享一下了。废话不多说,进入今天的主题--单元测试。

一:单元测试是什么?

百度百科的解释是这样的:单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。例如,你可能把一个很大的值放入一个有序list 中去,然后确认该值出现在list 的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确实不再包含这些字符了。

简单的说,单元测试就是对你程序中最小的功能模块进行测试,在c语言里可能是一个函数,java中可能是一个方法或者类。

目的就是为了提高代码的质量。

二:为什么要进行单元测试?

为什么要进行单元测试?说白了就是单元测试有什么好处,其实测试的好处无非就是减少bug、提高代码质量、使代码易于维护等。单元测试有什么好处请看一下百度百科中归纳的四条:

1、它是一种验证行为。
程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支援。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。
2、它是一种设计行为。
编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。
3、它是一种编写文档的行为。
单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。

4、它具有回归性。
自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。

三:如何更好的进行单元测试?

在讨论如何更好的进行单元测试之前,先来看看我以前是怎么测试代码的。

以前是这样测试程序的:

    public int add(int x,int y) {
        return x + y;
    }

    public static void main(String args[]) {
        int z = new Junit().add(2, 3);
        System.out.println(z);
    }

如上面所示,在测试我们写好的一个方法时,通常是用一个main方法调用一下我们要测试的方法,然后将结果打印一下。现在看来这种方式已经非常out了,所以出现了很多单元测试的工具,如:JUnit、TestNG等。借助它们可以让我们的单元测试变得非常方便、高效。今天就说说如何利用JUnit进行单元测试。

四:JUnit

简介:

JUnit是一个开放源代码的Java测试框架,用于编写和运行可重复的测试。他是用于单元测试框架体系xUnit的一个实例(用于java语言)。它包括以下特性:

1、用于测试期望结果的断言(Assertion)

2、用于共享共同测试数据的测试工具

3、用于方便的组织和运行测试的测试套件

4、图形和文本的测试运行器

JUnit使用:

定义测试方法

JUnit使用注解标注为测试方法并配置它们,下表中给出了JUnit4非常重要的注解。下面所有的注解都用在方法上。

JUnit 4 描述
import org.junit.* 用于导入下列注解。
@Test 将方法标记为测试方法。
@Before 在每次测试之前执行,一般用于准备测试环境(初始化类等)。
@After 在每次测试之后执行,用于清理测试环境 (例如删除临时数据,还原默认值等)。 它也可以拥有清理内存( It can also save memory by cleaning up expensive memory structures.)。
@BeforeClass 在所有测试之前,执行一次。它一般用于执行time intensive activities,例如连接数据库等。使用该注解标记的方法需要定义为static void。
@AfterClass 在所有的测试执行完成之后执行一次。 它一般用于清理一些 activities, 例如断开数据连接。使用该注解标记的方法需要定义为static void
@Ignore or @Ignore("Why disabled") 标记该注解的测试方法是被禁用的。这对于实际代码做了修改而测试代码没有修改的情况是非常有用的,或者由于这条测试执行时间过长先不将其包含在测试中,最好是提供一下不去测试的原因。
@Test (expected = Exception.class) 如果这个测试方法不抛出赋值的异常(Exception.class)将会失败。
@Test(timeout=100) 如果这个测试方法执行超过100毫秒将会失败。

断言语句(Assert statements)

JUnit提供静态方法,通过Assert类来测试某些条件。这些断言语句通常以assert开头。他们允许你指定错误信息、预期结果和实际结果。断言方法将测试返回的实际值和预期值相比较。如果比较失败就会抛出AssertionException异常。

下表简单的介绍了这些方法。[]中的参数是可选的字符串类型。

语句 描述
fail([message]) 让这个测试方法失败,可能用于检查代码中某个部分是否未执行,或者在执行测试代码之前是否存在失败的测试。这个message参数是可选的。
assertTrue([message,] boolean condition) 验证boolean条件为true。
assertFalse([message,] boolean condition) 验证boolean条件为false。
assertEquals([message,] expected, actual) 验证expected和actual相同。注:对于数组则检查的是引用而不是数组的内容
assertEquals([message,] expected, actual, tolerance) 验证float或者double匹配。 大家知道计算机表示浮点型数据都有一定的偏差,所以哪怕理论上他们是相等的,但是用计算机表示出来则可能不是,所以这里运行传入一个偏差值。如果两个数的差异在这个偏差值之内,则测试通过,否者测试失败。
assertNull([message,] object) 验证object为null。
assertNotNull([message,] object) 验证object不为null。
assertSame([message,] expected, actual) 验证expected和actual是同一个对象。
assertNotSame([message,] expected, actual) 验证expected和actual不是同一个对象。

JUnit的一些注意事项:

  • 测试方法必须使用@Test修饰
  • 测试方法必须使用public void进行修饰,不能带参数
  • 一般使用单元测试会新建一个test目录存放测试代码,在生产部署的时候只需要将test目录下代码删除即可
  • 测试代码的包应该和被测试代码包结构保持一致
  • 测试单元中的每个方法必须可以独立测试,方法间不能有任何依赖
  • 测试类一般使用Test作为类名的后缀
  • 测试方法使一般用test作为方法名的前缀

测试失败说明:

  • Failure:一般是由于测试结果和预期结果不一致引发的,表示测试的这个点发现了问题
  • error:是由代码异常引起的,它可以产生于测试代码本身的错误,也可以是被测试代码中隐藏的bug

总结:

总的来说使用单元测试要比自己手动的测试性能快一些,并且相对来说要简洁,因此确实是一个很重要的的知识,并且我也和实际开发的朋友沟通过,在实际开发中也是经常使用检测自己代码的一个标准。本文主要是将单元测试的一些简单介绍,如果有时间,我会将单元测试的框架JUnit进行具体的总结分享。