soarli

API 额度快过期了?我用几十行脚本让 DeepSeek 帮我把 Node.js 项目单测拉满!
如果你手里有一笔马上就要过期的 LLM(大语言模型)API 额度,你会拿它干什么?看着后台倒计时的日期,很多人的第...
扫描右侧二维码阅读全文
17
2026/03

API 额度快过期了?我用几十行脚本让 DeepSeek 帮我把 Node.js 项目单测拉满!

如果你手里有一笔马上就要过期的 LLM(大语言模型)API 额度,你会拿它干什么?

看着后台倒计时的日期,很多人的第一反应是:必须赶紧薅完这最后一把羊毛! 可是手动在网页端对话实在太慢了。作为一个程序员,我的思路很明确:找一个极其消耗 Token、平时懒得做、但做了又极具价值的“重体力活”,写个自动化脚本让大模型在后台满负荷跑起来。

经过一番衡量,我放弃了“让 AI 重构老代码”(因为幻觉风险太高,且侵入性强),最终锁定了一个完美的满级耗流任务:全项目自动化补全单元测试。

今天,就手把手教你如何用几十行 Python 脚本,结合 DeepSeek V3.2 的强大代码能力,安全、高效地把你的 Node.js 项目单测覆盖率强行拉满!


💡 为什么选“生成单元测试”?

在压榨 API 剩余价值时,生成单测有三大无可比拟的优势:

  1. 绝对安全,零生产风险: 它是非侵入式的。AI 只会生成独立的测试文件,就算它胡言乱语写错了,大不了就是测试跑不过,绝对不会把你原本跑得好好的业务逻辑搞崩。
  2. 真正的“耗 Token 巨兽”: 单元测试的代码量往往比业务代码还要大。为了覆盖各种边界条件、Mock 外部接口,AI 需要疯狂输出,烧额度效率极高。
  3. 消除技术债的利器: 平时业务迭代紧,单测总是被搁置。趁这个机会把历史包袱甩给 AI,以后重构代码底气都足了。

🛠️ 核心思路与武器库

为了让大模型吐出的测试代码高质量且不破坏现有目录,我们采用了以下策略:

  • 目标精准制导: 只扫描核心业务目录(如 controllers, services, models, utils 等),跳过静态资源和文档。
  • 安全隔离: 所有生成的测试文件统一存放在一个全新的 ai_generated_tests 文件夹中。
  • 高并发请求: 使用 Python 的 asyncioaiohttp 替代单线程请求,最大化并发吞吐量。

你需要准备的武器:

  • 一个 Node.js 后端项目(本文以 Express/Koa + Jest 为例)。
  • Python 3.8+ 环境(用于运行高并发请求脚本)。
  • 即将过期的 API Key(本文以硅基流动平台调用的 DeepSeek-V3 为例)。

🚀 实战代码:Python 高并发生成脚本

在你的 Node.js 项目根目录下,新建一个 burn_api.py 文件,贴入以下代码。

(⚠️ 注意:运行前请先在终端执行 pip install openai asyncio 安装依赖)

import os
import asyncio
from openai import AsyncOpenAI

# ================= 配置区 =================
# 🚨 警告:切勿将真实的 API Key 提交到公开仓库!
API_KEY = "sk-你的真实API_KEY"
BASE_URL = "https://api.siliconflow.cn/v1" # 这里以硅基流动为例
MODEL = "deepseek-ai/DeepSeek-V3" 

# 精准制导:只扫描包含核心逻辑的文件夹
TARGET_DIRS = ["controllers", "services", "models", "middlewares", "utils"]
# 安全隔离:生成的代码存放在全新文件夹
TEST_DIR = "./ai_generated_tests"      
FILE_EXTENSIONS = ('.js', '.ts') 
TEST_SUFFIX = ".test"     

# 并发数控制:如果遇到 429 报错,请适当调低
CONCURRENCY_LIMIT = 5     
# ==========================================

client = AsyncOpenAI(api_key=API_KEY, base_url=BASE_URL)

SYSTEM_PROMPT = """你是一个资深的 Node.js 测试开发工程师。请为以下代码编写严密的单元测试。
要求:
1. 使用 Jest 测试框架。
2. 覆盖正常逻辑(Happy Path)与异常边界条件(Edge Cases)。
3. 涉及数据库、外部网络请求或文件系统时,必须使用 jest.mock() 进行深度 Mock,绝不能发起真实请求。
4. 使用 describe 块组织测试套件,用 it() 提供清晰的中文测试用例说明。
严格限制:你必须且只能输出纯测试代码本身!不要包含任何解释、问候语或 Markdown 代码块标记(如 ```javascript)。"""

