大淘寶用戶平臺技術團隊單元測試建設
單元測試是工程交付前質量保障的第一環,也無疑是軟件工程質量保障的重要基石,有效的單元測試能夠提前發現90%以上的代碼Bug問題,同時也能防止代碼的腐化,在工程重構演進時起到至關重要的作用。
為什么需要單元測試
縱觀優秀的開源工程,完備的單元測試總是必須的條件。通過這些單元測試,我們可以充分了解代碼中相關類和方法的作用和核心邏輯,熟悉各種場景的運行情況。同時也因為有了單元測試,開源作者在接受各種feature的代碼提交時才有穩定安全的保障。其實單元測試的重要性所有開發同學應該都了然于胸,同樣TDD(測試驅動開發)也不是一個新的概念,但是真當我們落地實踐時,又總會找出各種各樣的理由來勸服自己下次一定好好寫單元測試,這一次先放過自己。這些理由無外乎,開發周期太緊了; 測試同學能保證功能正確性;寫單元測試代碼量比業務代碼還大; 又不是不能跑。所以雖然我們總是在追逐工程師文化,卻又時不時放縱在放棄工程師底蘊的路上。
單元測試是工程交付前質量保障的第一環,也無疑是軟件工程質量保障的重要基石,有效的單元測試能夠提前發現90%以上的代碼Bug問題,同時也能防止代碼的腐化,在工程重構演進時起到至關重要的作用。
怎么寫單元測試
? 好的單元測試的幾個要點
摘自阿里巴巴開發規約
- 單元測試必須遵守AIR原則,單元測試必須具備Automatic(自動化),Independent(獨立性),Repeatable(可重復)性;
- 單元測試應該是全自動執行的,并且非交互式的。測試用例通常是被定期執行的,執行過程必須完全自動化才有意義。輸出結果需要人工檢查的測試不是一個好的單元測試;
- 單元測試要保證測試粒度足夠小。單元測試測試粒度足夠小,有助于精確定位問題。單測粒度至多是類級別,一般是方法級別;
- 單元測試要遵守BCDE原則,Border,邊界值測試,包括循環邊界、特殊取值、特殊時間點、數據順序等;Correct,正確的輸入,并得到預期的結果;Design,與設計文檔相結合,來編寫單元測試;Error,強制錯誤信息輸入(如:非法數據、異常流程、非業務允許輸入等),并得到預期的結果;
- 核心業務、核心應用、核心模塊的增量代碼要確保單元測試通過;
? 單元測試編碼范式
這里主要以Mockito單元測試框架為模版
- Mock :
通過when().thenReturn/thenAnswer/thenThrow 或者doReturn().when()等mock方式將依賴類方法進行模擬,模擬服務依賴或者中間結果 - DO :
調用被測試類方法,執行測試鏈路 - Verify :
校驗執行結果正確性,通過Assert校驗數據結果準確,通過Verify校驗鏈路執行準確,通過expected=Exception.class校驗異常鏈路
public class Test {
// 0. 依賴類
@Mock
DependencyClass dependencyClass;
// 0. 待測試類
@InjectMocks
TestClass testClass;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testMethod() {
// 1. Mock, 依賴方法,構造中間層數據
when(dependencyClass.someMehod(any())).thenReturn(mockData());
// 2. Do, 調用被測試類
Result result = testClass.testMehod();
// 3. Verify, 校驗結果數據
Assert.assertEquals("some expected result string", result.getModel());
}
}
當然寫單元測試用例雖然套路比較模版化,但是我們也要充分利用單元測試框架(Junit/Mockito/PowerMock/Spock),掌握其中的一些技巧,才能寫出快準狠的單元測試用例,這也是研發同學必須要掌握的基本功。關于如何利用單元測試框架這里不再贅述(詳細可以參考阿里技術《Java編程技巧之單元測試用例編寫流程》)。
? 單元測試編碼提效
IDEA上有很多單元測試插件,能夠半自動化生成單元測試類文件,這里重點推薦TestMe插件。TestMe插件可以智能分析被測試類的依賴類,結合Mockito+Junit等單元測試框架,生成Mock/InjectMocks依賴關系,自動生成單元測試類。
假設業務代碼如下:
@Component
public class DefaultMemberManager implements MemberManager {
@Autowired
private MemberDAO memberDAO;
@Autowired
private CacheManager cacheManager;
@Override
public Date queryActivationTime(long userId) {
Date activationTime = cacheManager.getActivationTime(userId);
if (activationTime == null) {
MemberDO memberDO = memberDAO.queryByUserId(userId);
if (memberDO != null) {
cacheManager.saveActivationTime(userId, memberDO.getActiveTime());
activationTime = memberDO.getActiveTime();
}
}
return activationTime;
}
}
則通過TestMe快捷鍵COMMOND+N, 可以極速自動生成如下的單元測試類
public class DefaultMemberManagerTest {
@Mock
MemberDAO memberDAO;
@Mock
CacheManager cacheManager;
@InjectMocks
DefaultMemberManager defaultMemberManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testQueryActivationTime() throws Exception {
when(memberDAO.queryByUserId(anyLong())).thenReturn(null);
when(cacheManager.getActivationTime(anyLong())).thenReturn(
new GregorianCalendar(2022, Calendar.MARCH, 5, 23, 2).getTime());
Date result = defaultMemberManager.queryActivationTime(0L);
Assert.assertEquals(new GregorianCalendar(2022, Calendar.MARCH, 5, 23, 2).getTime(), result);
}
}
團隊單元測試建設
? 覆蓋率概念
覆蓋率是類JaCoCo插件通過javaagent掛載的方式,在單元測試命令運行時執行代碼覆蓋率檢測,計算單元測試執行過程中所覆蓋的代碼比例來生成覆蓋率。常見的覆蓋率指標,又可進一步細分為語句覆蓋率,條件覆蓋率,分支覆蓋率,路徑覆蓋率等。這里我們當前更為關注語句覆蓋率和分支覆蓋率,尤其是增量代碼的覆蓋率,更能體現變更代碼的單元測試覆蓋情況。
? 如何進行單元測試
這里我們借助于阿里研發平臺Aone的測試實驗室功能,Aone實驗室支持測試任務插件的編排組合,通過獨立的測試資源執行測試任務。所以我們將代碼拉取插件,單元測試插件和覆蓋率計算插件進行編排配置,形成最終的執行流:拉取代碼;執行單元測試命令;單元測試結果解析;計算覆蓋率。最終完成整個工程的單元測試覆蓋率計算。
單元測試覆蓋率結果示例如下:
? 什么時候觸發單元測試
單元測試任務主要通過持續交付流水線pipeline來集成,當前幾個主要觸發策略如下:
- 代碼提交時,保證單元測試執行及時性
- 代碼審核時,保證代碼審核通過的代碼分支符合單元測試標準
- 發布流程中,保證最終集成發布的所有分支代碼符合單元測試標準
? 單元測試覆蓋率卡點
用戶平臺技術團隊單元測試規范如下:
- 單元測試用例通過率為100%
- 單元測試增量代碼行覆蓋率為85%
- 代碼規范掃描增量問題總數為0個
? 單元測試覆蓋率報表
為了更好的衡量單元測試的覆蓋率情況,我們采用報表的形式統計每個應用,每個團隊的代碼單元測試覆蓋率。
總結
當前團隊內各應用(除邊緣應用外)的單元測試增量代碼覆蓋率在2022年已經全部達到85%標準,最新平均增量代碼行覆蓋率達到88%,整體全量代碼覆蓋率平均提高20%。誠然單元測試覆蓋率的提高不是最終的目的,覆蓋率高不能完全代表工程質量高,但是一個沒有單元測試或者單元測試覆蓋率低的工程,其代碼質量和穩定性必然不高。同時團隊內研發同學對單元測試也有了新的認識,自測和提測質量顯著提升,全年未發生由于代碼質量產生的線上故障,有效提升了工程質量和服務穩定性。
后續規劃,持續優化單元測試質量,提升分支覆蓋率,優化邊界異常覆蓋;關注單元測試編碼效率的提升,優化測試用例和測試數據分離;關注核心鏈路單元測試覆蓋率;熟練將TDD思維運用到業務開發過程中。
團隊介紹
大淘寶技術-用戶平臺技術團隊是一支集研發、數據、算法一體的團隊,負責淘系的用戶增長,游戲互動,平臺會員和私域運營等消費者核心業務。團隊承擔捍衛電商主板塊增長的重要使命,是阿里核心電商戰場的參與者,用持續的技術創新來驅動阿里電商引擎的穩步前行。
這是一支年輕開放的團隊,在這里你將收獲超大規模高并發場景的架構設計能力,洞悉用戶增長最前沿的實踐方法,在數字化時代收獲最核心的競爭力。團隊技術氛圍濃厚,倡導創新和工程師文化,鼓勵用數據和代碼發現解決問題。團隊研發流程規范,代碼質量高,學習成長速度快。