Java单元测试初体验(JUnit4)
什么是单元测试
我们在编写大型程序的时候,需要写成千上万个方法或函数,这些函数的功能可能很强大,但我们在程序中只用到该函数的一小部分功能,并且经过调试可以确定,这一小部分功能是正确的。但是,我们同时应该确保每一个函数都完全正确,因为如果我们今后如果对程序进行扩展,用到了某个函数的其他功能,而这个功能有bug的话,那绝对是一件非常郁闷的事情。所以说,每编写完一个函数之后,都应该对这个函数的方方面面进行测试,这样的测试我们称之为单元测试。传统的编程方式,进行单元测试是一件很麻烦的事情,你要重新写另外一个程序,在该程序中调用你需要测试的方法,并且仔细观察运行结果,看看是否有错。这样的话太过于麻烦了,本文简要介绍一下在Eclipse中使用JUnit4进行单元测试的方法。用更加通俗的话来描述单元测试就是:写了个类,要给别人用,会不会有bug?怎么办?测试一下。用main方法测试好不好?这种方法我们经常用,就是写一个方法实现一些功能,把方法的调用方式放在main函数中。这样的测试方式一个是使得main函数太过于混乱,再者测试过程需要人的仔细观察来辨别每个函数的功能实现,哪一个函数出错了,哪一个函数没有输出之类的问题层出不穷,单元测试就是来解决这些问题的。下面我会就单元测试的每一步给出详细的图解和描述。
第一部分
我们先创建一个Java Project,名字就叫做JUnit4,然后创建两个包com.itcast.junit4和com.itcast.junit.test,如下图所示
其中com.itcast.junit4用于我们自己写的类和方法的存放,com.itcast.junit4.test用于我们完成单元测试
在com.itcast.junit4包中创建一个类叫做T(名字可以是任意的,这里是为了方便),在类中添加两个方法add和divide实现两个数的加法和除法运算
package com.itcast.junit4; public class T { public int add(int x,int y){
return x+y;
}
public int divide(int x,int y){
return x/y;
}
public static void main(String[] args) {
int z=new T().add(3, 5);
System.out.println(z);
} }
我们传统测试的方法通常都是按上面的方式去看add函数是否可以实现我们想要的功能,把方法的调用放在main函数中。下面我们看一下单元测试到底是什么东西:
在com.itcast.junit4.test包中创建一个类,叫做TTest(单元测试命名规范:a) 类放在test包中;b) 类名用XXXTest结尾;c) 方法用testMethod命名;)
【步骤提示】com.itcast.junit4.test包-->右键-->New--Junit Test Case,然后选择New Junit 4 test,那个Junit 3已经过时了。下面的一行Class under test,单击右侧的Browser,在弹出的输入框中输入我们想要测试的类名T,选中单击next出现了一个界面要我们选择需要测试的方法,我们这里选择add方法和divide;
接着出现了下面的界面:
这一步提示我们是否将我们需要的JUnit 4的相关包加入到我们项目的ClassPath路径下,点击OK就行,因为Eclipse中包含JUnit的jar包,我们暂且先用Eclipse自带的Junit4去测试,后面后将如何用我们自己下载的JUnit4 JAR包。
做完上面的步骤会创建一个这样的测试类
import static org.junit.Assert.*; import org.junit.Test; public class TTest { @Test
public void testAdd() {
fail("Not yet implemented");
} @Test
public void testDivide() {
fail("Not yet implemented");
} }
上面的org.junit.Assert.*;就是静态导入的我们实现单元测试要用到的一些方法;【注意】这是静态引入,可以把方法直接引入,org.junit.Assert是一个类,不是一个包,当然这些方法肯定都是静态方法了。出现的代码都是Assert类中的一些方法,"@Test”表明下面这个方法是一个测试方法,我们先删除自动生成的fail()函数的代码。添加以下代码:
public class TTest { @Test
public void testAdd() {
int z=new T().add(2, 4);
//判断z==6,以往的assert
assertEquals(6, z);
} @Test
public void testDivide() {
//测试T类中的divide方法
int z=new T().divide(8, 2);
System.out.println(z);
} }
我们现在开始进行测试:要测试的方法-->右键-->Run As-->JUnit Test
如果你想两个方法一块测试,则Run As-->选择Run Configurations:按图中选择相应的选项,然后点击Run
点击run会出现下面的结果:
绿条显示两个方法的功能没有错误,有这样的调试准则:keep the bar green,to keep the code clean,绿色代表测试成功,其中Error:程序出错 Failures:测试失败
Error:是程序有问题,比如我们在testAdd方法中加上这一句:int a=8/0;再次测试这个方法则会出现一个Error
public void testAdd() {
int z=new T().add(2, 4);
//判断z==6,以往的assert
assertEquals(6, z);
int a=8/0;
}
可以看到最下方提示我们,我们写的方法中出现了除数为0的情况
Failures:测试失败,比如我们在方法改成下面的形式:
public void testAdd() {
int z=new T().add(2, 4);
//判断z==6,以往的assert
assertEquals(6, z);
assertTrue(z<3);
// int a=8/0;
}
再次测试一下我们的方法:则会出现调试失败的情况
第二部分
通过上面的学习我们已经了解基本的单元测试的步骤,我们查看一下JUnit API可以看到org.junit.Assert类有很多类似于assertEquals(6, z);assertTrue(z<3);之类的方法的使用;
我们可以看到有很多方法都是以重载的形式出现的,比如我们前面的例子,在testAdd()方法中添加assertTrue("z too small",z>10);前面的字符串用于在我们测试失败的情况下给我们提示:因为8<10,所以会在测试失败的情况下给我们提示"z too small";
重磅出击:assertThat
assertThat(来自hamcrest包,所以我们需要下载hamcrest这个包,这里共享给大家,里面有很多我们平时都可以用到的JAR包和文件,地址:链接:http://pan.baidu.com/s/1sl02DOD 密码:ci5m): assertThat(actual, matcher);的出现可以替代其他所有的assert。放弃旧的断言,使用hamcrest断言。其中actual参数是实际的值,matcher可以是一个匹配器。在以后的项目开发中我们就可以使用assertThat代替前面出现的类如assertEquals(6, z); assertTrue(z<3);方法。
首先第一步,我们想使用assertThat,需要添加两个jar包hamcrest-core-1.2和hamcrest-library-1.2,这两个包都在我的共享里了,想在把这两个包添加进我们的Java Project中
JUnit4 Test-->右键-->Build Path-->Add External Archives,将这两个Jar加进去
添加以后:我们就可以使用assertThat了
我们把testAdd()方法改成下面的形式:
@Test
public void testAdd() {
int z=new T().add(2, 4);
assertThat(z, is(8));
//判断z==6,以往的assert
// assertEquals(6, z);
// assertTrue(z<3);
// int a=8/0;
}
代码中的is()方法是在import static org.hamcrest.Matchers(这个类在我们加进来的hamcrest-core-1.2.jar内)类的一个方法我们需要将其静态引入,所以在最上面要加上下面这一句,应该就可以了;
import static org.hamcrest.Matchers.*;
但是,测试又出现了这样的错误:
我们可以看到Failure Trace第一行什么ClassLoader的错误,这是因为我们在这里用了两种包,一个是hamcrest包,一个是JUnit4的包,这两个包它们的ClassLoader用的不是一个(不清楚啥是ClassLoader,不要紧,先学会怎么解决,以后再研究)。解决方法很简单:在我们的JUnit4项目中-->右键JUnit->Build Path-->Remove from Build Path即可,如图所示:
然后我们自己将JUnit包引入进来(Junit也在我的分享文件中)
JUnit4 Test-->右键-->Build Path-->Add External Archives,选择我们JUnit包中的junit-4.10,如下图所示:
ok!测试成功!
assert的使用是测试代码更加自然(诸如这样的理解:z is 8),自己可以体会一下,下面给出一些实例,大家可以自己动手试一下
示例
a)assertThat( n, allOf( greaterThan(1), lessThan(15) ) );
assertThat( n, anyOf( greaterThan(16), lessThan(8) ) );
assertThat( n, anything() );
assertThat( str, is( "bjsxt" ) );
assertThat( str, not( "bjxxt" ) ); b)assertThat( str, containsString( "bjsxt" ) );
assertThat( str, endsWith("bjsxt" ) );
assertThat( str, startsWith( "bjsxt" ) );
assertThat( n, equalTo( nExpected ) );
assertThat( str, equalToIgnoringCase( "bjsxt" ) );
assertThat( str, equalToIgnoringWhiteSpace( "bjsxt" ) ); c)assertThat( d, closeTo( 3.0, 0.3 ) );
assertThat( d, greaterThan(3.0) );
assertThat( d, lessThan (10.0) );
assertThat( d, greaterThanOrEqualTo (5.0) );
assertThat( d, lessThanOrEqualTo (16.0) ); d)assertThat( map, hasEntry( "bjsxt", "bjsxt" ) );
assertThat( iterable, hasItem ( "bjsxt" ) );
assertThat( map, hasKey ( "bjsxt" ) );
assertThat( map, hasValue ( "bjsxt" ) );
第三部分 JUnit4 Annotation
几种常见的注释形式:
@Test: 测试方法
a) (expected=XXException.class) b) (timeout=xxx) @Ignore: 被忽略的测试方法
@Before: 每一个测试方法之前运行
@After: 每一个测试方法之后运行
@BeforeClass: 所有测试开始之前运行
@AfterClass: 所有测试结束之后运行
我们分别进行解释
1)@Test,前面已经说明了,@Test注解表明下面的方法是一个测试方法,a), b)两种形式,比如@Test(expected=java.lang.ArithmeticException.class,timeout=100)a是在测试出现异常的情况下告知我们出现的异常信息,类似与try-catch中的e.printstacktrace() 方法,比较简单。b中的timeout=100,运行时间限制在100ms以内(通常在测试代码运行效率时这样设置)
2)@ignore: 被忽略的测试方法(就是测试的时候跳过ignor标记的模块或方法)
有时候某些方法还不具备测试的条件,暂时还不能测试或者某些方法已经不需要再做测试了,这就可以进行忽略的操作了。
有时方法的测试条件还没满足,整个项目还差一个模块,则可以采用该方法假定测试条件成立。
3)@after和@before
我们把代码改成这个样子:为方便起见先把那个divide方法删掉
public class TTest {
@Before
public void before() {
System.out.println("befor");
} @Test
public void testAdd() {
int z = new T().add(2, 4);
assertThat(z, is(8));
// 判断z==6,以往的assert
// assertEquals(6, z);
// assertTrue(z<3);
// int a=8/0;
} @After
public void after() {
System.out.println("after");
} }
当然需要在上面需要添加这两句:
import org.junit.After;
import org.junit.Before;
测试一下我们的testAdd()方法,控制台输出:
befor
after
说明@Before在每一个测试方法(@Test方法)之前运行 @After:在每一个测试方法之后运行。它们两个的应用场合: 有些方法需要执行的时候需要一些先决条件,比如打开某文件、获取资源,搭建环境,执行完之后需要关闭文件、释放资源、卸载环境这就需要before和after操作。
4)@BeforeClass;@AfterClass,它们两个都是静态的方法。我们继续改写代码:
public class TTest {
@BeforeClass
public static void beforeClass(){
System.out.println("before class..");
}
@Before
public void before() {
System.out.println("befor");
} @Test
public void testAdd() {
int z = new T().add(2, 4);
assertThat(z, is(6));
// 判断z==6,以往的assert
// assertEquals(6, z);
// assertTrue(z<3);
// int a=8/0;
} @After
public void after() {
System.out.println("after");
}
@AfterClass
public static void afterClass(){
System.out.println("after class..");
} }
当然也要加上:
import org.junit.BeforeClass;
import org.junit.AfterClass;
输出结果:
before class..
befor
after
after class..
这就说明了@BeforeClass 所有测试开始之前运行;@AfterClass: 所有测试结束之后运行【一定要注意】这两个方法都是静态方法,想想也应该明白类一加载就执行这两个方法,此时还没有创建任何对象,能执行的肯定就是静态方法了。
总结:
用了一天的时间学习了一下JUnit单元测试,其实还有很多东西没有看到,但对于单元测试的步骤和套路也算了解了一些,写在这里也方便自己以后的查阅和复习,待以后用到更深入的时候再更新一些单元测试在Spring/Mock中的应用,有错误的地方欢迎大家指出,再次谢谢大家的阅读!
本文为博主原创文章,转载请注明出处:http://www.cnblogs.com/ysw-go/
1、本博客的原创原创文章,都是本人平时学习所做的笔记,如有错误,欢迎指正。
2、如有侵犯您的知识产权和版权问题,请通知本人,本人会即时做出处理文章。
3、本博客的目的是知识交流所用,转载自其它博客或网站,作为自己的参考资料的,感谢这些文章的原创人员
Java单元测试初体验(JUnit4)的更多相关文章
- Java单元测试工具:JUnit4(一)(二)(三)(四)
Java单元测试工具:JUnit4(一)--概述及简单例子 Java单元测试工具:JUnit4(二)--JUnit使用详解 Java单元测试工具:JUnit4(三)--JUnit详解之运行流程及常用注 ...
- JAVA 11初体验
JAVA 11初体验 随着JAVA没半年发布一次新版本,前几天JAVA 11隆重登场.在JAVA 11中,增加了一些新的特性和api, 同时也删除了一些特性和api,还有一些性能和垃圾回收的改进. 作 ...
- Java集合初体验
背景: 因为对Java的集合完全不了解,所以才在网上找了找能形成初步印象的文章进行学习,大多涉及的是一些概念和基础知识. 一.数组array和集合的区别: (1)数组是大小固定的,并且同 ...
- java学习初体验之课后习题
import java.util.Scanner; public class HelloWorld { public static void main(String[] args) { //打印Hel ...
- java activemq初体验
1.下载安装ActiveMQ ActiveMQ官网下载地址:http://activemq.apache.org/download.html ActiveMQ 提供了Windows 和Linux.Un ...
- JAVA多线程-初体验
一.线程和进程 每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程. 进程是所有线程的集合,每一个线程是进程中的一条执行路径. 二.为什么使用多线程,哪些场景下使用 多线程的好处是提高程序 ...
- java学习初体验NO.1
一.学习目标: 1.理解Java编译原理 在Java编程语言中,所以源代码首先以用.Java扩展名结尾的纯文本件编写,然后,编译器将这些源文件编译成.Class文件.然后,Java启动器工具使用Jav ...
- java程序初体验
示例代码 class Demo { public static void main(String[] args) { System.out.print("hello world") ...
- Java爬虫初体验
年关将近,工作上该完成的都差不多了,上午闲着就接触学习了一下爬虫,抽空还把正则表达式复习了,Java的Regex和JS上还是有区别的,JS上的"\w"Java得写成"\\ ...
随机推荐
- Nginx + IIS实现负载均衡 Session多站点共享
日子过得太索然无味了,研究了一下,所谓的负载均衡(主要是windows服务器IIS下的).先看看分析图:环境:linux服务器: centos 6.3windows服务器: windows serve ...
- python socketserver框架解析
socketserver框架是一个基本的socket服务器端框架, 使用了threading来处理多个客户端的连接, 使用seletor模块来处理高并发访问, 是值得一看的python 标准库的源码之 ...
- Spring的事务 之 9.1 数据库事务概述 ——跟我学spring3
9.1 数据库事务概述 事务首先是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务. 事务必需满足ACID(原子性.一致性.隔离性和持久性 ...
- db2 查新索引 主键 sql
1.查询主键 select * from sysibm.syskeycoluse where tbname='...' 2.查询索引 select * from sysibm.indexs where ...
- 北京一家JAVA开发公司面试题(留给后人)
1.jsp有哪些内置对象?作用分别是什么? 2.描述一下servlet的生命周期和基本架构. 3.多线程有几种实现方法,都是什么? 同步有几种实现方法,都是什么? 4.作用域public priv ...
- windows下mongodb安装详解
1.打开官网https://www.mongodb.com/download-center?jmp=nav#community 注:这里小伙伴们可是开启下FQ软件psiphon 3下载(不开启FQ好像 ...
- JavaScript中对象数组 根据某个属性值进行排序
将下列对象数组,通过工资属性,由高到低排序 var BaiduUsers = [], WechatUsers = []; var User = function(id, name, phone, ge ...
- Error:unsupported class file version 52.0问题的解决
这个问题主要的原因是依赖包的编译版本比主程序的编译版本高,导致主程序无法正常编译或运行,解决这个问题无非两招: 1.提升主程序的编译器版本,用最新的编译器编译主程序,这样就可以兼容那个依赖包 2.降低 ...
- 爬虫之urllib.error模块
error模块简介 我们在爬虫的时候发请求的时候难免出现错误,如访问不到服务器或者访问被禁止等等, 出错了之后urllib将错误信息封装成了一个模块对象中,这个模块就叫error模块 error的分类 ...
- java解析xml字符串方法
一,用DOM4J 针对无重复标签的xml字符串格式,如下: 针对此种情况可用DOM4J解析法,引入 dom4j的相关jar包代码如下: Document document=DocumentHelpe ...