posts - 6,comments - 7,trackbacks - 0
          單元測(cè)試基礎(chǔ)
          ???????? 當(dāng)今軟件測(cè)試十分盛行時(shí),本人通過項(xiàng)目實(shí)踐和個(gè)人親身體會(huì)淺談單元測(cè)試,本人一直堅(jiān)持 用代碼說話的原則 ,同時(shí)也希望個(gè)人能給出寶貴意見,共同探討、共同進(jìn)步,為中國軟件事業(yè)有更大的發(fā)展共同奮斗!
          最早我們項(xiàng)目組開發(fā)的項(xiàng)目時(shí),寫代碼都是從底層一直寫到表現(xiàn)層到 jsp ,然后開發(fā)人員在 web 層調(diào)試頁面,近乎 98 %都會(huì)報(bào)一大堆 exception, 然后再在代碼中加斷點(diǎn)一步一步查到底哪一層代碼出現(xiàn)問題 ……, 比較好點(diǎn)做法就是在各個(gè)類中加上 main 方法測(cè)試,但總體很不理想,給 web 層開發(fā)人員的調(diào)試和質(zhì)量控制人員帶來繁重的工作壓力;使用單元測(cè)試后,針對(duì)每一個(gè)方法都做嚴(yán)格的把關(guān),大大減少調(diào)試的時(shí)間;同時(shí)質(zhì)量控制人員返回過來的 bug 少了近 60 %,現(xiàn)在對(duì)于開發(fā)人員寫測(cè)試用例非常熟練,并且本人根據(jù)實(shí)際情況對(duì)測(cè)試用例做了點(diǎn)小小改動(dòng)(這部分主要在后面代碼中詳述),帶來很好的效果!
          單元測(cè)試到底給實(shí)際開發(fā)帶來什么好處那?
          (1) 首先對(duì)于開發(fā)人員來說大大減少調(diào)試工作的時(shí)間,同時(shí)也規(guī)范了對(duì)于代碼安全管理(我們知道那些方法是可以調(diào)用的);
          (2) 對(duì)于整個(gè)項(xiàng)目來說,有了完整的測(cè)試,保證項(xiàng)目最后交付測(cè)試有了可靠依據(jù);
          (3)對(duì)于測(cè)試人員大大減少bug的反饋;
          (4)對(duì)于項(xiàng)目經(jīng)理整個(gè)項(xiàng)目達(dá)到很好的可控;
          (5)最主要的完整的單元測(cè)試給后期維護(hù)人員帶來很大的便捷!

          單元測(cè)試好處可能還有很多,但本人只能理解和感悟這么多 , 希望觀者補(bǔ)充!
          單元測(cè)試配置:
          ?????? 我將使用 eclipse myEclopse 給大家介紹關(guān)于 JUNIT 的環(huán)境的簡(jiǎn)單配置;右鍵點(diǎn)擊項(xiàng)目選擇 屬性 ,在彈出窗口中到環(huán)境變量中添加 junit.jar 包,這樣下一步我們就可以進(jìn)行單元測(cè)試了;
          使用 eclipse 快速開發(fā) test Case
          如下圖:右鍵選擇你要測(cè)試的類,在新建中點(diǎn)擊 “JUnit 測(cè)試用例
          彈出對(duì)話框,配置測(cè)試名稱和根目錄,添加注釋等,再點(diǎn)擊 下一步 到下圖:
          ????? 選擇你要測(cè)試類中的方法,點(diǎn)擊完成!便生成測(cè)試類的基本框架,如下代碼,我們以對(duì)一個(gè) DAO 類測(cè)試為例:
          /*
          ?*?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();
          ????}
          ????
          /**?
          ?????*??主函數(shù)
          ?????*?
          @param?args
          ?????
          */
          ????
          public?static?void?main(String[]?args){
          ????????TestRunner.run(OrgTypeDAOTest?.
          class);
          ????}
          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#getOrgTypeList()}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testGetOrgTypeList()?{
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}

          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#insertOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testInsertOrgTypeInfo()?{
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}

          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#deleteOrgTypeInfo(java.lang.String)}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testDeleteOrgTypeInfo()?{
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}

          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#updateOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testUpdateOrgTypeInfo()?{
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}

          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#getOrgTypeInfoById(java.lang.String)}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testGetOrgTypeInfoById()?{
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}

          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#isRepeatOrgTypeInfo(java.lang.String)}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testIsRepeatOrgTypeInfoString(){
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}

          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#isRepeatOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testIsRepeatOrgTypeInfoOrgTypeVO()?{
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}

          ????
          /**?
          ?????*?{
          @link?OrgTypeDAO#getFlatOrgIdByName(java.lang.String)}?的測(cè)試方法。
          ?????
          */
          ????
          public?final?void?testGetFlatOrgIdByName()?{
          ????????fail(
          "尚未實(shí)現(xiàn)");?//?TODO
          ????}
          }


          JUnit單元測(cè)試一共要注意一下幾點(diǎn):
          1import junit.framework.TestCase junit.textui.TestRunner
          2)繼承junit.framework.TestCase
          3)自行添加一個(gè)main方法????中調(diào)用TestRunner.run(測(cè)試類名.class);
          4)有一個(gè)調(diào)用super(String)的構(gòu)造函數(shù);
          ??????? 以上都是JUnit必有的特征,除以上外,我們發(fā)現(xiàn)有許多以test開頭的方法,而這些方法正是我們要測(cè)試的方法,Junti測(cè)試其實(shí)采用的是斷言的方式,只要我們?cè)谒?/span>test開頭中的方法對(duì)數(shù)據(jù)添加斷言方法,同時(shí)提供很多斷言的方法,

          常用斷言方法
          assertEquals("失敗提示信息","期望數(shù)據(jù)","測(cè)試數(shù)據(jù)")
          斷言獲取數(shù)據(jù)是否與所期望的相等
          assertNotNull("失敗提示信息","測(cè)試數(shù)據(jù)")
          斷言獲取數(shù)據(jù)不為null,否則提示錯(cuò)誤
          assertNull("失敗提示信息","測(cè)試數(shù)據(jù)")
          斷言獲取數(shù)據(jù)是為null,否則提示錯(cuò)誤
          assertTrue("失敗提示信息",測(cè)試數(shù)據(jù)blooean)
          斷言獲取數(shù)據(jù)是否為ture,否則提示錯(cuò)誤
          fail("失敗提示信息");
          此方法一般放到異常處,遇到此方法,測(cè)試將停止!
          assertSame("失敗提示信息","期望數(shù)據(jù)","測(cè)試數(shù)據(jù)")
          斷言獲取數(shù)據(jù)是否與所期望的相同
          當(dāng)我們寫完所有方法策略后,JUnit測(cè)試如下圖:
          在方法頁面中點(diǎn)擊右鍵在調(diào)試方式運(yùn)行方式中點(diǎn)擊JUnit 測(cè)試,就運(yùn)行測(cè)試類!
          在執(zhí)行測(cè)試類時(shí),執(zhí)行的大概過程
          1)先執(zhí)行構(gòu)造方法public OrgTypeDAOTest(String arg0)
          2)再執(zhí)行初始化數(shù)據(jù)方法protected void setUp()
          3)再執(zhí)行以test開頭的測(cè)試方法;
          4)最后執(zhí)行protected void tearDown()方法清理對(duì)象;
          如果測(cè)試失敗或者錯(cuò)誤,將會(huì)顯示一個(gè)紅色的亮條;如果測(cè)試通過將顯示綠色亮條;如下圖
          ?
          這樣就把一個(gè)整個(gè)單元測(cè)試操作例子演示完成!
          可能對(duì)于一個(gè)測(cè)試類中有多個(gè)方法要測(cè)試,對(duì)于后面看著的確有些困難,因此,我對(duì)上測(cè)試類進(jìn)行簡(jiǎn)單的調(diào)整,如下代碼:
          import?junit.framework.TestCase;
          import?junit.textui.TestRunner;
          //import?com.zhjy.mock.SpringMock;

          **?
          ?*?舉例測(cè)試類
          ?
          */

          public?class?OrgTypeDAOTest?extends?TestCase?{????//(1)繼承TestCase?

          ????
          //private?OrgTypeDAO?orgTypeDAO;
          ????
          //private?OrgTypeVO?orgTypeVO;
          ????
          //private?String?id?;
          ????**?
          ?????*??構(gòu)造方法
          ?????*?
          @param?arg0?
          ?????
          */

          ????
          public?OrgTypeDAOTest(String?arg0)?{
          ????????
          super(arg0);
          ????}
          ????
          ????
          /**
          ?????*初時(shí)化方法
          ??????*?
          @see?junit.framework.TestCase#setUp()
          ?????
          */

          ????
          protected?void?setUp()?throws?Exception?{
          ????????
          super.setUp();
          ????????
          //測(cè)試初始話數(shù)據(jù)調(diào)用類?orgTypeDAO和?封裝數(shù)據(jù)的對(duì)象orgTypeVO
          ????}


          ????
          /**?執(zhí)行完清理方法
          ?????*?
          @see?junit.framework.TestCase#tearDown()
          ?????
          */

          ????
          protected?void?tearDown()?throws?Exception?{
          ????????
          //清空?對(duì)象??;==null
          ????????
          //orgTypeDAO?=null;
          ????????
          //orgTypeVO?=null;
          ????????super.tearDown();
          ????}

          ????
          /**?
          ?????*??主函數(shù)
          ?????*?
          @param?args
          ?????
          */

          ????
          public?static?void?main(String[]?args){

          ????????TestRunner.run(OrgTypeDAOTest.
          class);
          ????}

          ????
          /**?
          ?????*?測(cè)試方法
          ?????*?Test?method?testOrgTypeInfo
          ?????
          */

          ????
          public?void?testOrgTypeInfo()?{
          ????????
          //添加
          ????????String?id?=?insertOrgTypeInfo();
          ????????
          //列表
          ????????orgTypeList();
          ????????
          //修改
          ????????updateOrgTypeInfo(id);
          ????????
          //查詢
          ????????selectOrgTypeInfoById(id);
          ????????
          //校驗(yàn)
          ????????iExistOrgByOrgTypeId(id);
          ????????
          //測(cè)試是否重復(fù)數(shù)據(jù)方法(add)
          ????????isRepeatOrgTypeInfo(orgTypeVO.getName(),"");
          ????????
          //獲取數(shù)據(jù)方法(根據(jù)名稱)
          ????????selectOrgTypeIdByName(orgTypeVO.getName());
          ????????
          //刪除
          ????????deleteOrgTypeInfo(id);
          ????}

          ????
          **
          ?????*添加初始數(shù)據(jù)
          ?????
          */

          ????
          private?void?setOrgTypeVOAddInfo()?{
          ????????orgTypeVO.setName(
          "add中海測(cè)試");
          ????????orgTypeVO.setDescription(
          "add中海測(cè)試");
          ????????orgTypeVO.setStatus(
          "1");
          ????}

          ????
          **
          ?????*添加初始數(shù)據(jù)
          ?????
          */

          ????
          private?void?setOrgTypeVOUpdateInfo(){
          ????????
          ????????
          //orgTypeVO.setId(id);
          ????????orgTypeVO.setName("add中海測(cè)試");
          ????????orgTypeVO.setDescription(
          "update中海測(cè)試");
          ????????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(
          "添加通用組織機(jī)構(gòu)失敗!");
          ????????}

          ????????
          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(
          "修改通用組織機(jī)構(gòu)失敗!",?false);
          ????????}

          ????????
          //查詢
          ????????orgTypeVO?=?orgTypeDAO.selectOrgTypeInfoById(id);
          ????????assertEquals(
          "修改通用組織機(jī)構(gòu)失敗!",?orgTypeVO.getDescription(),?"update中海測(cè)試");
          ????}

          ????
          **
          ?????*?獲取數(shù)據(jù)方法(主健)
          ?????*?Test?method?for?{
          @link?OrgTypeDAO#selectOrgTypeInfoById(java.lang.String)}.
          ?????
          */

          ????
          public?void?selectOrgTypeInfoById(String?id)?{
          ????????orgTypeVO?
          =?orgTypeDAO.selectOrgTypeInfoById(id);
          ????????assertTrue(
          "無法查看一條通用機(jī)構(gòu)名稱信息!",orgTypeVO?!=?null);
          ????????assertEquals(
          "添加通用組織機(jī)構(gòu)失敗!",?orgTypeVO.getName(),?"add中海測(cè)試");
          ????????assertEquals(
          "修改通用組織機(jī)構(gòu)失敗!",?orgTypeVO.getDescription(),?"update中海測(cè)試");
          ????}


          ????
          **
          ?????*?測(cè)試此通用組織機(jī)構(gòu)是否被引用
          ?????*?Test?method?for?{
          @link?OrgTypeDAO#iExistOrgByOrgTypeId(java.lang.String)}.
          ?????
          */

          ????
          public?void?iExistOrgByOrgTypeId(String?id){
          ????????
          boolean?isfalse;
          ????????
          try?{
          ????????????isfalse?
          =?this.orgTypeDAO.iExistOrgByOrgTypeId(id);
          ????????????assertFalse(
          "通用組織機(jī)構(gòu)校驗(yàn)錯(cuò)誤!",isfalse);
          ????????}
          ?catch?(DataAccessException?e)?{
          ????????????assertTrue(
          "通用組織機(jī)構(gòu)數(shù)據(jù)操作錯(cuò)誤!!",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(
          "刪除通用組織機(jī)構(gòu)失敗!",?orgTypeVO);
          ????}

          ????
          **
          ?????*?獲取數(shù)據(jù)方法(根據(jù)名稱)
          ?????*?Test?method?for?{
          @link?OrgTypeDAO#selectOrgTypeIdByName(java.lang.String)}.
          ?????
          */

          ????
          public?void?selectOrgTypeIdByName(String?name)?{
          ????????String?orgtypeid?
          =?orgTypeDAO.selectOrgTypeIdByName(name);
          ????????
          //assertEquals(orgtypeid,?id);
          ????????assertNotNull("未查出來通用組織機(jī)構(gòu)ID!",orgtypeid);
          ????}

          ????
          /**
          ?????*?測(cè)試是否重復(fù)數(shù)據(jù)方法
          ?????*?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("通用組織機(jī)構(gòu)錯(cuò)誤數(shù)據(jù)",isTrue,?false);
          ????????assertTrue("通用組織機(jī)構(gòu)錯(cuò)誤數(shù)據(jù)",?isTrue);
          ????}

          ????
          /**
          ?????*?列表方法
          ?????*?Test?method?for?{
          @link?com.zhjy.mltx.dao.OrgTypeDAO#orgTypeList()}.
          ?????
          */

          ????
          public?void?orgTypeList()?{
          ????????List?list?
          =?orgTypeDAO.orgTypeList();
          ????????assertNotNull(
          "無法獲取通用機(jī)構(gòu)名稱列表list?is?null!",list);
          ????}


          }

          處理過程:
          1 )把所有以 test 開頭的方法中的方法名稱前的 test 去掉(例如把 testOrgTypeList() 改為 orgTypeList() 方法);
          2 )同時(shí)添加一個(gè)以 test 開頭的方法,并調(diào)用測(cè)試方法;這樣做主要是因?yàn)閳?zhí)行測(cè)試時(shí) JUnit (除本身特有的方法出外)只執(zhí)行以 test 為開頭的方法;
          3 )同時(shí)大家注意到我還把測(cè)試初始數(shù)據(jù)放到兩個(gè) private 方法中 private void setOrgTypeVOAddInfo() private void setOrgTypeVOUpdateInfo() 方法中供調(diào)用;
          ??????? 大家可能認(rèn)為很簡(jiǎn)單,就是這樣簡(jiǎn)單的改動(dòng)帶來很大的方便,大家是否注意到,我以后運(yùn)行測(cè)試用例時(shí),只關(guān)心 public void testOrgTypeInfo() 方法就行了,因?yàn)橹灰鱾€(gè)方法的斷言策略定制完成,一般就不會(huì)再改動(dòng),因此,后期的回歸測(cè)試還是驗(yàn)收測(cè)試,縮小了我們對(duì)測(cè)試的關(guān)注點(diǎn),同時(shí)對(duì)后面寫 test suite 構(gòu)建整體測(cè)試帶來極大方便性!其中的好處相信只有使用才能體會(huì)!
          ???? 說到這,就此暫告一段落!由于時(shí)間倉促,書寫不規(guī)范和關(guān)于任何問題處敬請(qǐng)指出,相互探討.
          posted on 2006-10-01 14:54 扁豆 閱讀(2643) 評(píng)論(2)  編輯  收藏 所屬分類: java技術(shù)

          FeedBack:
          # re: junit in java 真正的測(cè)試用例實(shí)戰(zhàn)!
          2006-10-01 20:05 | JonneyQuest
          最新版本的JUnit已經(jīng)不用集成TestCase了,可以直接加Annountation標(biāo)記。  回復(fù)  更多評(píng)論
            
          # re: junit in java 真正的測(cè)試用例實(shí)戰(zhàn)!
          2009-08-01 09:30 | vanjor
          “把所有以 test 開頭的方法中的方法名稱前的 test 去掉(例如把 testOrgTypeList() 改為 orgTypeList() 方法)”
          好小細(xì)節(jié)!  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 黄平县| 靖西县| 中山市| 南木林县| 青川县| 南丰县| 新蔡县| 马边| 固阳县| 泉州市| 永定县| 扬中市| 胶南市| 偏关县| 广平县| 罗平县| 唐河县| 乌拉特中旗| 闵行区| 印江| 万宁市| 高密市| 万山特区| 朝阳县| 巴里| 漳浦县| 福州市| 治多县| 万荣县| 浦县| 南岸区| 略阳县| 迭部县| 唐海县| 肇源县| 广德县| 岱山县| 于都县| 新建县| 友谊县| 集贤县|