http://blog.csdn.net/mao520741111/article/details/51462215

原文地址 http://www.open-open.com/lib/view/open1462177214142.html

JUnit单元测试框架的使用

我们写单元测试,一般都会用到一个或多个单元测试框架,在这里,我们介绍一下JUnit4这个测试框架。这是Java界用的最广泛,也是最基础的一个框架,其他的很多框架,包括我们后面会看到的Robolectric,都是基于或兼容JUnit4的。然而首先要解决的问题是。。。

为什么要使用单元测试框架呢

或者换句话说,单元测试框架能够为我们做什么呢?从最基本的开始说起,假如我们有这样一个类:

public class Calculator {
public int add(int one, int another) {
// 为了简单起见,暂不考虑溢出等情况。
return one + another;
} public int multiply(int one, int another) {
// 为了简单起见,暂不考虑溢出等情况。
return one * another;
}
}

如果不用单元测试框架的话,我们要怎么写测试代码呢?我们恐怕得写出下面这样的代码:

public class CalculatorTest {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int sum = calculator.add(1, 2);
if(sum == 3) {
System.out.println("add() works!")
} else {
System.out.println("add() does not works!")
} int product = calculator.multiply(2, 4);
if (product == 8) {
System.out.println("multiply() works!")
} else {
System.out.println("multiply() does not works!")
}
}
}

然后我们再通过某种方式,比如命令行或IDE,运行这个 CalculatorTest 的 main 方法,在看着terminal的输出,才知道测试是通过还是失败。想想一下,如果我们有很多的类,每个类都有很多方法,那么就要写一堆这样的代码,每个类对于一个含有 main 方法的test类,同时 main 方法里面会有一堆代码。这样既写起来痛苦,跑起来更痛苦,比如说,你怎么样一次性跑所有的测试类呢?所以,一个测试框架为我们做的最基本的事情,就是允许我们按照某种更简单的方式写测试代码,把每一个测试单元写在一个测试方法里面,然后它会自动找出所有的测试方法,并且根据你的需要,运行所有的测试方法,或者是运行单个测试方法,或者是运行部分测试方法等等。

对于上面的 Calculator 例子,如果使用Junit的话,我们可以按照如下的方式写测试代码:

public class CalculatorTest {

    @Test
public void testAdd() throws Exception {
Calculator calculator = new Calculator();
int sum = calculator.add(1, 2);
Assert.assertEquals(3, sum);
} @Test
public void testMultiply() throws Exception {
Calculator calculator = new Calculator();
int product = calculator.multiply(2, 4);
Assert.assertEquals(8, product);
} }

每一个被测试的方法( add(), multiply() ),写一个对应的测试方法( testAdd(), testMultiply() )。那JUnit怎么知道那些是测试方法,哪些不是呢?这个是通过前面的 @Test 注解来标志的,只要有这个注解,JUnit4就会当做是一个测试方法,方法名其实是可以随意起的。当然,名字还是应该起的更有可读性一点,让人一看就知道,这个测试方法是测试了被测的类的那个方法,或者是测试了那个功能点等等。

除了帮我们找出所有的测试方法,并且方便运行意外,单元测试框架还帮我们做了其他事情。在 这个系列的第一篇文章 中我们提到,一个测试方法主要包括三个部分:

  1. setup
  2. 执行操作
  3. 验证结果

而一个单元测试框架,可以让我们更方便的写上面的每一步的代码,尤其是第一步和第三部。比如说,在上面的 CalculatorTest 中, testAdd() 和 testMultiply() 都有相同的setup: Calculator calculator = new Calculator(); ,如果 Calculator 还有其他的方法的话,这行代码就得重复更多次,这种duplication是没必要的。绝大多数单元测试框架考虑到了这一点,它们知道一个测试类的很多测试方法可能需要相同的setup,所以为我们提供了便捷方法。对于JUnit4,是通过 @Before 来实现的:

