自動化測試:settings.json 骨架與驗證動作)
1. 為什么要在 CI 里讓 DeepSeek 寫測試用例自動化測試最費時間的環(huán)節(jié)往往不是跑用例而是寫用例。接口改一個字段斷言要跟著改新加一個業(yè)務分支就得補一組參數(shù)化數(shù)據(jù)。DeepSeek 這類模型擅長把「自然語言描述的需求」翻譯成 pytest 代碼正好補上這塊人力。但直接把模型塞進 CI 有兩個現(xiàn)實問題一是 Key 散落在各個倉庫的 secrets 里輪換一次要改十幾個地方二是不同模型、不同供應商的 Base URL 和鑒權(quán)格式不統(tǒng)一腳本里到處是 if-else。我這次要搭的閉環(huán)是CI 流水線觸發(fā) → 調(diào)用 DeepSeek 根據(jù)接口描述生成測試用例 → 寫入臨時測試文件 → 執(zhí)行冒煙腳本 → 斷言通過后歸檔報告。整條鏈路里模型調(diào)用統(tǒng)一走 TaoToken 的 API 通道Key 只維護一份settings.json 作為配置骨架被腳本讀取。這樣換模型、換供應商只改配置不動測試代碼。適合誰看已經(jīng)在用 GitHub Actions / GitLab CI 跑 pytest想讓模型參與用例生成又不想把鑒權(quán)邏輯寫死的測試或運維同學。下面從配置骨架開始一步步把可運行的驗證動作做出來。2. TaoToken 前置統(tǒng)一 Key 與 API 通道TaoToken 在這里扮演的角色是「統(tǒng)一入口」它提供 OpenAI 兼容的 API 格式DeepSeek 的模型通過它調(diào)用Base URL 和 Key 都收斂到一處。你不需要在 CI 里分別配置 DeepSeek 官方地址和 TaoToken 地址腳本只認一個base_url。先拿到 Key。訪問控制臺創(chuàng)建 API Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite創(chuàng)建后復制以sk-開頭的字符串存到 CI 的 Secret 里命名建議TAOTOKEN_API_KEY。本地調(diào)試可以放到.env但別提交。API 通道地址是https://taotoken.net/api兼容 OpenAI 的/v1/chat/completions路徑。也就是說任何支持自定義 Base URL 的 OpenAI SDK 都能直接指向它。模型名填deepseek-chat即可這是 DeepSeek 的對話模型標識。如果你更習慣用現(xiàn)成的編碼 Agent 來生成測試可以看 Coding Plan 的接入方式它把模型調(diào)用和代碼生成串好了https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite接入文檔里有各語言 SDK 的 Base URL 填法遇到 401 或路徑 404 時對照排查https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite注意Key 只放環(huán)境變量不要寫進 settings.json 提交到倉庫。settings.json 里用占位符引用環(huán)境變量名。3. 可復制的 settings.json 配置骨架settings.json 的定位是「測試生成器的配置中心」模型參數(shù)、輸出路徑、超時、重試都在這里腳本讀它CI 也讀它。下面這份骨架可以直接復制字段按注釋替換。{ llm: { provider: taotoken, base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: deepseek-chat, temperature: 0.2, max_tokens: 2048, timeout_seconds: 60, max_retries: 3 }, generation: { prompt_template: prompts/gen_tests.md, output_dir: tests/generated, overwrite: true, language: python, framework: pytest }, smoke: { target_file: tests/generated/test_smoke_generated.py, run_command: pytest -q tests/generated/test_smoke_generated.py, report_path: reports/smoke.xml } }幾個關(guān)鍵點說明。temperature設 0.2 是為了讓生成的測試代碼穩(wěn)定別太發(fā)散max_retries配合下面的腳本做指數(shù)退避CI 網(wǎng)絡抖動時不至于直接失敗。prompt_template指向一個 Markdown 文件里面寫清楚「根據(jù)以下接口描述生成 pytest 用例必須包含斷言」這類約束比把提示詞硬編碼在腳本里好維護。output_dir和target_file分開是為了讓生成的文件名可預測冒煙腳本能直接定位。report_path用 JUnit XML 格式CI 平臺大多能直接解析。讀取配置的 Python 片段import json import os def load_settings(pathsettings.json): with open(path, r, encodingutf-8) as f: cfg json.load(f) api_key os.environ.get(cfg[llm][api_key_env]) if not api_key: raise RuntimeError(f缺少環(huán)境變量 {cfg[llm][api_key_env]}) cfg[llm][api_key] api_key return cfg這樣 Key 從環(huán)境變量注入settings.json 本身可以安全地進版本庫。4. 生成到斷言的完整驗證動作先寫提示詞模板prompts/gen_tests.md把接口描述和輸出要求講清楚你是一個測試工程師。根據(jù)下面的接口描述生成 pytest 測試用例。 要求 1. 使用 requests 庫調(diào)用接口base_url 從環(huán)境變量 API_BASE 讀取。 2. 每個用例必須有 assert 斷言覆蓋正常返回和至少一個異常入?yún)ⅰ?3. 只輸出 Python 代碼不要解釋不要 Markdown 代碼塊標記。 接口描述 {{INTERFACE_DESC}}生成腳本gen_tests.py調(diào)用 TaoToken 的兼容接口import json import time import requests from pathlib import Path from settings_loader import load_settings def build_prompt(cfg, interface_desc): tpl Path(cfg[generation][prompt_template]).read_text(encodingutf-8) return tpl.replace({{INTERFACE_DESC}}, interface_desc) def call_llm(cfg, prompt): url cfg[llm][base_url].rstrip(/) /v1/chat/completions headers { Authorization: fBearer {cfg[llm][api_key]}, Content-Type: application/json, } payload { model: cfg[llm][model], messages: [{role: user, content: prompt}], temperature: cfg[llm][temperature], max_tokens: cfg[llm][max_tokens], } last_err None for attempt in range(cfg[llm][max_retries]): try: resp requests.post( url, headersheaders, jsonpayload, timeoutcfg[llm][timeout_seconds], ) resp.raise_for_status() return resp.json()[choices][0][message][content] except Exception as e: last_err e time.sleep(2 ** attempt) raise RuntimeError(f調(diào)用失敗: {last_err}) def main(): cfg load_settings() desc Path(interface_desc.txt).read_text(encodingutf-8) code call_llm(cfg, build_prompt(cfg, desc)) out Path(cfg[generation][output_dir]) out.mkdir(parentsTrue, exist_okTrue) target out / test_smoke_generated.py target.write_text(code, encodingutf-8) print(f已生成 {target}) if __name__ __main__: main()interface_desc.txt里寫你要測的接口比如GET /api/v1/users/{id} 返回 JSON: {id: int, name: str, email: str} id 不存在時返回 404。跑一次生成export TAOTOKEN_API_KEYsk-你的key python gen_tests.py生成的tests/generated/test_smoke_generated.py大致長這樣import os import requests BASE os.environ[API_BASE] def test_get_user_ok(): r requests.get(f{BASE}/api/v1/users/1, timeout5) assert r.status_code 200 body r.json() assert isinstance(body[id], int) assert isinstance(body[name], str) def test_get_user_not_found(): r requests.get(f{BASE}/api/v1/users/999999, timeout5) assert r.status_code 404執(zhí)行冒煙export API_BASEhttp://localhost:8000 pytest -q tests/generated/test_smoke_generated.py --junitxmlreports/smoke.xml成功時輸出類似2 passed in 0.42s到這里從「接口描述」到「斷言通過」的閉環(huán)就跑通了。CI 里把gen_tests.py和pytest串成兩個 step 即可生成失敗會因重試機制自動兜底。5. 本篇常見錯排查401 Unauthorized九成是 Key 沒注入。檢查 CI Secret 名是否和 settings.json 里的api_key_env一致本地確認echo $TAOTOKEN_API_KEY有值。別把 Key 直接寫進 settings.json。404 Not FoundBase URL 拼錯。正確是https://taotoken.net/api腳本里再拼/v1/chat/completions。如果 SDK 自己會補/v1就別重復加否則變成/v1/v1/...。生成的代碼帶 Markdown 圍欄模型偶爾會輸出 python 包裹。提示詞里已經(jīng)要求「不要 Markdown 標記」若仍出現(xiàn)在寫入前加一層清洗def strip_fence(code: str) - str: lines code.strip().splitlines() if lines and lines[0].startswith(): lines lines[1:] if lines and lines[-1].startswith(): lines lines[:-1] return \n.join(lines)斷言太弱或沒有斷言把提示詞里的「必須有 assert」寫得更具體比如「至少一個狀態(tài)碼斷言和一個字段類型斷言」。temperature 再降到 0.1 也有幫助。超時CI 網(wǎng)絡慢時把timeout_seconds調(diào)到 90max_retries保持 3。重試間隔用指數(shù)退避別固定 1 秒猛打。生成文件覆蓋了手寫用例output_dir單獨放tests/generated別和手寫測試混在一起。overwrite設 true 只影響生成目錄。6. 把閉環(huán)接進 CI 與后續(xù)動作GitHub Actions 里兩個 step 就夠- name: Generate tests env: TAOTOKEN_API_KEY: ${{ secrets.TAOTOKEN_API_KEY }} run: python gen_tests.py - name: Run smoke env: API_BASE: http://localhost:8000 run: pytest -q tests/generated/test_smoke_generated.py --junitxmlreports/smoke.xml想驗證模型本身是否正??梢韵扔媚P蛯υ掜撁姘l(fā)一條消息確認通道通https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite長期在 CI 里跑生成任務Key 用量和并發(fā)會上去Coding Plan 更適合這種持續(xù)編碼場景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewriteKey 管理和輪換在控制臺完成新 Key 建好后只改 CI Secretsettings.json 不用動https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接口路徑或鑒權(quán)格式有疑問時翻接入文檔里面有各語言示例https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite實測下來這套骨架最省心的地方是配置和代碼分離換模型只改 settings.json 的model字段換供應商只改base_url測試腳本一行不動。生成目錄獨立也避免了模型輸出污染手寫用例。