资讯动态

DeepSeek 自动化生成脚本与单元测试:从 API 接入到 pytest 落地的完整实践

发布时间:2026/10/6 10:53:40 来源:尧图企业网站定制
简介一份聚焦DeepSeek在自动化代码生成与单元测试方向应用的PDF电子书面向希望提升软件开发效率的开发者、测试人员及技术管理者。内容系统梳理了DeepSeek的核心能力与多语言支持、智能补全、测试用例自动生成等特性并给出从需求明确、脚本生成到优化运行的可操作流程。围绕系统管理、数据处理、自动化部署三类典型脚本以及unittest、pytest、JUnit等测试框架对比说明生成单元测试的方法与覆盖率提升技巧同时结合实践案例展示生产力改善效果也客观分析了准确性、安全性、集成兼容性等挑战及应对策略附录中对IDE集成、CI/CD嵌入和行业变革前景做了展望。PDF共17页单文件约1.75MB目录结构清晰、章节逐层递进可作为快速上手DeepSeek自动化开发的入门与进阶参考资料。目前已有421人学习下载。1. DeepSeek 自动化生成脚本与测试一句话需求换一叠能跑的文件每天开工最烦的不是业务逻辑难而是为了验证一个想法得先写几十行批处理脚本再补一版没人爱看的单元测试。DeepSeek 自动化生成可执行脚本与单元测试正好卡在这个痛点上把需求说清楚模型返回可执行的 Python 脚本再让它对着同一个函数生成 pytest 用例Mock 和边界断言一起给全。它的价值不在“能生成代码”而在“生成完直接能跑、能进 CI”。适合每天和 CSV、接口、运维脚本、老代码测试覆盖率打交道的开发者也适合刚接触大模型编程、想找一条稳定落地路径的团队。反直觉的一点单元测试这种“行为描述”类代码LLM 生成得比业务代码更合格因为输入输出足够明确幻觉空间小。2. DeepSeek API 接入与参数选择先调通最小调用再谈生产力2.1 官方 API 与本地 vLLM 部署的调用差异无论官方接口还是本地部署DeepSeek 都兼容 OpenAI 的 chat/completions 协议。官方接口的 base_url 是 https://api.deepseek.com/v1模型名选 deepseek-chat本地用 vLLM 部署时base_url 变成 http://localhost:8000/v1api_key 随便填一个非空字符串就行。这个差异决定了你后面的调用脚本能不能在两种环境之间无缝切换所以我习惯把 base_url 抽成环境变量而不是写死在代码里。from openai import OpenAI # 官方 APIkey 从平台后台拿按量计费 client OpenAI( api_keysk-xxxxxxxx, base_urlhttps://api.deepseek.com/v1, ) # 本地 vLLM 部署key 不校验但必须传非空字符串 # client OpenAI(api_keylocal, base_urlhttp://localhost:8000/v1) resp client.chat.completions.create( modeldeepseek-chat, messages[{role: user, content: 写一个把 CSV 转成 JSON 的 Python 脚本}], temperature0.3, max_tokens2048, ) print(resp.choices[0].message.content)这段代码是整套流程的最小起点。base_url指向 DeepSeek 的 OpenAI 兼容端点所以用openai这个包就能直接调不需要额外封装。官方接口在国内直连即可没有额外的网络配置本地 vLLM 则需要先确认模型已经加载完成否则会连不上端口。模型名要注意官方接口用 deepseek-chat 或 deepseek-reasoner本地部署必须填启动时注册的名字填错会直接返回 model not found。如果你已经在用 codex 这类命令行工具把它的 base_url 指向 DeepSeek 的兼容地址也能接入相当于换了一个模型后端。我不建议一开始就上社区里那些名称花哨的封装层先让这一段 curl 或 python 调用跑通后面所有生成流程都建立在这个链路上。2.2 温度、max_tokens 和 top_p 怎么设才不会翻车代码生成和聊天是两回事聊天要发散代码要收敛。同一个需求temperature 设成 1第一次可能给你一段完美脚本第二次就还你一个只写了一半的伪代码。我自己的经验值如下直接抄作业没问题。参数脚本生成单元测试生成代码改写temperature0.2 - 0.40.3 - 0.50.1 - 0.2max_tokens2048 - 40961024 - 20482048top_p1.00.9 - 1.01.0temperature 是首要参数。生成可执行脚本时我固定用 0.2宁可模型保守一点也不要它临场发挥生成单元测试稍微放到 0.4让 Mock 和边界用例有一点变化空间但不要超过 0.5。max_tokens 的坑在于截断脚本生成到一半被截断代码文件直接语法错误所以 4096 是安全值。top_p 保持 1.0 就好不要和 temperature 同时调低两个一起收会让输出变得机械甚至丢掉必要的 import。另外两个参数容易被人忽略frequency_penalty 和 presence_penalty。代码生成里这两个都保持 0因为它们的作用是“鼓励模型换说法”对代码来说等于诱导它换一种写法本来稳定的输出会因此飘掉。结构化输出场景下给请求加上response_format{type: json_object}更稳但注意 DeepSeek 的 JSON 模式要求 prompt 里出现 json 这个单词否则接口会报错。2.3 DeepSeek API 调用最小闭环一次请求拿到脚本和测试的 JSON单次生成只能拿到一串文本真正的生产力在于让模型返回结构化 JSON一次性拿到脚本、测试和说明然后自动落盘。我一般把 prompt 设计成“输出 JSON 格式”的强约束再用response_format兜底这样即使模型想写解释也会被 JSON 框住。import json from openai import OpenAI client OpenAI(api_keysk-..., base_urlhttps://api.deepseek.com/v1) def generate_code(requirement: str): prompt f 请根据需求生成 Python 脚本和 pytest 单元测试输出 JSON格式如下 {{script: 代码, tests: 代码, notes: 说明}} 需求{requirement} resp client.chat.completions.create( modeldeepseek-chat, messages[{role: user, content: prompt}], temperature0.3, max_tokens4096, response_format{type: json_object}, ) return json.loads(resp.choices[0].message.content) if __name__ __main__: result generate_code(写一个函数 is_palindrome(s)判断字符串是否回文忽略大小写和非字母数字字符) open(palindrome.py, w, encodingutf-8).write(result[script]) open(test_palindrome.py, w, encodingutf-8).write(result[tests]) print(result.get(notes, ))这段代码把“一句话需求”变成了“两个文件”。json.loads负责解析write负责落盘模型返回什么文件就写什么文件。需要注意的是result不一定每次都包含三个 key所以用result[script]之前最好先做 key 检查或者用result.get(script, )兜底。落盘之后先不要急着跑下一步先用 py_compile 做静态检查这部分后面会展开。3. 生成能直接跑的脚本从自然语言到可执行文件的完整链路3.1 需求描述模板输入、输出、约束、失败策略直接把一句话需求扔给 DeepSeek结果经常翻车要么路径写死要么遇到空文件直接崩要么生成一段“功能对但跑不起来”的伪代码。我后来把需求描述固定成五段模板成功率明显上升。这个模板不是玄学它把模型最容易自由发挥的地方全部钉死。请生成一个 Python 脚本要求如下 - 目标一句话说清要做什么 - 输入输入文件/目录在哪什么格式 - 输出输出文件路径和格式 - 依赖只能用标准库如果必须用第三方库请列在 requirements.txt - 约束不接受交互输入参数从 sys.argv 或环境变量传入单条数据出错时记录日志并跳过不要中断整体 - 输出格式只输出代码不要解释不要 Markdown 代码块标记关键在依赖和约束这两条。限制标准库直接消除了“环境里没装 pandas”这一类 ImportError要求失败策略让脚本在真实脏数据面前不至于崩掉最后一条“只输出代码”防止模型把解释文字写进 .py 文件。第一次用模板可能会觉得少点什么但跑过几次就会发现几乎所有翻车都发生在模板没写清楚的地方。3.2 用 DeepSeek 生成 CSV 批处理脚本的完整示例拿一个最常见的需求举例合并 data/ 目录下所有 CSV 文件过滤出 status 为 active 的行写入 merged.csv。我把需求填进模板模型返回的脚本通常长这样。import csv import glob import logging import os logging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s %(message)s) def merge_active_csv(data_dir: str, output_file: str) - None: out_f open(output_file, w, newline, encodingutf-8-sig) writer None fieldnames None try: for path in glob.glob(os.path.join(data_dir, *.csv)): try: with open(path, r, encodingutf-8-sig) as f: reader csv.DictReader(f) rows [row for row in reader if row.get(status) active] if not rows: logging.info(跳过 %s没有 active 行, path) continue if writer is None: fieldnames list(rows[0].keys()) writer csv.DictWriter(out_f, fieldnamesfieldnames, extrasactionignore) writer.writeheader() for row in rows: writer.writerow(row) except Exception as exc: logging.error(跳过 %s%s, path, exc) finally: out_f.close() logging.info(完成 - %s, output_file) if __name__ __main__: merge_active_csv(data, merged.csv)这是生成代码里比较稳的形态。utf-8-sig处理带 BOM 的 CSVDictWriter的extrasactionignore容忍列不一致try/except包住单个文件失败只记日志不中断整体。字段顺序取自第一个非空文件如果你的目录里各文件列名不一致最终输出会缺列这是模型不会主动提醒你的地方只能靠你的 prompt 写明“所有 CSV 列结构一致”。生成之后先看两件事函数是否接受路径参数而不是写死目录异常处理是“跳过单条”还是“跳过整个文件”。模型常在这两个地方自作主张需求描述里一旦没写它就会按最省事的逻辑来。3.3 验证生成脚本的最小动作先静态检查再跑样例数据生成不等于可用。我会用一个固定动作验证先 py_compile再给最小样例数据最后看输出。这个过程不花一分钟能滤掉大半翻车。python -m py_compile merge_active_csv.py mkdir -p sample_data cat sample_data/a.csv EOF id,status,name 1,active,alice 2,inactive,bob EOF python merge_active_csv.py cat merged.csvpy_compile 只查语法不查逻辑但对“模型输出里混了 Markdown 标记”这类问题非常敏感一跑一个准。最小样例数据要故意包含两种 status确认过滤条件生效之后再手动造一个损坏的 CSV 文件比如缺列名看脚本是否按预期跳过。这一步的真正价值是尽早暴露“字段名对不上”“编码不是 utf-8”“空文件崩溃”这三类问题而不是等接进 CI 再后悔。4. 让 DeepSeek 写单元测试覆盖路径、Mock 与断言设计4.1 为什么 LLM 写测试比写业务代码更稳基于 LLM 的单元测试生成本质上是把“函数契约”翻译成“输入-执行-断言”的三元组。模型不需要理解整个业务上下文只需要看到签名、注释和异常约定就能输出一份可运行的 pytest 文件。这也是为什么我会优先让 DeepSeek 写测试而不是写业务逻辑业务代码有外部副作用模型容易幻觉测试代码是对行为的描述输入输出足够明确幻觉空间小得多。但前提是你得把契约喂够。函数签名、合法取值范围、异常时该抛什么这些信息不传给模型它就会自己编一套“看起来合理”的测试计划覆盖率很平均但没有重点。我习惯在生成测试前先让模型把测试计划列出来用注释写在测试文件顶部再生成 pytest 用例这样你至少能检查它到底打算测什么。4.2 分析函数签名让 DeepSeek 先列测试计划再生成 pytest 用例下面这个折扣函数是典型的纯函数非常适合当测试生成样例。# discounter.py def apply_discount(price: float, discount: float) - float: if price 0 or discount 0: raise ValueError(price and discount must be non-negative) return price * (1 - discount)给 DeepSeek 的 prompt 我这样写下面是 apply_discount 的函数签名。请先用注释在测试文件顶部列出测试计划 再生成 pytest 用例要求覆盖 - 正常价格和折扣的组合 - price0、discount0、discount1 的边界 - price0、discount0 的异常 - discount1 时按公式返回不额外报错 测试文件需要 from discounter import apply_discount。只输出测试代码。模型返回的测试代码大致如下import pytest from discounter import apply_discount def test_normal_case(): assert apply_discount(100, 0.2) pytest.approx(80.0) def test_zero_price(): assert apply_discount(0, 0.5) 0 def test_zero_discount(): assert apply_discount(200, 0) 200 def test_discount_one(): assert apply_discount(100, 1) 0 def test_discount_greater_than_one(): assert apply_discount(100, 1.5) -50 def test_negative_price(): with pytest.raises(ValueError): apply_discount(-1, 0.1) def test_negative_discount(): with pytest.raises(ValueError): apply_discount(10, -0.2)这份测试看起来全绿但有边界问题discount1.5时函数返回负数模型直接断言等于 -50。如果业务上不允许折扣超过 1函数就该抛异常测试也应该锁这个异常。所以在 prompt 里必须明确“discount1 属于合法还是非法”否则模型只能按公式锁结果。边界断言不是越多越好而是要跟函数契约完全对齐。4.3 给生成函数接 Mock外部依赖别挂错路径更贴近真实场景的是函数有外部调用比如发 HTTP 请求。下面这个函数用from requests import post直接引入请求方法。# notify.py from requests import post def send_alert(webhook_url: str, message: str) - bool: resp post(webhook_url, json{text: message}, timeout3) return resp.status_code 200让 DeepSeek 生成 pytest 用例时正确的 Mock 路径是notify.post而不是requests.post。因为from requests import post已经把函数绑定到了 notify 模块自己的命名空间你改requests.post影响不到 notify 内部的引用。import pytest import notify class FakeResponse: status_code 200 class FakeServerErrorResponse: status_code 500 def test_send_alert_success(monkeypatch): def fake_post(url, jsonNone, timeout3): assert url http://example.com/hook assert json {text: hello} return FakeResponse() monkeypatch.setattr(notify, post, fake_post) assert notify.send_alert(http://example.com/hook, hello) is True def test_send_alert_server_error(monkeypatch): def fake_post(url, jsonNone, timeout3): return FakeServerErrorResponse() monkeypatch.setattr(notify, post, fake_post) assert notify.send_alert(http://example.com/hook, hello) is False这条很值得讲模型经常生成monkeypatch.setattr(requests.post, fake_post)看起来没问题跑起来测试全红。这跟前端 vue 工程里单元测试报错常见的 ESM mock 路径问题是一类都是“打到原对象上没打到模块加载后的引用上”。我的习惯是生成测试后先看所有monkeypatch.setattr第一个参数是不是被测模块自己的名字不是就改掉。5. 避坑生成结果“看起来能用”背后的 5 个真实雷区5.1 ImportError生成脚本依赖了环境里没装的库现象第一次运行脚本直接ModuleNotFoundError: No module named pandas。原因模型默认选择它见过的“最常见”路径pandas、requests、numpy 张口就来完全不考虑你的运行环境是干净的 Docker 容器还是老服务器。解决在 prompt 里限制“只能用标准库如果必须用第三方库请列 requirements.txt”让依赖声明和代码一起生成。脚本里最好再加一段 import 时的 friendly 报错比如try: import pandas失败就提示“请先 pip install pandas”而不是抛原始堆栈。真正接 CI 时把 requirements.txt 纳入版本管理跑测试前先安装依赖能把这雷挡在门外。5.2 单元测试全绿但全是弱断言现象pytest显示 10 个 passed你故意把函数里的改成-测试依然全部通过。原因LLM 为了确保测试一定通过倾向于生成assert result is not None、assert len(result) 0这种“安全断言”它们永远不会失败自然也没有守护能力。解决在 prompt 里加一条硬约束每个测试必须断言具体返回值或副作用禁止使用is not None、len0这类泛化断言。生成后做一个反向验证手工改错被测函数的实现跑一次 pytest确认至少一个用例会变红。测试的价值不是证明“今天能跑”而是锁住“以后改错会挂”。5.3 温度没固定同一份需求两次结果天差地别现象上午生成的脚本能跑下午同样 prompt 再生成一份连函数名都变了偶尔还会给你一段带语法错误的残片。原因调用 API 时 temperature 用了默认值 1模型随机性被拉满。代码生成不是创意写作发散等于翻车。解决把 temperature 写进封装脚本里固定下来脚本生成用 0.2测试生成用 0.4不要每次都手动填。网页对话框里反复点“再试一次”不是解决问题是在赌运气真想要不同实现应该改需求描述而不是靠温度赌。5.4 模型幻觉调用了不存在的 API 或属性现象运行时报AttributeError: module requests has no attribute get_text查官方文档才发现根本没有这个函数。原因训练数据里混杂了不同版本的库用法甚至把另一个库的 API 拼了过来。模型对高频库也有记忆错乱的时候尤其 requests、subprocess、os.path 这几个老牌模块。解决prompt 里写死 Python 版本和关键库版本还要加一句“如果某个 API 你不确定请用标准库替代并在文件顶部注释说明”。生成后不要直接跑先用 grep 把代码里所有第三方库调用抽出来去官方文档核对一遍。这个动作只花两分钟却能挡掉最难受的运行时崩溃。5.5 输出被 Markdown 和解释文字污染现象把模型回答直接存成 .py文件开头是“好的下面给你一个脚本”结尾跟着三个反引号的 Markdown 代码块标记py_compile 直接报语法错误。原因模型为了可读性习惯在代码前后加解释这是对话模型的默认行为不是它能自动识别“你要落盘”的意图。解决prompt 里明确“只输出代码不要解释不要 Markdown 代码块标记”如果用了response_format{type: json_object}从result[script]取代码JSON 解析天然过滤掉解释文字。落盘前做一个后缀检查比如文件不是 .py 结尾就直接报错退出。这五个雷区我都踩过每一条的修复成本都不高但都会在没准备的时候浪费一小时。6. 进阶把生成、验证、落盘固化成一条命令模型生成不是终点接进本地工具链才算生产力。我会把整个流程封装成一个管道调用 API、写入文件、静态检查、跑通 pytest全部塞进同一个 Python 脚本。import json import subprocess import sys from openai import OpenAI def main(requirement: str): client OpenAI(api_keysk-..., base_urlhttps://api.deepseek.com/v1) prompt ( 请生成 Python 脚本和 pytest 单元测试输出 JSON格式 {script: 代码, tests: 代码}。 f需求{requirement} ) resp client.chat.completions.create( modeldeepseek-chat, messages[{role: user, content: prompt}], temperature0.2, max_tokens4096, response_format{type: json_object}, ) data json.loads(resp.choices[0].message.content) with open(generated.py, w, encodingutf-8) as f: f.write(data[script]) with open(test_generated.py, w, encodingutf-8) as f: f.write(data[tests]) subprocess.run([sys.executable, -m, py_compile, generated.py], checkTrue) subprocess.run([sys.executable, -m, pytest, -q, test_generated.py], checkTrue) if __name__ __main__: main(写一个函数把输入字符串里的连续空格压缩成单个空格)这条命令就是你的最小生成闭环。py_compile 没过说明模型输出被污染或截断直接抛异常退出pytest 没过说明测试或代码里有逻辑问题你会立刻看到失败用例而不是等到接 CI 才爆。这一步之后可以再给 pytest 加--maxfail1避免一个失败拖着后面几十个用例空跑。我现在的习惯是任何模型生成的代码第一件事不是读逻辑而是先 py_compile再给最小样例数据跑一遍测试代码先故意改错实现确认测试会红再改回正确实现确认测试会绿。这一套固定动作把 DeepSeek 从“偶尔惊艳的玩具”变成了“每天能用的生成器”。如果你也能把生成参数和验证顺序固定成一个命令你的落地过程会比大多数人顺得多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