public class CalculatorTest {
Calculator mCalculator; @Before
public void setup() {
mCalculator = new Calculator();
} @Test
public void testAdd() throws Exception {
int sum = mCalculator.add(1, 2);
assertEquals(3, sum); //为了简洁,往往会static import Assert里面的所有方法。
} @Test
public void testMultiply() throws Exception {
int product = mCalculator.multiply(2, 4);
assertEquals(8, product);
} }

如果一个方法被 @Before 修饰过了,那么在每个测试方法调用之前,这个方法都会得到调用。所以上面的例子中, testAdd() 被运行之前, setup() 会被调用一次,把 mCalculator 实例化,接着运行 testAdd() ; testMultiply() 被运行之前, setup() 又会被调用一次,把 mCalculator 再次实例化,接着运行 testMultiply() 。如果还有其他的测试方法,则以此类推。

对应于 @Before 的,有一个 @After ,作用估计你也猜得到,那就是每个测试方法运行结束之后,会得到运行的方法。比如一个测试文件操作的类,那么在它的测试类中,可能 @Before 里面需要去打开一个文件,而每个测试方法运行结束之后,都需要去close这个文件。这个时候就可以把文件close的操作放在 @After 里面,让它自动去执行。

类似的,还有 @BeforeClass 和 @AfterClass 。 @BeforeClass 的作用是,在跑一个测试类的所有测试方法之前,会执行一次被 @BeforeClass 修饰的方法,执行完所有测试方法之后,会执行一遍被 @AfterClass 修饰的方法。这两个方法可以用来setup和release一些公共的资源,需要注意的是,被这两个annotation修饰的方法必须是静态的。

前面讲的是单元测试框架对于一个测试方法的第一步“setup”,为我们做的事情。而对于第三部“验证结果”,则一般是通过一些assert方法来完成的。JUnit为我们提供的assert方法,多数都在 Assert 这个类里面。最常用的那些如下:

assertEquals(expected, actual)
验证expected的值跟actual是一样的,如果是一样的话,测试通过,不然的话,测试失败。如果传入的是object,那么这里的对比用的是equals()

assertEquals(expected, actual, tolerance)
这里传入的expected和actual是float或double类型的,大家知道计算机表示浮点型数据都有一定的偏差,所以哪怕理论上他们是相等的,但是用计算机表示出来则可能不是,所以这里运行传入一个偏差值。如果两个数的差异在这个偏差值之内,则测试通过,否者测试失败。

assertTrue(boolean condition)
验证contidion的值是true

assertFalse(boolean condition)
验证contidion的值是false

assertNull(Object obj)
验证obj的值是null

assertNotNull(Object obj)
验证obj的值不是null

assertSame(expected, actual)
验证expected和actual是同一个对象,即指向同一个对象

assertNotSame(expected, actual)
验证expected和actual不是同一个对象,即指向不同的对象

fail()
让测试方法失败

注意:上面的每一个方法,都有一个重载的方法,可以在前面加一个String类型的参数,表示如果验证失败的话,将用这个字符串作为失败的结果报告。

比如:

assertEquals("Current user Id should be 1", 1, currentUser.id());

当 currentUser.id() 的值不是1的时候,在结果报道里面将显示"Current user Id should be 1",这样可以让测试结果更具有可读性,更清楚错误的原因是什么。

比较有意思的是最后一个方法, fail() ,你或许会好奇,这个有什么用呢?其实这个在很多情况下还是有用的,比如最明显的一个作用就是,你可以验证你的测试代码真的是跑了的。

此外,它还有另外一个重要作用,那就是验证某个被测试的方法会正确的抛出异常,不过这点可以通过下面讲到的方法,更方便的做到,所以就不讲了。

这部分相对来说还是很好理解的,不做过多解释。

JUnit的其他功能

Ignore一些测试方法