async def generate_tests(file_path, content, semaphore):
    async with semaphore:
        print(f"🧪 正在请求 API 生成单测: {file_path}...")
        try:
            response = await client.chat.completions.create(
                model=MODEL,
                messages=[
                    {"role": "system", "content": SYSTEM_PROMPT},
                    {"role": "user", "content": content}
                ],
                temperature=0.1, # 低温度保证测试逻辑严谨不发散
                max_tokens=4000
            )
            return response.choices[0].message.content
        except Exception as e:
            print(f"❌ 处理 {file_path} 失败: {e}")
            return None

async def process_file(base_dir, file_name, semaphore):
    file_path = os.path.join(base_dir, file_name)
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()

    if not content.strip():
        return

    test_content = await generate_tests(file_path, content, semaphore)

    if test_content:
        # 清理可能残留的 Markdown 标记
        if test_content.startswith("```"):
            test_content = "\n".join(test_content.split("\n")[1:])
        if test_content.endswith("```"):
            test_content = "\n".join(test_content.split("\n")[:-1])

        # 还原目录结构,例如:ai_generated_tests/controllers/user.test.js
        name, ext = os.path.splitext(file_name)
        test_file_name = f"{name}{TEST_SUFFIX}{ext}"
        
        out_dir = os.path.join(TEST_DIR, base_dir)
        os.makedirs(out_dir, exist_ok=True)
        out_file_path = os.path.join(out_dir, test_file_name)
        
        with open(out_file_path, 'w', encoding='utf-8') as f:
            f.write(test_content)
        print(f"✅ 成功保存: {out_file_path}")

async def main():
    if not os.path.exists(TEST_DIR):
        os.makedirs(TEST_DIR)

    semaphore = asyncio.Semaphore(CONCURRENCY_LIMIT)
    tasks = []

    for target in TARGET_DIRS:
        if not os.path.exists(target):
            continue
        for root, _, files in os.walk(target):
            for file in files:
                if file.endswith(FILE_EXTENSIONS) and TEST_SUFFIX not in file:
                    tasks.append(process_file(root, file, semaphore))

    if not tasks:
        print("未找到需要处理的代码文件。")
        return

    print(f"🚀 共找到 {len(tasks)} 个文件,开始高并发生成...")
    await asyncio.gather(*tasks)
    print("\n🎉 全部单元测试生成完毕!API 额度燃烧成功!")

if __name__ == "__main__":
    asyncio.run(main())

配置好你的 API_KEY 后,只需在终端运行 python burn_api.py,你就可以端起咖啡,看着终端里不断弹出的“✅ 成功保存”,享受自动化带来的赛博快感了。


📦 运行测试:如何验收 AI 的工作成果?

代码生成完毕后,你的项目根目录会多出一个 ai_generated_tests 文件夹。接下来我们要把它跑起来。

1. 安装 Jest(如果还没装的话)

npm install --save-dev jest supertest

**2. 配置 package.json**
scripts 中添加一条专门运行 AI 测例的命令:

"scripts": {
  "test:ai": "jest ./ai_generated_tests --coverage"
}

3. 一键验收

npm run test:ai

🚧 避坑指南:AI 代码的“通病”与修复

当你按下回车,大概率会看到满屏的红色报错。别慌!这不是模型傻,而是大模型在没有整个文件系统上下文时,必然会遇到的“相对路径瞎猜”问题。

只要掌握以下三招,一小时内就能让测试满眼飘绿:

  1. 修复 require 路径报错:
    大模型经常搞错引入层级(比如报 Cannot find module '../../controllers/user')。你需要手动打开报错的测试文件,把顶部的相对路径修正(比如多加一层 ../)。
  2. 处理数据库连通性问题:
    即便你在 Prompt 里要求了 Mock,它有时还是会试图连接真实数据库。报错时,只需在测试文件顶部手动补上一句 jest.mock('../../models/user') 即可。
  3. 修复 app is not defined
    在测试 Controller 时,AI 喜欢用 supertest(app),但它不知道你的 app.js 具体在哪。检查测试文件顶部,确保正确引入了你的 Express/Koa 实例:const app = require('../../app');

🛡️ 最后的安全叮嘱

无论在何种情况下,都不要把含有真实 API Key 的脚本提交到 Git 仓库! 额度跑完后,立刻去控制台把当前的 Key 删除作废,养成良好的安全习惯。

最后修改:2026 年 03 月 17 日 01 : 56 AM

发表评论