目錄
🌼前言
一,實習經歷怎么寫簡歷
🌹業務理解
🎂結構化表達
二,實習
🦂技術和流程卡點
🔑實習收獲 / 代碼風格
三,測試理論,用例設計,工具鏈
🌳測試理論 / 用例設計
🍌工作內容
四,交互式 / 非交互式自動化測試實踐
📱交互式自動化測試
👖非交互式自動化測試
📕手動確定覆蓋率
五,測試工程構建 / 主要業務
🐎測試工程構建
🐘主要業務(API 的二次開發)
概念
客戶如何工作
API 和 平臺的關系
六,測試分級 / bug 排查 / 單元測試規范
🐱測試用例分級
🐉BUG排查 / 定位
🐒測試模式
🏢單元測試規范
🍑優秀的單元測試
🌼單元測試細節 / 項目單測流程
七,代碼質量問題案例
🌊案例
🌼POD 對象
🌼前言
3個月的實習,之前就面了 15 分鐘,閑聊 5 分鐘,隔天發的 offer
畢竟只是任務重,招點實習生來打雜,以下總結下實習經驗
一,實習經歷怎么寫簡歷
🌹業務理解
①
業務全套首先要弄清楚,因為面試官不了解這個行業,你能把前前后后的產品邏輯講清楚,他就覺得你學習能力還不錯
②
結合公司文檔,將公司文檔中,一些和自己平時工作有關聯的東西,加以記錄和理解
(不是在內網拷貝,而且用自己話,記錄下來)
后面面試時,能講清楚邏輯和細節,就行
比如 RAII 機制的一些宏,具體怎么實現的,涉及 C++11 的哪些特性,解決了什么問題等等
③
實習中,一整套的工作流程是怎樣的,有什么比較熱門的技術,在日常的功能測試,交互式,非交互式測試中使用了
④
或者,你平時測試的那些 API 接口,在整個業務的上下游,整天的鏈路邏輯,你處于哪個位置等
⑤?
實習的一些收獲,比如干活前,需要明確需求和 ddl,防止返工,大膽問
或者怎么處理分支沖突,以及 Git Extension, GitLab 的使用,還有宏失敗了怎么處理
🎂結構化表達
?①?
實習經歷分:工作職責 / 技術和框架 / 實習收獲,三個方面去寫
②
比如使用 C++ Catch2 測試框架,GenCMake 自動添加 CMake,.mac 宏錄制和自動化測試跑宏,遇到一個新的接口怎么區測試的完整流程
③?
實習做的東西,比如寫了 100+ 不同類型接口的測試用例,完成 30 個接口覆蓋率的提升,發現 9 個接口 BUG,3 天時間熟悉產品的基本操作
二,實習
🦂技術和流程卡點
①?
技術上,最開始的宏錄制,容易失敗,因為存在無關操作,比如點擊了無關的點,縮放了屏幕大小,覆蓋已保存的文件;
或者,沒有區分不同類型的變量,誤判了父類型和子類型等等
②?
測試流程上,不熟悉整個從 VS 檢索接口注釋,到平臺嘗試復現 API 功能,再到 VScode 搜索歷史代碼的整個流程
③?
出現分支沖突,不知道如何處理,比如常見的 commit, pull merge, fetch all,以及 stash, stash pop 等;
分支沖突時可以注釋他人的代碼;或者詢問 mentor 誰修改了這個文件
④
部門溝通上,你要和產品,測試,開發進行對接,需要頻繁的溝通,以保證自己的工作效率(比如人均 3 次 / 天)
某個功能點,產品比你了解的更清楚;某個接口的具體實現,問開發也是最好的選擇
比如接口返回值,位于平臺哪里,函數調用需要注意的點,某些沒跑進覆蓋率的代碼實現
⑤ 遇到困惑的點,積極跟 mentor 溝通,不要閉門造車
🔑實習收獲 / 代碼風格
①?
技術上,熟悉了 Catch2 的部分操作,學會處理分支沖突,宏失敗怎么排查,以及記錄遇到的問題避免重復提問,還有 P1,P2 功能性+無效等價類+邊界+異常場景的測試用例怎么寫
②?
代碼風格上,首先,避免使用 new / delete, malloc / free 這一套,容易忘記釋放導致內存泄露,而應該使用 C_AUTO 類宏,來聲明指針,數組,句柄,鎖等;
但是部分接口,需要接收已有的地址,就不能用 C_AUTO 宏來初始化,而應該使用 C++ 自帶的初始化,比如 svxDeskFace deskFace{};
③
頻繁被調用的代碼,可以封裝為函數,放到公用的頭文件里
④?
代碼中,注釋不要太多,但是基本的接下來上百行代碼,別人知道你在干什么就行,比如 Catch2 的 WHEN("niu bi plus")
三,測試理論,用例設計,工具鏈
🌳測試理論 / 用例設計
①
從公司文檔學習:白盒測試,黑盒測試,測試流程,測試用例的設計過程
②?
講講你們自動化測試這一塊的內容
③
實習時,git 如何協作
④?
公司的工具鏈是怎樣的
🍌工作內容
①?
主要工作:API 二次開發自動化測試
②?
負責公司 API 的自動化測試,優化覆蓋率,確保 API 功能與平臺一致
③?
使用 C++ 和 Catch2 框架,進行單元測試和集成測試
④?
使用 Git 進行版本控制,參與 GitLab 分支管理,合并分支沖突
⑤
通過錄宏 / 跑宏,交互式 / 非交互式相結合的方式測試
⑥
在 GitLab 登記問題,提供詳細復現步驟和日志文件,便于開發快速定位問題
四,交互式 / 非交互式自動化測試實踐
📱交互式自動化測試
①
利用公司平臺已有的宏測試系統實現自動化測試
②
優點:測試上下文易構建,通過平臺操作,測試場景容易擴展,直接在平臺添加測試宏
缺點:交互式只能構建正常場景;測試接口容易受其他模塊干擾;不貼合客戶真實使用場景
③
適用場景:平臺命令封裝類接口;UI 界面相關的顯示接口
👖非交互式自動化測試
①
使用 Catch2 單元測試框架實現
②?
優點:貼合客戶使用場景;更容易實現參數層面的枚舉
缺點:
測試上下文難構建,只能調用其他 API 進行
部分入參難以確定
部分命令場景,對入參有要求時,難以使用現有 API 指定
③
適用場景:數據查詢類接口,數學計算類接口
📕手動確定覆蓋率
①
當 OpenCppCoverage 的覆蓋率還未更新,需要手動獲取覆蓋率時
②?
可以在測試用例中,每個調用 API 的位置,打斷點,f11,step into 源碼
③
找到 API 的實現后,在每個小分支的入口處,都打上斷點,然后針對測試用例中調用的 API,不斷 step into,在源碼中 continue
④
每跑進源碼的一個小分支,就取消分支入口處斷點
⑤
最后源碼中尚未取消的斷點,就是源碼未覆蓋的分支,然后針對未覆蓋分支處調用的其他 API 和 參數,找開發問一下
五,測試工程構建 / 主要業務
🐎測試工程構建
①
類似啟動 vs 工程時,雙擊 .sln 的操作:雙擊 generate.bat 構建出對應的 msbuild 工程并啟動 VS2019
②
一個測試用例的構建包括:
a. 測試場景的初始化(測試上下文的構建
b.?執行被測試的 API 接口c. 對測試結果斷言檢查
③
對于 Catch2 測試框架,平臺的運行環境屬于全局變量,聲明周期存在于整個測試流程中,所以每個測試場景運行結束時,無法自動還原環境,需要手動還原
🐘主要業務(API 的二次開發)
概念
①
二次開發,就是在現有軟件的基礎上,進行定制修改和功能擴展,實現自己想要的功能
②
客戶需要借助二次開發的 API 進行開發
③
這個 API 接口可以理解為,根據客戶的要求,將平臺內部的某個函數,再做一層封裝和處理后,再給用戶使用
④
二次開發的 API 接口,一般對應平臺的一個功能或子功能,而對于客戶來說,他只需要知道 API 的函數名,功能,以及輸入輸出
客戶如何工作
①
客戶在開發環境中,引用二次開發?API 的頭文件(聲明),鏈接靜態庫 .lib(實現),就可以直接使用這批接口
②
客戶通過這批 API,實現自己的功能,并且把代碼封裝成一個函數,將函數注冊成自定義命令,然后編譯生成動態庫(.dll),也就是二次開發插件
③
接著運行我們的平臺,加載 .dll 插件,就能使用用戶自定義的功能了
API 和 平臺的關系
①
API 測試一般對應界面上某個功能,與平臺交互就能測試;
或者通過調用平臺內部的功能函數進行測試
所以對 API 進行測試,也能測到平臺的功能
②
API 測試用例失敗,可能是二次開發 API 的問題,也可能是對應功能的內部函數出了問題
③
平臺命令 / 底層函數的變更,也會對 API 產生影響
a. 顯示影響:api 對應某個命令,命令發生了變化,比如增加了入參選項,對應的 api 也要同步更新
b. 隱式影響: api 使用了某個內部函數 或 底層機制,額你不函數邏輯變更,或機制變更
④
在不確定項目對 api 是否有影響時
a. 開發要主動向 api 開發進行確認
b. 測試也要提升 api 的覆蓋率,并且及時全量地運行 api 自動化測試,檢測項目是否引入 api 問題(master 主干分支,每周進行集成測試)
六,測試分級 / bug 排查 / 單元測試規范
🐱測試用例分級
① 功能性:有效等價類(輸入性 + 功能性),邊界,對平臺運行環境的影響
? a. 有些接口會影響全局變量,導致當前接口運行正常,但是其他測試用例因此失敗
②?健壯性:無效等價類 + 其他常見錯誤異常場景
? a.
接口會針對不同的錯誤場景,返回不同的錯誤碼
一類錯誤碼:客戶經常犯的(非法輸入),便于客戶調試問題
另一類錯誤碼:針對接口內部或平臺底層代碼的處理,出現異常(內存錯誤,讀取對象數據失敗),便于開發排查問題
? b. 還要根據接口本身的功能,測試的經驗,反推可能存在的錯誤場景
③ 易用性:命令規范,接口易用,注釋正確
④ 兼容性
⑤ 效率測試
⑥ 回歸測試
🐉BUG排查 / 定位
①
逐步排查接口 bug 時,不要用 for,會增大排查難度
②
復現問題,定位問題(用例 / 環境 / 接口實現 / 平臺功能),查閱文檔和源碼,覆蓋率分析和斷點測試,和開發產品測試溝通,猜想與驗證,查看日志,內存檢測
③
特別注意:接口的依賴關系 + 前置條件 + 易混淆類型
🐒測試模式
① 傳統測試模式
使用 TEST_CASE 定義單元測試
(test_name [,tags]) {}
test_name:測試用例名稱,任意的,需要全局唯一
tags:標簽名,用 [] 將每個標簽抱起來,逗號分隔,不用全局唯一
② BDD模式
行為驅動開發(behaviour driven development),通過在 GIVEN,WHEN,THEN 中,自然語言書寫測試用例的信息,擴展了測試驅動開發的方法
Ⅰ 其中,GIVEN 前提條件 = 測試場景初始化 + 構建入參
Ⅱ 然后,WHEN 做的事情 = 執行被測試 api
Ⅲ 最后,THEN 結果 = 對 api 調用后的返回值,入參,出餐進行檢測
🏢單元測試規范
①?
平臺的測試案例使用 BDD-style,一個完整的單元測試案例包括
Arrange(初始化),Action(執行),Assert(斷言)
②
編碼原則:邊界,正確輸入,設計與需求相結合,錯誤輸入,預期結果
③
編碼規范:每個文件只對應一個接口? +? 所有單元測試案例必須有描述
④ 測試維度
接口功能性(接口的正確性 + 一定條件下正常被調用 + 返回預期結果)
局部數據結構(變量是否有初始值 + 變量是否溢出)、
邊界(指針 + 數值 + 字符串 + 集合)
異常處理(異常可否被識別 + 識別后是否有處理)
⑤
使用 OpenCppCoverage 工具,分析測試用例對代碼的覆蓋率
🍑優秀的單元測試
a. 自動化的
b. 很容易實現
c. 第二天還有意義
d. 任何人都可以一鍵運行
e. 運行速度很快
f. 結果穩定,多次運行同一個測試用例,總是返回相同的結果
g. 能夠完全控制被測試的單元
h. 完全隔離的(不依賴其他所有測試用例)
i. 如果它失敗了,可以很容易發現什么是預期的結果,可以很快定位問題所在
🌼單元測試細節 / 項目單測流程
單元測試細節
a. 底層遺留代碼的測試:靜態測試(開發自己代碼走查)+? 動態測試(編寫測試用例)
b. 是否為所有共有接口編寫用例:資源有限時,有限覆蓋高頻,關鍵,危險接口
c. 推動開發改造接口:剔除非必要接口,提高接口容錯能力,重構復雜接口,配套demo測試
d. 測試方案:確定子模塊測試順序,獲取接口清單 和 調用頻次,重點關注危險行為
e. 測試過程:對照接口標注和注釋,檢查邏輯完整性,異常處理,全局變量使用,邊界等
f. 測試用例編寫:結合黑盒 / 白盒,邏輯覆蓋,邊界,錯誤場景補充;模擬平臺實際調用,發揮想象力設計異常輸入
項目單測流程
Ⅰ 輸入:項目核心文檔 + 設計方案
Ⅱ 輸出:測試方案 + 測試報告 + 用例代碼
Ⅲ 執行流程
a. 初步擬定測試方案,用例清單
b. 用例評審(上級,項目經理,項目開發)
c. 搭建測試環境,測試場景構建
d. 引入開發代碼,運行測試用例
e. 反饋測試結果,bug 跟蹤,生成測試報告
f. 開發人員處理 bug 后,進行回歸測試
g. 測試代碼集成(開發源碼集成后) -- 集成:多個組件模塊合并到同一個系統
h. 測試用例加入主干定期測試
七,代碼質量問題案例
🌊案例
① 超大文件
單個源文件幾千行,很難維護和理解,如果某個超大的 .cpp,被多個人同時修改,合并沖突的概率就會大大增加,影響開發效率
② 超長函數
函數超過 200 行,邏輯復雜,難以測試和復用,如果某個超長函數被修改,容易引發連鎖 bug,導致回滾上線
void processOrder() {// ... 500行代碼 ...}
③ 超長的類
類中成員變量和方法過多,違反單一職責原則,最好拆分為多個小的類,按功能聚合
④ 單個函數多個職責
一個函數既做數據校驗,又做業務處理,還做日志記錄
void handleUserRequest() {validateInput();processBusiness();logOperation(); } // 建議拆分為 validate(), process(), log() 三個函數
⑤ 需要頻繁更新的函數
頻繁變動的函數,容易引入回歸 bug,如果某個函數的邏輯頻繁變動,未做好單元測試,容易導致線上事故
⑥ 不必要的接口依賴
如果模塊 A 依賴模塊 B 內部的實現,導致 A 的變更影響 B,最好進行接口隔離,減少耦合
⑦ 不必要的實現依賴
如果項目直接依賴第三方庫的實現細節,升級時風險很大,容易產生大量編譯錯誤
⑧ 逆模塊依賴的接口調用
如果低層模塊反向依賴高層模塊,會破壞分層結構
⑨ 模塊私有接口被外部調用
本應只在模塊內部使用的函數,被外部調用,導致維護困難
⑩ 濫用全局變量
全局變量被多處修改,難以追蹤和調試,比如被多線程同時修改,導致數據錯亂
① 不必要的局部靜態變量
靜態變量聲明周期長,容易引發資源泄露或線程安全問題
② 難理解的名稱縮寫
比如 tmp, foo, bar
③ 抽象的函數命名
比如 doTask(), handle(),無法體現具體功能
④ 不必要的模板編程
追求泛型而濫用模板編程,導致代碼復雜難懂,新人難以上手維護
⑤ 無意義的函數注釋
注釋應說明返回值,入參,出參,邊界,注意事項等
// this is a function
⑥ 過長的參數列表
函數參數不要超過 5 個,否則易出錯,考慮封裝為結構體或對象傳參
⑦ 缺乏單元測試
無單元測試,線上熱修復容易引發故障
⑧ 重復代碼
同樣的邏輯在多個地方實現,維護成本高,最好提取為公共函數或工具類
⑨ 通用處理方法--插入大量針對個別對象的特殊處理邏輯
如,主流程代碼被特殊 case 污染,導致難以閱讀和修改
⑩ 對非 pod 對象進行裸內存操作
如,直接 memcpy, memset 非 POD 類型對象,容易產生 undefined 行為
⑩① 混合編碼風格
C/C++ 混用,代碼風格不統一,容易內存泄露
🌼POD 對象
什么是 POD?
Plain Old Data,普通的舊的數據?
POD 類型:像 C 語言結構體那樣,沒有復雜構造 / 析構函數,沒有虛函數,沒有繼承,沒有非靜態 C++類 或 結構體
本質:內存布局簡單,和 C 語言的 struct 一樣,可以安全使用 memcpy, memset 等 C 函數直接操作內存的對象
裸內存操作:
直接對內存進行原始的字節級別的操作,而不是經過對象的構造,析構,拷貝等 C++ 語義
比如 memcpy(拷貝一段內存),memset(填充一段內存)
malloc / free(直接分配 / 釋放原始內存塊,不調用構造 / 析構函數)
realloc(直接調整內存塊大小)
本質:
① 只關心內存的字節內容,不關心對象的聲明周期,構造,析構,拷貝等 C++ 語義
② 適用于 C 語言風格的 POD (老古董類型)對象
③ 對于有復雜成員(std::string, vector, 虛函數, 繼承等)的 C++ 對象,裸內存操作,會破壞對象內部的狀態,導致 Undefined 行為
建議
?① 判斷 POD 場景
a. 只有簡單結構體(全是 int, float, char 等基礎類型的 struct)
b. 只要有 C++ 特性(string, vector, 虛函數, 繼承, 構造, 析構等)就不是 POD
② 不要對非 POD 對象用 memcpy / memset
比如
struct User {std::string name;int age; };User u1, u2;memcpy(&u2, &u1, sizeof(User)); // wrong! User not POD!
③ 安全做法
a. 對于非 POD 對象,通過拷貝構造,賦值運算符,swap 等 C++ 標準方法,不用 memxxx()
b. 如果需要序列化 / 反序列化,用 protobuf, json 等庫,不要直接 dump 內存
④ 工程建議
a. 寫工具類 / 測試代碼時,如果需要內存填充,清零,二進制比較,需要確認對象是 POD
b. 代碼評審時,看到 memcpy / memset 操作對象,需要確認必須是 POD
c. 如果對含 std::string 的 struct 做 memcpy(),會導致線上服務頻繁 core dump?