很多时候,因为某些原因(比如正式代码还没有实现等),我们可能想让JUnit忽略某些方法,让它在跑所有测试方法的时候不要跑这个测试方法。要达到这个目的也很简单,只需要在要被忽略的测试方法前面加上 @Ignore 就可以了,如下:

public class CalculatorTest {
Calculator mCalculator; @Before
public void setup() {
mCalculator = new Calculator();
} // Omit testAdd() and testMultiply() for brevity @Test
@Ignore("not implemented yet")
public void testFactorial() {
}
}

验证方法会抛出某些异常

有的时候,抛出异常是一个方法正确工作的一部分。比如一个除法函数,当除数是0的时候,它应该抛出异常,告诉外界,传入的被除数是0,示例代码如下:

public class Calculator {

    // Omit testAdd() and testMultiply() for brevity

    public double divide(double divident, double dividor) {
if (dividor == 0) throw new IllegalArgumentException("Dividor cannot be 0"); return divident / dividor;
}}

那么如何测试当传入的除数是0的时候,这个方法应该抛出 IllegalArgumentException 异常呢?

在Junit中,可以通过给 @Test annotation传入一个expected参数来达到这个目的,如下:

public class CalculatorTest {
Calculator mCalculator; @Before
public void setup() {
mCalculator = new Calculator();
} // Omit testAdd() and testMultiply() for brevity @Test(expected = IllegalArgumentException.class)
public void test() {
mCalculator.divide(4, 0);
} }

@Test(expected = IllegalArgumentException.class) 表示验证这个测试方法将抛出 IllegalArgumentException 异常,如果没有抛出的话,则测试失败。

在Android项目里面使用JUnit

Android项目里面使用JUnit是很简单的,你只需要将JUnit这个library加到你的dependencies里面。

testCompile 'junit:junit:4.12'

如果你通过AndroidStudio创建一个项目,这个dependency默认是加上了的,所以你甚至这步都可以省略。

此外,你需要把测试代码放到src/test/java 目录下面。

接下来关于怎么样运行测试代码,怎么样看结果,请参考 这个系列的第一篇文章 的相关部分,因为图比较多,这边就不重复了。

这里让大家看一下运行的结果是什么样子的,其中有一个失败的测试用例是故意的。如果你直接在AndroidStudio里面跑上面的测试类CalculatorTest的所有测试方法的话,会看到如下的结果:

左边可以看到所有的测试方法,以及每个方法跑出来的结果,绿色表示测试通过的测试方法,黄色的感叹号或红色的表示测试失败的。第三个那个有条纹的球球表示被忽略的测试方法。如果是通过terminal跑的话,则会看到如下的测试结果:

这篇文章的相关代码可以在 github的这个project 看到。

小结

这篇文字大概简单介绍了JUnit的使用,相对来说是比较简单,也是比较容易理解的,希望能帮助到大家。其中Assert部分,可以帮我们验证一个方法的返回结果。然而,这些只能帮我们测试有返回值的那些方法。在第一篇文章里面我们讲了,一个类的方法分两种,一是有返回值的方法,这些可以通过我们今天讲的JUnit来做测试。而另外一种没有返回值的方法,即void方法,则要通过另外一个框架,Mockito,来验证它的正确性。至于怎么样验证void方法的正确性,以及Mockito的使用,请关注下一篇文章。

