單元測試基礎(chǔ)
????????
當今軟件測試十分盛行時,本人通過項目實踐和個人親身體會淺談單元測試,本人一直堅持
“
用代碼說話的原則
”
,同時也希望個人能給出寶貴意見,共同探討、共同進步,為中國軟件事業(yè)有更大的發(fā)展共同奮斗!
最早我們項目組開發(fā)的項目時,寫代碼都是從底層一直寫到表現(xiàn)層到
jsp
,然后開發(fā)人員在
web
層調(diào)試頁面,近乎
98
%都會報一大堆
exception,
然后再在代碼中加斷點一步一步查到底哪一層代碼出現(xiàn)問題
……,
比較好點做法就是在各個類中加上
main
方法測試,但總體很不理想,給
web
層開發(fā)人員的調(diào)試和質(zhì)量控制人員帶來繁重的工作壓力;使用單元測試后,針對每一個方法都做嚴格的把關(guān),大大減少調(diào)試的時間;同時質(zhì)量控制人員返回過來的
bug
少了近
60
%,現(xiàn)在對于開發(fā)人員寫測試用例非常熟練,并且本人根據(jù)實際情況對測試用例做了點小小改動(這部分主要在后面代碼中詳述),帶來很好的效果!
單元測試到底給實際開發(fā)帶來什么好處那?
(1)
首先對于開發(fā)人員來說大大減少調(diào)試工作的時間,同時也規(guī)范了對于代碼安全管理(我們知道那些方法是可以調(diào)用的);
(2)
對于整個項目來說,有了完整的測試,保證項目最后交付測試有了可靠依據(jù);
(3)對于測試人員大大減少bug的反饋;
(4)對于項目經(jīng)理整個項目達到很好的可控;
(5)最主要的完整的單元測試給后期維護人員帶來很大的便捷!
單元測試好處可能還有很多,但本人只能理解和感悟這么多
,
希望觀者補充!
單元測試配置:
?????? 我將使用
eclipse
+
myEclopse
給大家介紹關(guān)于
JUNIT
的環(huán)境的簡單配置;右鍵點擊項目選擇
“
屬性
”
,在彈出窗口中到環(huán)境變量中添加
junit.jar
包,這樣下一步我們就可以進行單元測試了;
使用
eclipse
快速開發(fā)
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();
????}
????/**?
?????*??主函數(shù)
?????*?@param?args
?????*/
????public?static?void?main(String[]?args){
????????TestRunner.run(OrgTypeDAOTest?.class);
????}
????/**?
?????*?{@link?OrgTypeDAO#getOrgTypeList()}?的測試方法。
?????*/
????public?final?void?testGetOrgTypeList()?{
????????fail("尚未實現(xiàn)");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#insertOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的測試方法。
?????*/
????public?final?void?testInsertOrgTypeInfo()?{
????????fail("尚未實現(xiàn)");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#deleteOrgTypeInfo(java.lang.String)}?的測試方法。
?????*/
????public?final?void?testDeleteOrgTypeInfo()?{
????????fail("尚未實現(xiàn)");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#updateOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的測試方法。
?????*/
????public?final?void?testUpdateOrgTypeInfo()?{
????????fail("尚未實現(xiàn)");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#getOrgTypeInfoById(java.lang.String)}?的測試方法。
?????*/
????public?final?void?testGetOrgTypeInfoById()?{
????????fail("尚未實現(xiàn)");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#isRepeatOrgTypeInfo(java.lang.String)}?的測試方法。
?????*/
????public?final?void?testIsRepeatOrgTypeInfoString(){
????????fail("尚未實現(xiàn)");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#isRepeatOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}?的測試方法。
?????*/
????public?final?void?testIsRepeatOrgTypeInfoOrgTypeVO()?{
????????fail("尚未實現(xiàn)");?//?TODO
????}
????/**?
?????*?{@link?OrgTypeDAO#getFlatOrgIdByName(java.lang.String)}?的測試方法。
?????*/
????public?final?void?testGetFlatOrgIdByName()?{
????????fail("尚未實現(xiàn)");?//?TODO
????}
}
JUnit單元測試一共要注意一下幾點:(1)import junit.framework.TestCase 和 junit.textui.TestRunner;
(2)繼承junit.framework.TestCase ;
(3)自行添加一個main方法????中調(diào)用TestRunner.run(測試類名.class);
(4)有一個調(diào)用super(String)的構(gòu)造函數(shù);
??????? 以上都是JUnit必有的特征,除以上外,我們發(fā)現(xiàn)有許多以test開頭的方法,而這些方法正是我們要測試的方法,Junti測試其實采用的是斷言的方式,只要我們在所有test開頭中的方法對數(shù)據(jù)添加斷言方法,同時提供很多斷言的方法,
常用斷言方法 |
assertEquals("失敗提示信息","期望數(shù)據(jù)","測試數(shù)據(jù)") | 斷言獲取數(shù)據(jù)是否與所期望的相等 |
assertNotNull("失敗提示信息","測試數(shù)據(jù)") | 斷言獲取數(shù)據(jù)不為null,否則提示錯誤 |
assertNull("失敗提示信息","測試數(shù)據(jù)") | 斷言獲取數(shù)據(jù)是為null,否則提示錯誤 |
assertTrue("失敗提示信息",測試數(shù)據(jù)blooean值) | 斷言獲取數(shù)據(jù)是否為ture,否則提示錯誤 |
fail("失敗提示信息"); | 此方法一般放到異常處,遇到此方法,測試將停止! |
assertSame("失敗提示信息","期望數(shù)據(jù)","測試數(shù)據(jù)") | 斷言獲取數(shù)據(jù)是否與所期望的相同 |
當我們寫完所有方法策略后,JUnit測試如下圖:
在方法頁面中點擊右鍵在“調(diào)試方式”或“運行方式”中點擊“JUnit 測試”,就運行測試類!
在執(zhí)行測試類時,執(zhí)行的大概過程:
(1)先執(zhí)行構(gòu)造方法public OrgTypeDAOTest(String arg0) ;
(2)再執(zhí)行初始化數(shù)據(jù)方法protected void setUp() ;
(3)再執(zhí)行以test開頭的測試方法;
(4)最后執(zhí)行protected void tearDown()方法清理對象;
如果測試失敗或者錯誤,將會顯示一個紅色的亮條;如果測試通過將顯示綠色亮條;如下圖
?
這樣就把一個整個單元測試操作例子演示完成!
可能對于一個測試類中有多個方法要測試,對于后面看著的確有些困難,因此,我對上測試類進行簡單的調(diào)整,如下代碼:
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?;

????/**?
?????*??構(gòu)造方法
?????*?@param?arg0?
?????*/
????public?OrgTypeDAOTest(String?arg0)?{
????????super(arg0);
????}????

????/**
?????*初時化方法
??????*?@see?junit.framework.TestCase#setUp()
?????*/
????protected?void?setUp()?throws?Exception?{
????????super.setUp();
????????//測試初始話數(shù)據(jù)調(diào)用類?orgTypeDAO和?封裝數(shù)據(jù)的對象orgTypeVO
????}


????/**?執(zhí)行完清理方法
?????*?@see?junit.framework.TestCase#tearDown()
?????*/
????protected?void?tearDown()?throws?Exception?{
????????//清空?對象??;==null
????????//orgTypeDAO?=null;
????????//orgTypeVO?=null;
????????super.tearDown();
????}

????/**?
?????*??主函數(shù)
?????*?@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);
????????//測試是否重復數(shù)據(jù)方法(add)
????????isRepeatOrgTypeInfo(orgTypeVO.getName(),"");
????????//獲取數(shù)據(jù)方法(根據(jù)名稱)
????????selectOrgTypeIdByName(orgTypeVO.getName());
????????//刪除
????????deleteOrgTypeInfo(id);
????}
????/**
?????*添加初始數(shù)據(jù)
?????*/
????private?void?setOrgTypeVOAddInfo()?{
????????orgTypeVO.setName("add中海測試");
????????orgTypeVO.setDescription("add中海測試");
????????orgTypeVO.setStatus("1");
????}
????/**
?????*添加初始數(shù)據(jù)
?????*/
????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("添加通用組織機構(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("修改通用組織機構(gòu)失敗!",?false);
????????}
????????//查詢
????????orgTypeVO?=?orgTypeDAO.selectOrgTypeInfoById(id);
????????assertEquals("修改通用組織機構(gòu)失敗!",?orgTypeVO.getDescription(),?"update中海測試");
????}
????/**
?????*?獲取數(shù)據(jù)方法(主健)
?????*?Test?method?for?{@link?OrgTypeDAO#selectOrgTypeInfoById(java.lang.String)}.
?????*/
????public?void?selectOrgTypeInfoById(String?id)?{
????????orgTypeVO?=?orgTypeDAO.selectOrgTypeInfoById(id);
????????assertTrue("無法查看一條通用機構(gòu)名稱信息!",orgTypeVO?!=?null);
????????assertEquals("添加通用組織機構(gòu)失敗!",?orgTypeVO.getName(),?"add中海測試");
????????assertEquals("修改通用組織機構(gòu)失敗!",?orgTypeVO.getDescription(),?"update中海測試");
????}

????/**
?????*?測試此通用組織機構(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("通用組織機構(gòu)校驗錯誤!",isfalse);
????????}?catch?(DataAccessException?e)?{
????????????assertTrue("通用組織機構(gòu)數(shù)據(jù)操作錯誤!!",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("刪除通用組織機構(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("未查出來通用組織機構(gòu)ID!",orgtypeid);
????}
????/**
?????*?測試是否重復數(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("通用組織機構(gòu)錯誤數(shù)據(jù)",isTrue,?false);
????????assertTrue("通用組織機構(gòu)錯誤數(shù)據(jù)",?isTrue);
????}
????/**
?????*?列表方法
?????*?Test?method?for?{@link?com.zhjy.mltx.dao.OrgTypeDAO#orgTypeList()}.
?????*/
????public?void?orgTypeList()?{
????????List?list?=?orgTypeDAO.orgTypeList();
????????assertNotNull("無法獲取通用機構(gòu)名稱列表list?is?null!",list);
????}

}

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