生活随笔
收集整理的這篇文章主要介紹了
持续集成实践小结[2] —单元测试
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
前文提到,在UI自動化之外,我們著力探索了如何實(shí)施
單元測試(unit?
test) 相對于UI自動化,單元測試方面的實(shí)踐還是不夠充分的,因此,這里也只是小結(jié)一下我們的經(jīng)驗(yàn)
概述 首先明確一下,此處單元測試概念與經(jīng)典意義有所不同,泛指所有: 由開發(fā)工程師編寫的,可以在開發(fā)本地一鍵運(yùn)行的,運(yùn)行時(shí)間在分鐘級別的
測試用例,用例執(zhí)行會依賴不多的,但往往也是穩(wěn)定可靠的外部環(huán)境 測試框架一般使用TestNg而不是
JUnit,主要原因在于TestNg的 DataProvider 功能很給力,非常適合用例須要覆蓋多分支的場景
用例組織原則: 一個(gè)測試類對應(yīng)一個(gè)功能類: funcOneTest.java 對應(yīng)于 funcOne.java 若干個(gè)測試方法對應(yīng)一個(gè)功能方法:test_funcOne_smoke() & test_funcOne_normal() & test_funcOne_error() 對應(yīng)于 funcOne() 用例分類約定 還是以 超市購物 為背景,寫幾個(gè)Demo用例
Δ冒煙型用例 – 甲 | @Test(description = "getNewestItems_冒煙_獲取最新商品并檢查若干關(guān)鍵屬性") public void test_getNewestItems_smoke() { List<ItemVo> itemList = itemBean.getNewestItems(1); Assert.assertTrue(itemList.size() == 16, ? ? ? ?"size應(yīng)該是16"); for (ItemVo vo : itemList) { Assert.assertTrue(vo.getName() != null, ? ? "name不能為空"); Assert.assertTrue(vo.getPrice() != null, ? ?"price不能為空"); } } |
說明: 顧名思義,就是在用例中簡單調(diào)用一下被測方法(method),主要是為了跑通流程,拒絕Block級別問題 實(shí)踐中,推薦 伴隨著功能開發(fā),隨時(shí)編寫冒煙用例,功能代碼與冒煙用例一起提交代碼庫 對于一些復(fù)雜的功能,功能開發(fā)的過程也是一個(gè)持續(xù)重構(gòu)的過程,編碼前期完成的冒煙用例,可以有效起到安全網(wǎng)作用
Δ冒煙型用例 – 乙 | @Test(description = "enter_and_leave_market_冒煙_進(jìn)入與離開超市") public void test_enter_and_leave_market_smoke() { Custom tom = new Custom("Tom"); tom.enterMarket(); Assert.assertTrue(Custom.isAtMarket(tom), ? "tom應(yīng)該在超市內(nèi)"); tom.leaveMarket(); Assert.assertFalse(Custom.isAtMarket(tom), ?"tom應(yīng)該不在超市"); } |
說明: 這個(gè)用例把進(jìn)入超市及離開超市這兩個(gè)(強(qiáng)相關(guān)的)接口串起來了,目的在于走通流程
實(shí)踐中,建議 將新模塊(函數(shù))與其強(qiáng)相關(guān)的模塊(函數(shù))盡早進(jìn)行簡單的集成測試以提前發(fā)現(xiàn)一些問題Δ正常流程用例 | @Test(description = "addToCart_正常流程_往購物車內(nèi)添加各種類型數(shù)目的商品", dataProvider = "test_addToCart_normal_data") @Rollback public void test_addToCart_normal(String caseNote, long itemId, int count) { Custom tom = new Custom("Tom"); this.setCustom(tom); cartBean.addToCart(itemId, count); Item item = tom.getCart.getItems.get(0); ? ?// 獲取購物車中的第一項(xiàng)商品 Assert.assertEquals(item.getId, ? ? ?itemId, ?"itemId is wrong"); Assert.assertEquals(item.getCount, ? count, ? "count is wrong"); } @DataProvider public Object[][] test_addToCart_normal_data() { return new Object[][] { // caseNote, ? ? ? ? ? ? ? ? ? ?itemId, ? ? count {"Milk ?- just a dozen", ? ? ? ?39001L, ? ? 12, ?}, {"Bread - huge number", ? ? ? ? 116001L, ? ?999}, {"Bean ?- less then 10", ? ? ? ?1018100L, ? 2}, }; } |
說明: 這個(gè)示例代碼演示了如何測試addToCart()的功能,假定這個(gè)方法內(nèi)部有十分復(fù)雜的業(yè)務(wù)邏輯,我們須要覆蓋各種場景 可以看到,正常流程用例與冒煙用例其實(shí)差不多,不同的是,正常流程用例會覆蓋更多分支,冒煙用例則一般是走通流程就行 這個(gè)用例使用了@Rollback標(biāo)簽,用例執(zhí)行后會回滾數(shù)據(jù),而不會真正往數(shù)據(jù)庫內(nèi)插入數(shù)據(jù);這個(gè)功能十分有用,可以大大減少數(shù)據(jù)準(zhǔn)備與清理的工作;至于@Rollback背后的實(shí)現(xiàn)原理,此處暫時(shí)按下不表 測試參數(shù)使用TestNg的@DataProvider組織起來,每一行都是一組測試數(shù)據(jù),覆蓋一種測試分支 測試參數(shù)的第一列建議設(shè)置為caseNote,簡單闡述用例的意圖,可以有效提升用例可讀性
Δ異常流程用例 | @Test(description = "addToCart_異常流程_往購物車內(nèi)添加參數(shù)非法的商品", dataProvider = "test_addToCart_error_data") @Rollback public void test_addToCart_error(String caseNote, long itemId, int count, int expectedErrorCode) { Custom tom = new Custom("Tom"); this.setCustom(tom); try { cartBean.addToCart(itemId, count); Assert.fail(); } catch (Exception e) { Assert.assertEquals(e.getErrorCode, expectedErrorCode); } } @DataProvider public Object[][] test_addToCart_error_data() { return new Object[][] { // caseNote, ? ? ? ? ? ? ? ? ? ? ? ?itemId, ? ? count, ?expectedErrorCode {"iPad ? ? ? - 0 count", ? ? ? ? ? ?39001L, ? ? 0, ? ? ?Cart.ZERO_COUNT}, {"MacBookPro - more then stock", ? ?116001L, ? ?1024, ? Cart.MORE_THAN_STOCK}, {"no such item", ? ? ? ? ? ? ? ? ? ?0L, ? ? ? ? 1L, ? ? Cart.NO_SUCH_ITEM} }; } |
說明: 如果不同的異常輸入會有相應(yīng)的 errorCode 的話, 可以把errorCode當(dāng)成測試數(shù)據(jù)的一項(xiàng)參數(shù)傳進(jìn)去Δ特殊場景 & 復(fù)雜流程 個(gè)別特殊場景,不方便使用@DataProvider合并的復(fù)雜流程,可以單獨(dú)創(chuàng)建一個(gè)用例進(jìn)行測試,函數(shù)命名的時(shí)候注明一下,例如: test_addToCart_error_withoutEnoughMoney() – 鈔票不夠 test_addToCart_normal_mergeMultiCarts() – 合并多個(gè)購物車 Δ前事不忘,后事之師 出過Bug的地方(及其周邊)補(bǔ)充單元測試覆蓋,由單元測試幫你記住前事 – test_funcOne_issue12345_bugfix()
用例編寫流程 用例編寫順序: 開發(fā)新功能時(shí),同步編寫冒煙測試,用于自測及調(diào)試,功能代碼與冒煙用例一起提交 – test_funcOne_smoke() 稍晚,補(bǔ)充更多分支覆蓋的正常流程用例,相當(dāng)于進(jìn)行又一輪自測 – test_funcOne_normal() 最后,補(bǔ)充異常流程和特殊(復(fù)雜)場景用例 – test_funcOne_error() & test_funcOne_specialScenario() 之所以這樣安排,一個(gè)很重要的原因是希望 保持主干及正常流程的暢通,確保開發(fā)及測試不會Block
此外: 盡量把單個(gè)開發(fā)任務(wù)切分成多個(gè)小功能點(diǎn),頻繁提交,穩(wěn)扎穩(wěn)打,配合Jenkins & Sonar,多跑單元測試和靜態(tài)代碼檢查,問題早發(fā)現(xiàn)早處理 前事不忘,后事之師,出過Bug的地方補(bǔ)充單元測試
補(bǔ)遺 單元測試與靜態(tài)代碼檢查(static analysis, SA)是一對好基友,兩者可以統(tǒng)一顯示在Sonar上面,在實(shí)踐中往往一起考察 關(guān)于Sonar,實(shí)乃居家必備,代碼度量之利器,以后會另外講述,這里先貼個(gè)圖:
最新內(nèi)容請見作者的GitHub頁:http://qaseven.github.io/
總結(jié)
以上是生活随笔為你收集整理的持续集成实践小结[2] —单元测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。