JUnit单元测试框架的使用的更多相关文章

  1. Spring集成JUnit单元测试框架

    一.JUnit介绍 JUnit是Java中最有名的单元测试框架,用于编写和运行可重复的测试,多数Java的开发环境都已经集成了JUnit作为单元测试的工具.好的单元测试能极大的提高开发效率和代码质量. ...

  2. Java - Junit单元测试框架

    简介 Junit : http://junit.org/ JUnit是一个开放源代码的Java语言单元测试框架,用于编写和运行可重复的测试. 多数Java的开发环境都已经集成了JUnit作为单元测试的 ...

  3. Java反射学习总结终(使用反射和注解模拟JUnit单元测试框架)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 本文是Java反射学习总结系列的最后一篇了,这里贴出之前文章的链接,有兴趣的可以打开看看. ...

  4. JAVA自动化之Junit单元测试框架详解

    一.JUnit概述&配置 1.Junit是什么? Junit是一个Java 编程语言的开源测试框架,用于编写和运行测试.官网 地址:https://junit.org/junit4/ 2.Ma ...

  5. junit单元测试框架

    一般我们写代码总想对方法测试一下结果,就存在这些问题: 1.如果方法需要测试,都需要在main方法上调用 2.目前的结果都需要我们人工对比 所以就需要用到 junit 进行测试: 1·下载 junit ...

  6. Selenium之(二)Junit单元测试框架

    书目-selenium 实战宝典 章节:第七章 p63-73 1.被测程序 2.测试代码 3.多个测试类整合到一起 4.运行查看结果

  7. Java 工具 JUnit单元测试

    Java 工具 JUnit单元测试 @author ixenos 1.1.   JUnit单元测试框架的基本使用 一.搭建环境: 导入junit.jar包(junit4) 二.写测试类: 0,一般一个 ...

  8. Java Junit测试框架

    Java    Junit测试框架 1.相关概念 Ø JUnit:是一个开发源代码的Java测试框架,用于编写和运行可重复的测试.它是用于单元测试框架体系xUnit的一个实例(用于java语言).主要 ...

  9. Junit 单元测试、BeanUtils、Properties类

    一. Junit单元测试 1.1. Junit单元测试框架的基本使用 一.搭建环境: 导入junit.jar包(junit4) 二.写测试类: 0,一般一个类对应一个测试类. 1,测试类与被测试类最好 ...

随机推荐

  1. 上传图片代码(chuantouxiang.php+touxiangchuli.php)

    <body> <form action="touxiangchuli.php" method="post" enctype="mul ...

  2. NetAnalyzer笔记 之 一. 开篇语

    [创建时间:2015-08-26 22:00:12] NetAnalyzer下载地址 第一次写技术相关的博客,不足之处还请担待并告知. 在开始之前,先简单介绍一下NetAnalyzer, NetAna ...

  3. 关于ionic的一些坑(3)

    (1)对于页面中的input之类的输入框,取值的时候一般采用的是$scope.model=””的方式来取得input输入框的值,然后进行操作,但实际上在ionic里面是取不到的,取值之前必须先把inp ...

  4. c++11 NULL、0、nullptr

      C的NULL 在C语言中,我们使用NULL表示空指针,也就是我们可以写如下代码: int *i = NULL;foo_t *f = NULL; 实际上在C语言中,NULL通常被定义为如下: #de ...

  5. HDU 多校联合练习赛2 Warm up 2 二分图匹配

    Warm up 2 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total ...

  6. Android 编程下模拟 HOME 键效果

    Android 应用开发中,有一种场景,就是我们不希望用户按 Back 键直接退出 Activity,而是希望应用隐藏到后台,类似于按 Home 键的效果.参考如下代码即可实现这样的效果: publi ...

  7. Windows Server 2008搭建域控制器《转载51CTO.com》

    Windows Server 2008搭建域控制器 引入 在小型网络中,管理员通常独立管理每一台计算机,如最为常用的用户管理.但当网络规模扩大到一定程度后,如超过 10 台计算机,而每台计算机上有 1 ...

  8. compass模块----Utilities

    引入Utilities: @import "compass/utilities"; 分别引入: @import "compass/utilities/color" ...

  9. 技术路线 vs 技术管理路线

    最近因为要给刚毕业的学生做一次演讲,所以就职业发展这类话题先以写博客的形式做一些思考,希望届时能给同学们带来质量更高的内容.我在<驾驭你的"职场布朗运动">一文中谈了2 ...

  10. javascript 生成UUID

    代码一: /*! Math.uuid.js (v1.4) http://www.broofa.com mailto:robert@broofa.com Copyright (c) 2010 Rober ...