專欄文章 質量保障系統的落地實踐 (七)-AI 落地&指標評估

亦攸發表於2025-03-13

往期文章

專欄文章 質量保障系統的落地實踐 (一) 概覽篇
專欄文章 質量保障系統的落地實踐 (二) 專案管理設計 - 基礎資訊與缺陷資訊設計
專欄文章 質量保障系統的落地實踐 (二) 專案管理設計 - 程式碼資訊設計
專欄文章 質量保障系統的落地實踐 (三) CI 管理設計 - 基礎設計
專欄文章 質量保障系統的落地實踐 (三) CI 管理設計 - 整合設計
專欄文章 質量保障系統的落地實踐 (四) 效能管理設計 - 造數工廠
專欄文章 質量保障系統的落地實踐 (五) 視覺化設計
專欄文章 質量保障系統的落地實踐 (六) 擴充延伸

前言

大家好,我是困學。上次專欄文章釋出之後,已經有一段時間沒有投稿了,這次給大家帶來的主題是:測試崗位如何結合 AI 提效

閱讀完整篇文章,大家會發現,設計思路、實現方案與專欄文章 質量保障系統的落地實踐 (三) CI 管理設計 - 基礎設計的方法是一致的,一定是因地制宜結合公司實際情況進行考量和落地,沒有一種方案可以適配所有場景,這篇文章的目的在於分享案例,一個在我司是怎麼依據實際情況設計並落地AI 測試用例以及指標評估的案例。

那麼廢話不多說,開始切入正文。

哪個階段使用 AI

大家入行後,無可避免的工作環節就是編寫測試用例,這篇文章就以這個環節作為切入點。
編寫測試用例的流程一般是:將產品需求文件描述的場景需求,翻譯為測試點,基於測試點構建測試用例。那麼 AI 在哪個環節介入比較合適?
最理想的情況是 AI 直接做完整個流程,這樣對測試的提效是最高的,測試只需要關注最終產出的測試用例,在 AI 生成的基礎上進行修改即可。

實際上,這樣的交付是不合適的。由於 AI 並不瞭解業務上下文 (或者說培訓 AI 正確瞭解業務的上下文並不容易),導致 AI 翻譯需求文件,得出測試點的過程,往往與測試的期望會產生偏差,那麼基於這份偏差之上產出的測試用例對於測試來說,其可用性是較差的。

目前階段,AI 介入比較合適的環節是測試點生成測試用例的過程。
比起分析需求文件得出測試點,由測試點派生出測試用例的過程,則相對容易,AI 可以套用測試用例設計方案生成通用測試用例;也可以依據測試點內標註的業務邏輯,生成對應的業務測試用例

若是 AI 培訓得當,需求完檔產出測試點的過程,或早或晚的也能交付給 AI 產出。而就本篇文章需要落地的功能來說,測試點生成測試用例需求文件生成測試用例以上兩種方案均能支援。

可行性分析

人工編寫或者 AI 提效,根本目的是為了得到一份測試用例,更重要的是一份符合測試組維護格式的用例,大白話就是拿來就可用。比如,公司使用的是 excel 維護測試用例,那麼 AI 產出的測試用例就需要是 excel 格式,或者說接近 excel 便於轉換成 excel 格式的資料結構;又比如,公司使用的是腦圖,那麼 AI 產出的測試用例就需要是腦圖格式,或者說接近腦圖便於轉換成腦圖格式的資料結構。

那麼問題來了,目前主流使用 AI 的方式是互動式介面溝通:

如果直接描述測試點,讓 AI 產出測試用例,AI 的輸出結果往往是這樣的:

內容以文字形式產出,這樣的文字內容,是無法稱為一份符合測試組維護格式的用例的。

那麼,最佳化一下描述,讓 AI 按照指定格式輸出:

從上面的結果來看,只需要讓 AI 輸出指定格式的內容,那麼就可以得到一份符合測試組維護格式的用例。最為關鍵的一個環節就打通了,那麼這個功能的可行性就具備了。

功能拆解

透過可行性章節的分析,可以得到一個滿足格式要求的測試用例結果,剩下的部分就是怎麼將功能測試點組裝成 prompt 的描述語句交付給 AI 了。
這個環節也可以用比較樸素的方式解決:平鋪,即測試點 1:測試點描述 1、測試點 2:測試點描述 2。
這個環節就需要質量保障平臺來做了,給予組內測試人員一個互動友好的介面,讓測試人員專注錄入測試點以及測試點描述:

由於交付 AI 生成測試用例,有一個等待過程。從體系功能的完善性上考慮,需要加上通知類的能力,告知測試用例已生成,並提供檢視路徑,以釘釘通知為例:

至此,就設計出了 AI 編寫測試用例的功能框架:

舉個例子

我司的測試用例情況如下,是基於腦圖二次開發後的資料結構,本質是樹狀 json 結構:

