junit in java 真正的测试用例实战!
单元测试基础 ???????? 当今软件测试十分盛行时,本人通过项目实践和个人亲身体会浅谈单元测试,本人一直坚持 “ 用代码说话的原则 ” ,同时也希望个人能给出宝贵意见,共同探讨、共同进步,为中国软件事业有更大的发展共同奋斗! 最早我们项目组开发的项目时,写代码都是从底层一直写到表现层到 jsp ,然后开发人员在 web 层调试页面,近乎 98 %都会报一大堆 exception, 然后再在代码中加断点一步一步查到底哪一层代码出现问题 ……, 比较好点做法就是在各个类中加上 main 方法测试,但总体很不理想,给 web 层开发人员的调试和质量控制人员带来繁重的工作压力;使用单元测试后,针对每一个方法都做严格的把关,大大减少调试的时间;同时质量控制人员返回过来的 bug 少了近 60 %,现在对于开发人员写测试用例非常熟练,并且本人根据实际情况对测试用例做了点小小改动(这部分主要在后面代码中详述),带来很好的效果! 单元测试到底给实际开发带来什么好处那?
(1) 首先对于开发人员来说大大减少调试工作的时间,同时也规范了对于代码安全管理(我们知道那些方法是可以调用的);
(2) 对于整个项目来说,有了完整的测试,保证项目最后交付测试有了可靠依据;
(3)对于测试人员大大减少bug的反馈;
(4)对于项目经理整个项目达到很好的可控;
(5)最主要的完整的单元测试给后期维护人员带来很大的便捷!
单元测试好处可能还有很多,但本人只能理解和感悟这么多 , 希望观者补充! 单元测试配置: ?????? 我将使用 eclipse + myEclopse 给大家介绍关于 JUNIT 的环境的简单配置;右键点击项目选择 “ 属性 ” ,在弹出窗口中到环境变量中添加 junit.jar 包,这样下一步我们就可以进行单元测试了;
使用 eclipse 快速开发 test Case : 如下图:右键选择你要测试的类,在新建中点击 “JUnit 测试用例 ” ,
弹出对话框,配置测试名称和根目录,添加注释等,再点击 “ 下一步 ” 到下图:
????? 选择你要测试类中的方法,点击完成!便生成测试类的基本框架,如下代码,我们以对一个 DAO 类测试为例:/*
?*?Copyright?reserved?2005?by?XXXXCo.?Ltd.
?*?Author:XXX???Date:2006-9-4
?*/
import?junit.framework.TestCase;
/**?
?*?@author?XXX
?*/
public?class?OrgTypeDAOTest?extends?TestCase?{
????/**?
?????*?@param?arg0
?????*/
????public?OrgTypeDAOTest(String?arg0){
????????super(arg0);
????}
????/**?
?????*?@see?junit.framework.TestCase#setUp()
?????*/
????protected?void?setUp()?throws?Exception{???????
??????????super.setUp();
????}
????/**
?????*?@see?junit.framework.TestCase#tearDown()
?????*/
????protected?void?tearDown()?throws?Exception{
????????super.tearDown();
????}
????/**?
?????*??主函数
?????*?@param?args
?????*/
????public?static?void?main(String[]?args){
????????TestRunner.run(OrgTypeDAOTest?.class);
????}
????/**?
?????*?{@link?OrgTypeDAO#getOrgTypeList()}?的测试方法。
?????*/
????public?final?void?testGetOrgTypeList()?{
????????fail("尚未实现");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#insertOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的测试方法。
?????*/
????public?final?void?testInsertOrgTypeInfo()?{
????????fail("尚未实现");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#deleteOrgTypeInfo(java.lang.String)}?的测试方法。
?????*/
????public?final?void?testDeleteOrgTypeInfo()?{
????????fail("尚未实现");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#updateOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的测试方法。
?????*/
????public?final?void?testUpdateOrgTypeInfo()?{
????????fail("尚未实现");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#getOrgTypeInfoById(java.lang.String)}?的测试方法。
?????*/
????public?final?void?testGetOrgTypeInfoById()?{
????????fail("尚未实现");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#isRepeatOrgTypeInfo(java.lang.String)}?的测试方法。
?????*/
????public?final?void?testIsRepeatOrgTypeInfoString(){
????????fail("尚未实现");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#isRepeatOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的测试方法。
?????*/
????public?final?void?testIsRepeatOrgTypeInfoOrgTypeVO()?{
????????fail("尚未实现");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#getFlatOrgIdByName(java.lang.String)}?的测试方法。
?????*/
????public?final?void?testGetFlatOrgIdByName()?{
????????fail("尚未实现");?//?TODO
????}
}
JUnit单元测试一共要注意一下几点:(1)import junit.framework.TestCase 和 junit.textui.TestRunner; (2)继承junit.framework.TestCase ; (3)自行添加一个main方法????中调用TestRunner.run(测试类名.class); (4)有一个调用super(String)的构造函数; ??????? 以上都是JUnit必有的特征,除以上外,我们发现有许多以test开头的方法,而这些方法正是我们要测试的方法,Junti测试其实采用的是断言的方式,只要我们在所有test开头中的方法对数据添加断言方法,同时提供很多断言的方法,
?
常用断言方法assertEquals("失败提示信息","期望数据","测试数据") 断言获取数据是否与所期望的相等assertNotNull("失败提示信息","测试数据") 断言获取数据不为null,否则提示错误 assertNull("失败提示信息","测试数据") 断言获取数据是为null,否则提示错误 assertTrue("失败提示信息",测试数据blooean值) 断言获取数据是否为ture,否则提示错误 fail("失败提示信息"); 此方法一般放到异常处,遇到此方法,测试将停止! assertSame("失败提示信息","期望数据","测试数据") 断言获取数据是否与所期望的相同当我们写完所有方法策略后,JUnit测试如下图: 在方法页面中点击右键在“调试方式”或“运行方式”中点击“JUnit 测试”,就运行测试类!
在执行测试类时,执行的大概过程: (1)先执行构造方法public OrgTypeDAOTest(String arg0) ; (2)再执行初始化数据方法protected void setUp() ; (3)再执行以test开头的测试方法; (4)最后执行protected void tearDown()方法清理对象; 如果测试失败或者错误,将会显示一个红色的亮条;如果测试通过将显示绿色亮条;如下图
?这样就把一个整个单元测试操作例子演示完成!可能对于一个测试类中有多个方法要测试,对于后面看着的确有些困难,因此,我对上测试类进行简单的调整,如下代码:?import junit.framework.TestCase;import junit.textui.TestRunner;
//import com.zhjy.mock.SpringMock;/**
?* 举例测试类
?*/
public class OrgTypeDAOTest extends TestCase {??? //(1)继承TestCase ??? //private OrgTypeDAO orgTypeDAO;
??? //private OrgTypeVO orgTypeVO;
??? //private String id ;
??? /**
???? *? 构造方法
???? * @param arg0
???? */
??? public OrgTypeDAOTest(String arg0) {
??????? super(arg0);
??? }???
??? /**
???? *初时化方法
????? * @see junit.framework.TestCase#setUp()
???? */
??? protected void setUp() throws Exception {
??????? super.setUp();
??????? //测试初始话数据调用类 orgTypeDAO和 封装数据的对象orgTypeVO
??? }??? /** 执行完清理方法
???? * @see junit.framework.TestCase#tearDown()
???? */
??? protected void tearDown() throws Exception {
??????? //清空 对象? ;==null
??????? //orgTypeDAO =null;
??????? //orgTypeVO =null;
??????? super.tearDown();
??? }
??? /**
???? *? 主函数
???? * @param args
???? */
??? public static void main(String[] args){??????? TestRunner.run(OrgTypeDAOTest.class);
??? }
??? /**
???? * 测试方法
???? * Test method testOrgTypeInfo
???? */
??? public void testOrgTypeInfo() {
??????? //添加
??????? String id = insertOrgTypeInfo();
??????? //列表
??????? orgTypeList();
??????? //修改
??????? updateOrgTypeInfo(id);
??????? //查询
??????? selectOrgTypeInfoById(id);
??????? //校验
??????? iExistOrgByOrgTypeId(id);
??????? //测试是否重复数据方法(add)
??????? isRepeatOrgTypeInfo(orgTypeVO.getName(),"");
??????? //获取数据方法(根据名称)
??????? selectOrgTypeIdByName(orgTypeVO.getName());
??????? //删除
??????? deleteOrgTypeInfo(id);
??? }
??? /**
???? *添加初始数据
???? */
??? private void setOrgTypeVOAddInfo() {
??????? orgTypeVO.setName("add中海测试");
??????? orgTypeVO.setDescription("add中海测试");
??????? orgTypeVO.setStatus("1");
??? }
??? /**
???? *添加初始数据
???? */
??? private void setOrgTypeVOUpdateInfo(){
???????
??????? //orgTypeVO.setId(id);
??????? orgTypeVO.setName("add中海测试");
??????? orgTypeVO.setDescription("update中海测试");
??????? orgTypeVO.setStatus("1");
??? }
??? /**
???? * 新增方法
???? * Test method for {@link OrgTypeDAO#insertOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}.
???? */
??? public String insertOrgTypeInfo(){
??????? setOrgTypeVOAddInfo();
??????? String id = null;
??????? try{
??????????? id = orgTypeDAO.insertOrgTypeInfo(orgTypeVO);
??????? }catch(Exception e){
??????????? fail("添加通用组织机构失败!");
??????? }
??????? return id;
??? }
??? /**
???? * 更新方法
???? * Test method for {@link com.zhjy.mltx.dao.OrgTypeDAO#updateOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}.
???? */
??? public void updateOrgTypeInfo(String id) {
??????? setOrgTypeVOUpdateInfo();
??????? orgTypeVO.setId(id);
??????? try{
??????????? orgTypeDAO.updateOrgTypeInfo(orgTypeVO);
??????? }catch(Exception e){
??????????? assertTrue("修改通用组织机构失败!", false);
??????? }
??????? //查询
??????? orgTypeVO = orgTypeDAO.selectOrgTypeInfoById(id);
??????? assertEquals("修改通用组织机构失败!", orgTypeVO.getDescription(), "update中海测试");
??? }
??? /**
???? * 获取数据方法(主健)
???? * Test method for {@link OrgTypeDAO#selectOrgTypeInfoById(java.lang.String)}.
???? */
??? public void selectOrgTypeInfoById(String id) {
??????? orgTypeVO = orgTypeDAO.selectOrgTypeInfoById(id);
??????? assertTrue("无法查看一条通用机构名称信息!",orgTypeVO != null);
??????? assertEquals("添加通用组织机构失败!", orgTypeVO.getName(), "add中海测试");
??????? assertEquals("修改通用组织机构失败!", orgTypeVO.getDescription(), "update中海测试");
??? }??? /**
???? * 测试此通用组织机构是否被引用
???? * Test method for {@link OrgTypeDAO#iExistOrgByOrgTypeId(java.lang.String)}.
???? */
??? public void iExistOrgByOrgTypeId(String id){
??????? boolean isfalse;
??????? try {
??????????? isfalse = this.orgTypeDAO.iExistOrgByOrgTypeId(id);
??????????? assertFalse("通用组织机构校验错误!",isfalse);
??????? } catch (DataAccessException e) {
??????????? assertTrue("通用组织机构数据操作错误!!",false);
??????? } catch (ObjectNotFoundException e) {
??????????? assertTrue("id is null!!",false);
??????? }
???????
??? }
??? /**
???? * 删除
???? * Test method for {@link OrgTypeDAO#deleteOrgTypeInfo(java.lang.String)}.
???? */
??? public void deleteOrgTypeInfo(String id) {
??????? this.orgTypeDAO.deleteOrgTypeInfo(id);
??????? orgTypeVO = this.orgTypeDAO.selectOrgTypeInfoById(id);
??????? assertNull("删除通用组织机构失败!", orgTypeVO);
??? }
??? /**
???? * 获取数据方法(根据名称)
???? * Test method for {@link OrgTypeDAO#selectOrgTypeIdByName(java.lang.String)}.
???? */
??? public void selectOrgTypeIdByName(String name) {
??????? String orgtypeid = orgTypeDAO.selectOrgTypeIdByName(name);
??????? //assertEquals(orgtypeid, id);
??????? assertNotNull("未查出来通用组织机构ID!",orgtypeid);
??? }
??? /**
???? * 测试是否重复数据方法
???? * Test method for {@link OrgTypeDAO#isRepeatOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}.
???? */
??? public void isRepeatOrgTypeInfo(String name,String id) {
??????? //setOrgTypeVOUpdateInfo();
??????? OrgTypeVO orgtypetest = new OrgTypeVO();
??????? orgtypetest.setId(id);
??????? orgtypetest.setName(name);
??????? boolean isTrue = orgTypeDAO.isRepeatOrgTypeInfo(orgtypetest);
??????? //assertEquals("通用组织机构错误数据",isTrue, false);
??????? assertTrue("通用组织机构错误数据", isTrue);
??? }
??? /**
???? * 列表方法
???? * Test method for {@link com.zhjy.mltx.dao.OrgTypeDAO#orgTypeList()}.
???? */
??? public void orgTypeList() {
??????? List list = orgTypeDAO.orgTypeList();
??????? assertNotNull("无法获取通用机构名称列表list is null!",list);
??? }}
处理过程: ( 1 )把所有以 test 开头的方法中的方法名称前的 test 去掉(例如把 testOrgTypeList() 改为 orgTypeList() 方法); ( 2 )同时添加一个以 test 开头的方法,并调用测试方法;这样做主要是因为执行测试时 JUnit (除本身特有的方法出外)只执行以 test 为开头的方法; ( 3 )同时大家注意到我还把测试初始数据放到两个 private 方法中 private void setOrgTypeVOAddInfo() 和 private void setOrgTypeVOUpdateInfo() 方法中供调用; ??????? 大家可能认为很简单,就是这样简单的改动带来很大的方便,大家是否注意到,我以后运行测试用例时,只关心 public void testOrgTypeInfo() 方法就行了,因为只要各个方法的断言策略定制完成,一般就不会再改动,因此,后期的回归测试还是验收测试,缩小了我们对测试的关注点,同时对后面写 test suite 构建整体测试带来极大方便性!其中的好处相信只有使用才能体会! ???? 说到这,就此暂告一段落!由于时间仓促,书写不规范和关于任何问题处敬请指出,相互探讨.