Java中的异常测试框架JUnit使用上手指南

JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即白盒测试。该项目主页:http://www.junit.org/

使用JUnit时,主要都是通过继承TestCase类别来撰写测试用例,使用testXXX()名称来撰写单元测试。

用JUnit写测试真正所需要的就三件事:

1.  一个import语句引入所有junit.framework.*下的类。

2.  一个extends语句让你的类从TestCase继承。

3.  一个调用super(string)的构造函数。

功能类MathTool

package com.zj.c01;
public class MathTool {
 public static int gcd(int num1, int num2) {
  int r = 0;
  while (num2 != 0) {
   r = num1 % num2;
   num1 = num2;
   num2 = r;
  }
  return num1;
 }
}

测试类MathToolTest

package com.zj.c01;
import junit.framework.TestCase; 

public class MathToolTest extends TestCase {
 public MathToolTest(String name) {
  super(name);
 } 

 public void testGcd() {
  assertEquals(5, MathTool.gcd(10, 5));
 }
}

 我们在用 JUnit 测试方法异常的时候,最容易想到的办法就是用 try…catch 去捕获异常,需要断言以下几个条件:
  1. 确实抛出的异常
  2. 抛出异常的 Class 类型
  3. 抛出异常的具体类型,一般检查异常的 message 属性中包含的字符串的断定
  所以常用的代码你可能会这么写:

    @Test
    public void testBizException()
{
	    try{
		    Password.validate( "123" );
		    fail( "No exception thrown." );
		   
	}catch ( Exception ex ) {
		    assertTrue( ex instanceof BizException );
		    assertTrue( ex.getMessage().contains( "error" ) );
		   
	}
	   
}

  这里被测试的方法是 Password.validate() 方法是否抛出了相应的异常,注意这里别漏 try 中的
  fail(“No Exception thrown.”)
  代码行,不然如果被测试的方法如果没有抛出异常的话,这个用例是通过的,而你预期的是要抛出异常的。
  在 JUnit 4 中,大可不必如此这般的去测试方法异常。虽然这样也能测定出是否执行出预期的异常来,但它仍有弊端,接下来会一对比就知道了,try…catch 的方法,JUnit 无法为你提示出详细的断言失败原因。
  那么来看看自从 JUnit 4 后可以怎么去测试异常呢?用 @Test(execpted=Exception.class) 注解就行,参考如下代码:

    @Test( expected = BizException.class )
    public void testBizException()
{
     Password.validate( null );
    
}

  如果被测试的方法有抛出 BizException类型便是断言成功,对了 @Test(expected = BizException.class) 只能判断出异常的类型,并无相应的注解能断言出异常的更具体的信息,即无法判定抛出异常的 message 属性。
  那么,有时候我们会在一个方法中多次抛出一种类型的异常,但原因不同,即异常的 message 信息不同,比如出现 BizException 时会有以下两种异常:

  new BizException(“Password must contains at least 6 letters.”)
  new BizException(“Password length less than 15 letters”)

  这就要有办法去断言异常的 message 了,针对于此,自 JUnit 4.7 之后又给了我们更完美的选择,就是下面的代码:

    @Rule
    public ExpectedException expectedEx = ExpectedException.none();
    @Test
    public void testBizException() throws InvalidPasswordException
{
	    expectedEx.expect( BizException.class );
	    expectedEx.expectMessage( "required" );
	    Password.validate( "" );
	   
}

  上面代码需重点关注几个:
  1. @Rule 注解的 ExpectedException 变量声明,它必须为 public
  2. @Test 处,不能写成 @Test(expected=BizException.class),否则不能正确测试,也就是
  @Test(expected=BizException.class) 和测试方法中的 expectedEx.expectXxx() 方法是不能同时并存的
  3. expectedEx.expectMessage() 中的参数是 Matcher 或 subString,就是说可用正则表达式判定,或判断是否包含某个子字符串
  4. 再就是有一点很重,把被测试方法写在 expectedEx.expectXxx() 方法后面,不然也不能正确测试的异常
  5. 最后一个是,只要测试方法直接抛出被测试方法的异常即可,并不影响你所关心的异常
  前面说到用 try…catch 的办法也能正确测试到异常,@Test(expected=…) 或 @Rule 与 try…catch 的方法对比有什么好处呢,显然用 JUnit 4 推荐的方法简洁明了。再来看测试失败时 JUnit 会为你提示什么呢?
  try…catch 测试异常失败时,得到的提示:
  无异常时:

  java.lang.AssertionError: No exception thrown.
  at org.junit.Assert.fail(Assert.java:91)
  at cc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:20)

  异常类型不对或异常的 message 不对时:

  java.lang.AssertionError:
  at org.junit.Assert.fail(Assert.java:91)
  at org.junit.Assert.assertTrue(Assert.java:43)
  at org.junit.Assert.assertTrue(Assert.java:54)
  at cc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:22)

  上面能提供给我们的定位错误的帮助不是特别大
  再看 @Test(expected=BizException.class) 时测试失败时的提示:

  java.lang.AssertionError: Expected exception: cc.test.BizException
  at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:32)
  at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110)

  用 @Rules ExpectedException方式来测试异常,失败时的提示:

  java.lang.AssertionError:
  Expected: (exception with message a string containing “YES. required” and an instance of java.lang.NullPointerException)
  got:
  at org.junit.Assert.assertThat(Assert.java:778)
  at org.junit.Assert.assertThat(Assert.java:736)
  at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:114)

  特别是 @Rules ExpectedException 方法时为何测试失败提示的清清楚楚。期望什么异常,异常 message 中含何字符串,实际上确得到什么类型的异常,异常中 message 是什么。有了这,你一看到就知道怎么去修补你的程序。