{
    "root": {
        "data": {
            "id": "d8et0ojfdxc0",
            "created": 1741835122468,
            "text": "AI用例"
        },
        "children": [
            {
                "data": {
                    "id": "d8et6t6iclc0",
                    "created": 1741835602755,
                    "text": "測試點"
                },
                "children": [
                    {
                        "data": {
                            "id": "d8et6ts10940",
                            "created": 1741835604057,
                            "text": "用例1",
                            "usecase": 2
                        },
                        "children": [
                            {
                                "data": {
                                    "id": "d8et6ug9ex40",
                                    "created": 1741835605522,
                                    "text": "前置條件1",
                                    "usecase": 1
                                },
                                "children": [
                                    {
                                        "data": {
                                            "id": "d8et6vld60g0",
                                            "created": 1741835608008,
                                            "text": "操作步驟1",
                                            "usecase": 4
                                        },
                                        "children": [
                                            {
                                                "data": {
                                                    "id": "d8et6w4gq9s0",
                                                    "created": 1741835609162,
                                                    "text": "預期結果1",
                                                    "usecase": 3
                                                },
                                                "children": []
                                            }
                                        ]
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        "data": {
                            "id": "d8et6wqnbbc0",
                            "created": 1741835610503,
                            "text": "用例2",
                            "usecase": 2
                        },
                        "children": [
                            {
                                "data": {
                                    "id": "d8et73wxtsw0",
                                    "created": 1741835626121,
                                    "text": "前置條件2",
                                    "usecase": 1
                                },
                                "children": []
                            },
                            {
                                "data": {
                                    "id": "d8et74eig4g0",
                                    "created": 1741835627184,
                                    "text": "操作步驟2",
                                    "usecase": 4
                                },
                                "children": []
                            },
                            {
                                "data": {
                                    "id": "d8et74zcejk0",
                                    "created": 1741835628444,
                                    "text": "預期結果2",
                                    "usecase": 3
                                },
                                "children": []
                            }
                        ]
                    }
                ]
            }
        ]
    },
    "template": "default",
    "theme": "fresh-blue-compat",
    "version": "1.4.43",
    "id": "d8et0ojfdxc0"
}

對應的外掛會將這份 json 結構渲染成如下圖所示的效果:

那麼,只要最終得到這樣結構的 json 資料,就得到了一份符合測試組維護格式的用例
接下來看怎麼描述 prompt,直接上程式碼:

# 用例整體結構
part1 = {
    "root": {
        "data": {
            "id": "d868fsvqsuw0",
            "created": 1740964584421,
            "text": f"{root_description}"
        },
        "children": []
    },
    "template": "default",
    "theme": "fresh-blue-compat",
    "version": "1.4.43",
    "id": "d868fsvqsuw0"
}

# 測試點結構
part2 = {
    "data": {
        "id": "d86982ow4yg0",
        "created": 1740966799971,
        "text": "子需求名稱1",
        "usecase": 0
    },
    "children": []
}

# 用例結構
part3 = {
    "data": {
        "id": "d8698dvdbmg0",
        "created": 1740966824307,
        "text": "測試用例",
        "usecase": 2
    },
    "children": [
        {
            "data": {
                "id": "d8698efskz40",
                "created": 1740966825542,
                "text": "前置條件",
                "usecase": 1
            },
            "children": [
                {
                    "data": {
                        "id": "d8698f7j6u80",
                        "created": 1740966827219,
                        "text": "操作步驟",
                        "usecase": 4
                    },
                    "children": [
                        {
                            "data": {
                                "id": "d8698fwp7zc0",
                                "created": 1740966828741,
                                "text": "預期結果",
                                "usecase": 3
                            },
                            "children": []
                        }
                    ]
                }
            ]
        }
    ]
}

# 封裝完整prompt
prompt = f"""
    "整體輸出結構":\n{json.dumps(part1, ensure_ascii=False)}\n
    "子需求結構":\n{json.dumps(part2, ensure_ascii=False)}\n
    子需求名稱替換usecase=0的單元的text欄位.\n
    最終生成的多個"子需求結構",append至"整體輸出結構"["root"]["children"]內.\n
    "用例結構":{json.dumps(part3, ensure_ascii=False)}\n
    前置條件替換"用例結構"中usecase=1的單元的text欄位,\n
    用例名稱替換"用例結構"中usecase=2的單元的text欄位,\n
    預期結果替換"用例結構"中usecase=3的單元的text欄位,\n
    操作步驟替換"用例結構"中usecase=4的單元的text欄位.\n
    最終生成的多個"用例結構",append至"子需求結構"["children"]內.\n
    以上所有資料結構內,若存在created欄位,則取生成結構時的時間戳填充.\n
    若存在id欄位,則隨機生成位數相同且唯一的字串填充.\n
    "根需求名稱":{root_description},\n
    以下為多個子需求描述:\n{child_description_str},\n
    根據上文分析出來的所有測試點,生成符合上述格式要求的測試用例.\n
    特別注意,確保最終輸出結果為json格式,內部不要有註釋,不要省略內容.\n
    最後只需要返回"整體輸出結構"即可,請按照json格式輸出。.
"""

# 剔除無關空格
prompt = re.sub(r'\s+', ' ', prompt).strip()        

實現思路比較直白,將幾個元素全部剝離:1、用例整體結構2、測試點結構3、用例結構。(這裡多說一嘴,用例的結構,由於每個人編寫的習慣不同,所以格式五花八門,既然設計系統功能,就不要考慮那麼多種格式的情況。以固定格式產出後,由測試自行調整即可。),然後讓 AI 構建出用例結構,按層級依次新增至測試點結構測試點結構依次新增至用例整體結構中,最終返回用例整體結構,按 json 格式即可。

指標評估

以上部分已經完成了功能搭建,接下來該考慮指標的問題了。大家在工作中都會面臨這個問題:你做的功能也好,設計的系統也好,能帶來什麼價值?這個價值,如何被量化?

落地之前,務必先想好資料量化,有了資料,才能做後續的評估,才有後續的改進,形成一個正反饋的閉環。

劃重點了,資料資料,還是 (國粹和諧)資料

那麼就這個 AI 用例功能來說,資料該怎麼沉澱呢?指標該怎麼設計呢?
回答這個問題之前,先回到原點,測試用例原本是由測試手工完成的,為什麼要引入 AI?除了當前萬物 all in AI 的理由外,更重要的是為測試人員提效,省去部分甚至全部編寫測試用例的工作量。那麼最好的指標就是計算出:AI 編寫測試用例的方式到底比全手動編寫測試用例的方式能效提高了多少?

比如說,在引入了 AI 用例編寫的功能後,測試產出測試用例需要 8 個人日。而 AI 產出的測試用例佔比有 30%,那麼人工比例就佔了 70%,而由於 AI 生成是不佔用時間的,所以可以認為這 70% 的人工比例是 8 個人日。那麼如果純人工編寫就需要:8 / 0.7 = 11.43 人日,AI 提效的人日就為 8 / 0.7 * 0.3 = 3.43 人日。(看到這篇文章的你未必就會認同這種演算法,此處只是舉個例子,你完全可以按照你的實際情況設計量化指標。)

順著這個邏輯下來,現在問題就到了怎麼計算 AI 產出的測試用例佔比的問題了。要知道佔比,首先得知道哪些測試用例是由 AI 產生的,很自然的想到用打標的方式。而往往我們編寫測試用例的軟體所生成的資料結構是外部很難直接修改的,例如:

如上圖,我司編寫測試用例的軟體,開啟後已經被渲染,並且在這個軟體基礎上所有的操作都已經被二次開發後固定下來了,如以下結構,是沒有辦法外部干預的 (除非你能讓開發這個軟體的人支援你修改):

{
    "data": {
        "id": "d8et6vld60g0",
        "created": 1741835608008,
        "text": "操作步驟1",
        "usecase": 4
    },
    "children": [
        {
            "data": {
                "id": "d8et6w4gq9s0",
                "created": 1741835609162,
                "text": "預期結果1",
                "usecase": 3
            },
            "children": []
        }
    ]
}

所以基本上不考慮在這一側做資料打標的工作,那就只能在 AI 測想辦法了。看到這裡,聰明的你大機率反應過來了,其實只需要在給 AI 的資料結構上直接加上引數就可以了,因為這個資料結構是我們自己設計的,問題解決:

{
    "data": {
        "id": "d8et6vld60g0",
        "created": 1741835608008,
        "text": "操作步驟1",
        "usecase": 4,
        "is_ai": true          # 直接打標籤
    },
    "children": [
        {
            "data": {
                "id": "d8et6w4gq9s0",
                "created": 1741835609162,
                "text": "預期結果1",
                "usecase": 3,
                "is_ai": true          # 直接打標籤
            },
            "children": []
        }
    ]
}

資料打標的問題也解決之後,AI 產出的所有測試用例部分全部標記了 is_ai=true,而測試人員拿到了這份測試用例後,可能會發生新增、編輯、刪除等操作得到一份系統測試用例,最終上傳回質量保障平臺。要做的就是設計一種方法,窮盡每個測試用例的節點,分別統計出 AI 產生的節點集合 setA,系統測試用例的節點集合 setB,兩者取交集,得到存活的 AI 節點 setC,那麼 AI 產出的佔比就可以計算:len(setC) / len(setB)。

得到佔比之後,後續統計 AI 提效的人日也好,其他指標也罷,就都方便了。
貼一張最終指標結果拋磚引玉:

結尾

這篇文章到這裡就結束了,感謝閱讀這篇文章的你,如果覺得對你有一些啟發的話,辛苦點贊收藏,不勝感激。
收到過私信評論,寫到現在的專欄文件,很多功能比如 CI,又比如造數,都是基於 RF 實現,很多閱讀文章的測試同學,他們日常並不使用 RF,那這些功能設計對他們還有用嗎?
這裡我想說一句,雖然功能不能直接照搬,但是不妨看看思路,用這些文章內的設計思路去舉一反三,去設計出一套適配於自己當前環境的功能。實踐是檢驗真理的唯一標準,所謂的測試開發,質量保障,他們做的事也是在這一件件日常工作的思考和實踐中逐漸完善自己的能力,適配自己的崗位。

多思考,多實踐,與君共勉。

相關文章