跳到主要内容 AI 辅助编程的边界探索:当 Copilot 学会写测试 | 极客日志
TypeScript AI 大前端
AI 辅助编程的边界探索:当 Copilot 学会写测试 探讨了 AI 辅助编程工具(如 Copilot)在编写测试用例时的能力边界。通过 TypeScript 和 Jest 的实验,发现 AI 擅长生成 Happy Path 和基础 Mock 代码,但在理解业务意图、处理边界条件和复杂集成测试时存在幻觉和局限。文章指出 AI 目前应定位为高级助理,人类需负责深层逻辑校验,并提出通过明确 Prompt 和角色设定来提升 AI 写测试的效率。结论是 AI 降低了测试门槛,但测试质量仍取决于人类对业务的理解。
邪神洛基 发布于 2026/4/6 更新于 2026/4/13 1 浏览
在过去的几年里,我们见证了人工智能辅助编程工具(如 GitHub Copilot、Cursor 等)从简单的代码补全插件,进化成为能够独立思考、生成复杂逻辑的'数字伙伴'。大多数开发者的使用场景停留在'帮我写个排序算法'或者'帮我补全这个 React 组件的样式'。然而,当 AI 开始染指软件工程中最繁琐、最需要逻辑严密性的领域——测试(Testing) 时,一切都变得有趣且充满挑战。
今天,我们就来深入探讨一下:当 Copilot 学会了写测试,它的边界在哪里?它能否取代人工编写测试用例的工作?它又会在哪里'翻车' ?让我们通过一系列真实的代码实验来寻找答案。
1. 从'写代码'到'验代码':AI 的新战场
传统的编程教学往往强调如何实现功能(Implementation),而软件工程的核心却有一半是维护。维护的核心不是加功能,而是保证加功能不破坏旧功能 。这就使得测试变得至关重要。
对于 AI 来说,写一段计算斐波那契数列的代码是简单的,因为逻辑是收敛的。但是,写一段针对斐波那契数列的测试用例则是发散的:你要考虑输入 0、输入负数、输入超大数、溢出处理等等。AI 写测试的核心难点在于:它不知道产品的'意图'(Intent),它只能根据上下文进行'模仿' 。
下面,我们构建一个典型的业务场景,来测试 Copilot 的能力边界。我们将使用 TypeScript 和 Jest 作为实验环境。
场景设定:一个简单的支付网关模拟器
我们需要一个看似简单但包含逻辑分支的函数。
interface PaymentRequest {
userId : string ;
amount : number ;
currency : 'USD' | 'CNY' | 'EUR' ;
}
interface PaymentResult {
success : boolean ;
transactionId ?: string ;
error ?: string ;
}
export const processPayment = (req : PaymentRequest ): PaymentResult => {
if (req. <= ) {
{ : , : };
}
(req. . ( )) {
{ : , : };
}
txId = ;
{ : , : txId };
};
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown 转 HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
HTML 转 Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
amount
0
return
success
false
error
'Invalid amount'
if
userId
startsWith
'test_'
return
success
true
transactionId
'test_tx_123'
const
`tx_${Math .random().toString(36 ).substr(2 , 9 )} `
return
success
true
transactionId
现在,如果我们直接告诉 AI:'为这个文件写测试',会发生什么?
2. 初级实验:AI 能写出'Happy Path'吗? Prompt: // Write unit tests for processPayment function using Jest
Copilot (or similar AI) 生成的代码:
import { processPayment } from './paymentService' ;
describe ('processPayment' , () => {
it ('should return success for valid payment' , () => {
const request = {
userId : 'user_123' ,
amount : 100 ,
currency : 'USD' ,
};
const result = processPayment (request);
expect (result.success ).toBe (true );
expect (result.transactionId ).toBeDefined ();
});
it ('should fail when amount is zero' , () => {
const request = {
userId : 'user_123' ,
amount : 0 ,
currency : 'USD' ,
};
const result = processPayment (request);
expect (result.success ).toBe (false );
expect (result.error ).toBe ('Invalid amount' );
});
});
分析: 这是标准的'模板式'测试。AI 完美地识别了代码中的两个 if 分支,并生成了对应的断言。这是 Copilot 最擅长的地方,它本质上是在做'模式匹配'。
但是,这引出了我们的第一个疑问:如果我们不告诉它需要测试'负数金额',它会主动写吗?通常不会。这暴露了 AI 的第一个盲区:被动响应,而非主动发现缺陷 。
3. 进阶实验:Mocking 与 外部依赖 在真实的项目中,你的函数通常不是独立的,它会调用数据库、调用第三方 API(Stripe, PayPal 等)。测试必须将这些外部依赖 Mock(模拟) 掉。这才是考验 AI 逻辑理解能力的地方。
export const saveTransaction = async (data : any ) => {
return { id : 'db_123' , ...data };
};
import { saveTransaction } from './database' ;
export const createOrder = async (items : string [], userId : string ) => {
if (!items.length ) throw new Error ('Cart is empty' );
const order = { items, userId, createdAt : new Date () };
const saved = await saveTransaction (order);
return saved;
};
Prompt: // Write tests for createOrder and mock the database module
import { createOrder } from './orderService' ;
import { saveTransaction } from './database' ;
jest.mock ('./database' , () => ({
saveTransaction : jest.fn (),
}));
describe ('createOrder' , () => {
it ('should save order to database' , async () => {
const mockSave = saveTransaction as jest.Mock ;
mockSave.mockResolvedValue ({ id : 'mock_id' });
const result = await createOrder (['item1' ], 'user_1' );
expect (mockSave).toHaveBeenCalled ();
expect (result.id ).toBe ('mock_id' );
});
it ('should throw error if cart is empty' , async () => {
await expect (createOrder ([], 'user_1' )).rejects .toThrow ('Cart is empty' );
});
});
评判结果: 这一波操作非常漂亮。AI 不仅自动生成了 jest.mock,还正确处理了异步函数 (async/await)。这说明在基于上下文的单元测试 场景下,AI 已经达到了相当高的可用性。
但是,精彩的地方来了。如果我们稍微修改一下业务逻辑,增加一个边界条件,AI 的表现会如何?
4. 陷阱与幻觉:AI 写测试时犯的那些错 AI 并不真正'理解'代码,它只是根据统计学预测最可能的下一段文本。这导致它在处理边界值 和特定业务规则 时,会产生令人啼笑皆非的'幻觉测试'。
案例 A:永远不会错的测试 假设我们有一段非常糟糕的代码(或者 AI 根本看不懂的混淆代码):
export const getStatus = (code : number ) => {
if (code > 100 ) return 'OK' ;
return 'ERROR' ;
};
it ('should return OK for code > 100' , () => {
expect (getStatus (101 )).toBe ('OK' );
});
这看起来没问题。但是,如果产品需求变了,实际上业务规则应该是 code >= 100(大于等于)呢?AI 不会质疑代码,它会忠实且错误地测试错误的代码。这就是所谓的 '测试覆盖了代码,但验证了错误' 。
案例 B:永远跑不通的断言 有时候,AI 会写出看似合理,但运行时永远无法通过的断言。
Prompt: // Test that the array contains unique elements
假设我们的原始数组是 [1, 2, 3, 3](有重复),AI 可能会生成这样的测试:
it ('should contain only unique elements' , () => {
const result = getData ();
const unique = new Set (result);
expect (unique.size ).toBe (result.length );
});
如果你运行这个测试,它会 Fail 。但开发者看到 Fail 后,往往会去改代码(试图让数组没有重复),而不是意识到这个测试用例本身是基于错误的假设(数据源本身可能有重复,这不是一个 Bug,而是一个业务事实)。AI 制造了新的'伪 Bug'。
案例 C:复杂集成测试的无力 在端到端(E2E)测试或者集成测试中,AI 的表现会指数级下降。因为这需要 AI 理解状态(State) 。
想象一下,你需要测试:'用户登录 -> 添加购物车 -> 点击结算 -> 验证库存扣减 -> 验证支付回调 -> 验证数据库订单状态'。
这种长达 10 步的流程,AI 很难一次性生成完整的 Flow。除非你在极其细粒度的 Prompt 中,详细描述每一步的数据库状态和预期结果。
5. 人机协作:重新定义测试工作流 既然 AI 不是万能的,那么最佳的策略就是利用 AI 的吞吐量,配合人类的判断力 。让我们用 Mermaid 图表来展示这个新时代的测试工作流。
graph TD
A[AI: 自动生成基础测试桩] --> B{人工校验}
B -- 通过 --> C[上线]
B -- 不通过 --> D[修正逻辑]
D --> B
如上图所示,AI 负责第一层的覆盖 (Happy path + 基本的 Mock),而人类负责最深层的逻辑校验 。
实践技巧:如何高效地让 AI 写测试?
不要只说'写测试' 。
❌ // write tests
✅ // Write unit tests for this function, specifically testing the error handling when input is null and the currency conversion logic for USD to EUR.
指定测试框架 。
明确告诉它用什么工具(例如 Jest, Mocha, PyTest),它能生成更精准的脚手架。
让它扮演 QA 工程师 。
使用 Prompt: Act as a QA engineer. What edge cases should I test for this login function? (扮演 QA 工程师,我应该测试这个登录函数的哪些边界情况?)。通常它会列出一堆你没想到的 negative case,比如 SQL 注入测试、并发登录测试等。
6. 展望未来:AI 会取代测试工程师吗? 目前的 AI,包括 Copilot,在测试领域的定位应该是 '高级助理' ,而不是'独立工作者'。
生成样板代码(Boilerplate)。
编写针对确定逻辑的单元测试。
批量生成 Mock 代码。
将代码转化为测试(如果你有一段代码,它能帮你写测试)。
理解真实的业务意图(Context)。
编写性能测试和压力测试。
维护测试(当代码重构后,测试需要同步更新,AI 往往会照着旧代码生成新的错误测试)。
探索性测试(Exploratory Testing)。
结论是:Copilot 能把测试的门槛降低,但无法把测试的质量拉高。 质量取决于人类对业务的理解深度。
让我们拥抱变化,利用工具,但永远保持怀疑精神。毕竟,代码可能会说谎,但测试不能。