(0)

相关推荐

  • java编程之单元测试(Junit)实例分析(附实例源码)

    本文实例讲述了java编程之单元测试.分享给大家供大家参考,具体如下: 完整实例代码代码点击此处本站下载. 在有些时候,我们需要对我们自己编写的代码进行单元测试(好处是,减少后期维护的精力和费用),这是一些最基本的模块测试.当然,在进行单元测试的同时也必然得清楚我们测试的代码的内部逻辑实现,这样在测试的时候才能清楚地将我们希望代码逻辑实现得到的结果和测试实际得到的结果进行验证对比. 废话少说,上代码: 首先创建一个java工程,在工程中创建一个被单元测试的Student数据类,如下: packa

  • 详解Java单元测试之JUnit篇

    单元测试是编写测试代码,应该准确.快速地保证程序基本模块的正确性. JUnit是Java单元测试框架,已经在Eclipse中默认安装. JUnit4 JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before 在测试方法运行之前运行 @Test 测试方法 @After 在测试方法运行之后允许 @AfterClass 全局只会执行一次,而且是最后一个运行 @Ignore 忽略此方法 下面基于Eclipse介绍JUn

  • 浅谈junit4单元测试高级用法

    Junit单元测试框架是Java程序开发必备的测试利器,现在最常用的就是Junit4了,在Junit4中所有的测试用例都使用了注解的形式,这比Junit3更加灵活与方便.之前在公司的关于单元测试的培训课程中,讲师仅仅讲述了Junit4的基本的与生命周期相关的注解的使用,主要包括@BeforeClass.@Before.@Test.@After.@AfterClass这些注解,这些在应付普通简单的单元测试已经足够,然而有很多更加复杂且也会经常遇到的测试需求依靠这些生命周期注解并不能完成!因此这篇分

  • 详解Java单元测试Junit框架实例

    问题: 1.目前测试存在的问题 2.Junit注意的细节 3.Junit使用规范 4.断言 5.案例 junit(单元测试框架) 1.目前存在的问题 1.目前的测试方法如果需要测试,都需要在main方法上调用 2.目前的结果都需要我们人工比对 2.Junit 注意的细节 1.如果使用junit测试一个方法的时候,在junit窗口上显示绿色那么表示测试正确,如果显示了红色,则代表该方法测试出现了异常不通过 2.如果点击方法名.类名.包名.工程名运行junit分别测试的是对于的方法,类.包中的所有类

  • Java中的异常测试框架JUnit使用上手指南

    JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework).Junit测试是程序员测试,即白盒测试.该项目主页:http://www.junit.org/ 使用JUnit时,主要都是通过继承TestCase类别来撰写测试用例,使用testXXX()名称来撰写单元测试. 用JUnit写测试真正所需要的就三件事: 1.  一个import语句引入所有junit.framework.*下的类. 2.  一个exte

  • 测试框架JUnit VS TestNG对比分析

    目录 引言 单元测试 TestNG 和 JUnit 的区别 测试套件 注释 用例管理 分组测试 忽略测试 参数化 依赖测试 异常测试 超时测试 结论 引言 软件开发经历了许多阶段,如需求收集和分析.设计.软件开发.测试和发布.测试是 SDLC 不可或缺的一部分,单元测试是一种可靠的测试类型.像 JUnit 和 TestNG 这样优秀的单元测试框架已经成为主流选择,但是关于 TestNG 与 JUnit 的差异的争论一直存在. 单元测试 测试不是单一的活动,而是涵盖各种测试场景.它以不同的方式分类

  • 浅析java中常用的定时任务框架-单体

    目录 一.阅读收获 二.本章源码下载 三.Timer+TimerTask 四.ScheduledExecutorService 五.Spring Task 5.1 单线程串行执行-@Scheduled 5.2 多线程并发运行-@Scheduled+配置定时器的程池(推荐) 5.3 多线程并发执行-@Scheduled+@Async+配置异步线程池 5.4 @Scheduled参数解析 六.Quartz 6.1. 创建任务类 6.2. 配置任务描述和触发器 一.阅读收获 1. 了解常用的单体应用定

  • 盘点MQ中的异常测试

    目录 前言 一.RocketMQ 消息模式 集群消费模式 广播消费模式 二.push 和 pull 优缺点 Pull方式 Push方式 三.刷盘策略 同步刷盘 异步刷盘 四.MQ 异常测试 MQ消息体 消息重复发送 消息到达顺序不一致 消息发送失败重试 接线上生产者 消息丢失 消息争用 MQ比落库快 前言 上一篇小结了一下关于redis的异常测试,今天再来盘一盘 MQ 相关的. MQ 跟 redis 一样,也是现在系统服务中不可或缺的重要中间件,通常用来流量削峰.应用解耦.异步处理等. 之前有过

  • 详解Java中多线程异常捕获Runnable的实现

    详解Java中多线程异常捕获Runnable的实现 1.背景: Java 多线程异常不向主线程抛,自己处理,外部捕获不了异常.所以要实现主线程对子线程异常的捕获. 2.工具: 实现Runnable接口的LayerInitTask类,ThreadException类,线程安全的Vector 3.思路: 向LayerInitTask中传入Vector,记录异常情况,外部遍历,判断,抛出异常. 4.代码: package step5.exception; import java.util.Vector

  • Java中内存异常StackOverflowError与OutOfMemoryError详解

     Java中内存异常StackOverflowError与OutOfMemoryError详解 使用Java开发,经常回遇到内存异常的情况,而StackOverflowError和OutOfMemoryError便是最常遇见的错误. 首先,看看这两种错误的解释: 如果当前线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常. 这里把异常分为两种情况,但是存在一些相互重

  • 深入探讨JAVA中的异常与错误处理

    异常与错误: 异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员)已经犯了个错误,现在有一个机会来修改它.Java中使用异常类来表示异常,不同的异常类代表了不同的异常.但是在Java中所有的异常都有一个基类,叫做Exception. 错误: 它指的是一个合理的应用程序不能截获的严重的问题.大多数都是反常的情况.错误是VM的一个故障(虽然它可以是任何系统级的服务).

  • 全面了解Java中对于异常的捕捉方法

    1. try-catch语句 在Java中,异常通过try-catch语句捕获.其一般语法形式为: try { // 可能会发生异常的程序代码 } catch (Type1 id1){ // 捕获并处置try抛出的异常类型Type1 } catch (Type2 id2){ //捕获并处置try抛出的异常类型Type2 } 关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域.Java方法在运行过程中出现异常,则创建异常对象.将异常抛出监控区域之 外,由Java运行时系统试图寻

  • Java中的异常和处理机制实例详解

    本文实例讲述了Java中的异常和处理机制.分享给大家供大家参考,具体如下: 简介 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?. Java提供了更加优秀的解决办法:异常处理机制. 异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰. Java中的异常可以是函数中的

  • 详解Java中NullPointerException异常的原因详解以及解决方法

    NullPointerException是当您尝试使用指向内存中空位置的引用(null)时发生的异常,就好像它引用了一个对象一样. 当我们声明引用变量(即对象)时,实际上是在创建指向对象的指针.考虑以下代码,您可以在其中声明基本类型的整型变量x: int x; x = 10; 在此示例中,变量x是一个整型变量,Java将为您初始化为0.当您在第二行中将其分配给10时,值10将被写入x指向的内存中. 但是,当您尝试声明引用类型时会发生不同的事情.请使用以下代码: Integer num; num

随机推荐