代碼測試(CodeTest)
代碼測試的立足點是Code,是基于代碼基礎(chǔ)之上的,而傳統(tǒng)的功能測試和接口測試是基于應(yīng)用的,必須對應(yīng)的測試系統(tǒng)是在運行中的。
代碼測試不會特別注重接口測試的可持續(xù)性集成。
代碼測試的特點是快捷高效準(zhǔn)確的完成測試工作,快速推進產(chǎn)品的迭代。
2. Code Test 的方法:
(1) 代碼走讀和review
適合場景:邏輯相對簡單,有較多的邊界值。
方法介紹:直接查看和閱讀代碼,檢驗邏輯是否正確。
(2) 代碼debug與代碼運行中測試
適合場景:數(shù)據(jù)構(gòu)造比較困難,特殊的場景覆蓋。
方法介紹:1.直接在debug代碼過程中查看數(shù)據(jù)流走向,校驗邏輯。
2.在debug過程中直接將變量的值或者對象的值直接改成想要的場景
(3) 私有方法測試
適合場景:需要測試的類的復(fù)雜邏輯處理是放在一個特定的方法中,而且該方法中沒有使用到其他引用的bean
方法介紹:通過反射的方式調(diào)用方法,進行測試。
例子:
假設(shè)有一個待測試的類叫MyApp,有一個私有方法叫g(shù)etSortList, 參數(shù)是一個整形List。
/** * Created by yunmu.wgl on 2014/7/16. */ public class MyApp { private List getSortList(List<Integer> srcList){ Collections.sort(srcList); return srcList; } } |
那么測試類的代碼如下:
/** * Created by yunmu.wgl on 2014/7/16. */ public class MyAppTest { @Test public void testMyApp() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class clazz = MyApp.class; Method sortList = clazz.getDeclaredMethod("getSortList",List.class); //獲取待測試的方法 sortList.setAccessible(true); //私有方法這個是關(guān)鍵 List<Integer> testList = new ArrayList<Integer>();//構(gòu)造測試數(shù)據(jù) testList.add(1); testList.add(3); testList.add(2); MyApp myApp = new MyApp(); // 新建一個待測試類的對象 sortList.invoke(myApp,testList); //執(zhí)行測試方法 System.out.println(testList.toString()); //校驗測試結(jié)果 } } |
(4) 快速搭建測試腳本環(huán)境
適合場景:待測試的方法以hsf提供接口方法,或者需要測試的類引入了其他bean 配置。
方法介紹:直接在開發(fā)工程中添加測試依賴,主要是junit,如果是需要測試hsf接口,則加入hsf的依賴,如果需要使用itest的功能,加入itest依賴。
Junit 的依賴一般開發(fā)都會加,主要看下junit的版本,最好是4.5 以上
HSF的測試依賴:以前的hsfunit 和hsf.unit 最好都不要使用了。
<dependency>
<groupId>com.taobao.hsf</groupId>
<artifactId>hsf-standalone</artifactId>
<version>2.0.4-SNAPSHOT</version>
</dependency>
Hsf 接口測試代碼示例:
// 啟動HSF容器,第一個參數(shù)設(shè)置taobao-hsf.sar路徑,第二個參數(shù)設(shè)置HSF版本
HSFEasyStarter.start("d:/tmp/", "1.4.9.6");
String springResourcePath = "spring-hsf-uic-consumer.xml";
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(springResourcePath);
UicReadService uicReadService = (UicReadService) ctx.getBean("uicReadService");
// 等待相關(guān)服務(wù)的地址推送(等同于sleep幾秒,如果不加,會報找不到地址的錯誤)
ServiceUtil.waitServiceReady(uicReadService);
BaseUserDO user = uicReadService.getBaseUserByUserId(10000L, "detail").getModule();
System.out.println("user[id:10000L] nick:" + user.getNick());
Hsf bean的配置示例:
<beans>
<bean name="uicReadService" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean"
init-method="init">
<property name="interfaceName" value="com.taobao.uic.common.service.userinfo.UicReadService" />
<property name="version" value="1.0.0.daily" />
</bean>
</beans>
Itest的依賴:這個版本之前修復(fù)了較多的bug。
<dependency>
<groupId>com.taobao.test</groupId>
<artifactId>itest</artifactId>
<version>1.3.2.1-SNAPSHOT</version>
<dependency>
(5) 程序流程圖校驗
適合場景:業(yè)務(wù)流程的邏輯較為復(fù)雜,分支和異常情況很多
方法介紹:根據(jù)代碼邏輯畫出業(yè)務(wù)流程圖,跟實際的業(yè)務(wù)邏輯進行對比驗證,是否符合預(yù)期。
(6) 結(jié)對編程
適合場景:代碼改動較小,測試和開發(fā)配對比較穩(wěn)定
方法介紹:開發(fā)修改完代碼后,將修改部分的邏輯重復(fù)給測試同學(xué),測試同學(xué)review 開發(fā)同學(xué)講述的邏輯是否和代碼的邏輯一致。
3. 具體操作步驟:
(1) checkout代碼,在接手項目和日常后第一件事情是checkout 對應(yīng)的應(yīng)用的代碼
(2) 了解數(shù)據(jù)結(jié)構(gòu)與數(shù)據(jù)存儲關(guān)系:了解應(yīng)用的數(shù)據(jù)對象和數(shù)據(jù)庫的表結(jié)構(gòu)及存儲關(guān)系。
(3) 了解代碼結(jié)構(gòu), 主要搞清楚代碼的調(diào)用關(guān)系。
(4) 了解業(yè)務(wù)邏輯和代碼的關(guān)系:業(yè)務(wù)邏輯肯定是在代碼中實現(xiàn)的,找到被測試的業(yè)務(wù)邏輯對應(yīng)的代碼,比較常見的是通過url 或者接口名稱等。
如果是webx框架的可以根據(jù)http請求找到對應(yīng)的代碼,如果是其他框架的也可以通過http請求的域名在配置文件中找到對應(yīng)的代碼。
(5) 閱讀相關(guān)代碼,了解數(shù)據(jù)流轉(zhuǎn)過程。
(6) Review 代碼,驗證條件,路徑覆蓋。
(7) 復(fù)雜邏輯可以選用寫腳本測試或者私有方法測試,或者畫出流程圖。
4. 代碼測試的常見測試場景舉例:
(1) 條件,邊界值,Null 測試:
復(fù)雜的多條件,多邊界值如果要手工測試,會測試用例非常多。而且null值的測試往往構(gòu)造數(shù)據(jù)比較困難。
例如如下的代碼:
if (mm.getIsRate() == UeModel.IS_RATE_YES) { float r; if (value != null && value.indexOf(‘.’) != -1) { r = currentValue – compareValue; } else { r = compareValue == 0 ? 0 : (currentValue – compareValue) / compareValue; } if (r >= mm.getIncrLowerBound()) { score = mm.getIncrScore(); } else if (r <= mm.getDecrUpperBound()) { score = mm.getDecrScore(); } mr.setIncreaseRate(formatFloat(r)); } else { // 目前停留時間為非比率對比 if (currentValue – compareValue >=mm.getIncrLowerBound()) { score = mm.getIncrScore(); } else if (currentValue – compareValue <= mm.getDecrUpperBound()) { score = mm.getDecrScore(); } } |
(2) 方法測試
某個方法相對比較獨立,但是是復(fù)雜的邏輯處理
例如下面的代碼:
private void filtSameItems(List<SHContentDO> contentList) { Set<Integer> sameIdSet = new TreeSet<Integer>(); Set<Integer> hitIdSet = new HashSet<Integer>(); for (int i = 0; i < contentList.size(); i++) { if (sameIdSet.contains(i) || hitIdSet.contains(i)) { continue; } SHContentDO content = contentList.get(i); List<Integer> equals = new ArrayList<Integer>(); equals.add(i); if ("item".equals(content.getSchema())) { for (int j = i + 1; j < contentList.size(); j++) { SHContentDO other = contentList.get(j); if ("item".equals(other.getSchema()) && content.getReferItems().equals(other.getReferItems())) { equals.add(j); } } } if (equals.size() > 1) { Integer hit = equals.get(0); SHContentDO hitContent = contentList.get(hit); for (int k = 1; k < equals.size(); k++) { SHContentDO other = contentList.get(k); if (hitContent.getFinalScore() == other.getFinalScore()) { long hitTime = hitContent.getGmtCreate().getTime(); long otherTime = other.getGmtCreate().getTime(); if (hitTime > otherTime) { hit = equals.get(k); hitContent = other; } } else { if (hitContent.getFinalScore() < other.getFinalScore()) { hit = equals.get(k); hitContent = other; } } } for (Integer tmp : equals) { if (tmp != hit) { sameIdSet.add(tmp); } } hitIdSet.add(hit); } } Integer[] sameIdArray = new Integer[sameIdSet.size()]; sameIdSet.toArray(sameIdArray); for (int i = sameIdArray.length - 1; i >= 0; i--) { contentList.remove((int) sameIdArray[i]); } } |
posted on 2014-10-30 10:46 順其自然EVO 閱讀(592) 評論(0) 編輯 收藏 所屬分類: 測試學(xué)習(xí)專欄