


AI的提示词专栏:单元测试 Prompt,自动生成测试用例
本文围绕单元测试与 Prompt 结合展开,先指出传统单元测试效率低、覆盖度不均的痛点,阐述 Prompt 驱动生成测试用例在效率提升、覆盖度优化、规范统一方面的核心价值,接着明确单元测试 Prompt 需遵循代码信息完整、测试规则明确、输出格式约束的设计原则。随后通过 Python(Pytest)、Java(JUnit 5 + AssertJ)、前端 JavaScript(Jest)三个不同场景的实战案例,展示 Prompt 编写、预期输出及技巧分析,还介绍了补充反例约束、增加代码上下文等调优技巧,解答常见问题,最后总结核心要点,强调生成测试用例后需人工校验与 IDE 运行验证结合,确保保障代码质量。

人工智能专栏介绍
人工智能学习合集专栏是 AI 学习者的实用工具。它像一个全面的 AI 知识库,把提示词设计、AI 创作、智能绘图等多个细分领域的知识整合起来。无论你是刚接触 AI 的新手,还是有一定基础想提升的人,都能在这里找到合适的内容。从最基础的工具操作方法,到背后深层的技术原理,专栏都有讲解,还搭配了实例教程和实战案例。这些内容能帮助学习者一步步搭建完整的 AI 知识体系,让大家快速从入门进步到精通,更好地应对学习和工作中遇到的 AI 相关问题。

这个系列专栏能教会人们很多实用的 AI 技能。在提示词方面,能让人学会设计精准的提示词,用不同行业的模板高效和 AI 沟通。写作上,掌握从选题到成稿的全流程技巧,用 AI 辅助写出高质量文本。编程时,借助 AI 完成代码编写、调试等工作,提升开发速度。绘图领域,学会用 AI 生成符合需求的设计图和图表。此外,还能了解主流 AI 工具的用法,学会搭建简单智能体,掌握大模型的部署和应用开发等技能,覆盖多个场景,满足不同学习者的需求。


1️⃣ ⚡ 点击进入 AI 的提示词专栏,专栏拆解提示词底层逻辑,从明确指令到场景化描述,教你精准传递需求。还附带包含各行业适配模板:医疗问诊话术、电商文案指令等,附优化技巧,让 AI 输出更贴合预期,提升工作效率。
2️⃣ ⚡ 点击进入 AI 灵感写作专栏,AI 灵感写作专栏,从选题到成稿,全流程解析 AI 写作技巧。涵盖论文框架搭建、小说情节生成等,教你用提示词引导 AI 输出内容,再进行人工润色。附不同文体案例,助你解决写作卡壳,产出高质量文本。
3️⃣ ⚡ 点击进入 AI 辅助编程专栏,AI 辅助编程专栏,通过实例教你用 AI 写代码:从功能描述到调试优化。涵盖前端、后端、数据库等,语言包括HTML5、VUE、Python、Java、C# 等语言,含算法实现、Bug 修复技巧,帮开发者减少重复劳动,专注核心逻辑,提升开发速度。
4️⃣ ⚡ 点击进入 AI 精准绘图专栏,AI 精准绘图,聚焦 AI 绘图在设计场景的落地。详解如何描述风格、元素、用途,生成 logo、商标等。含 Midjourney 等工具参数设置,及修改迭代方法,帮设计新手快速出图,满足商业与个人需求。
5️⃣ ⚡ 点击进入 AI 绘制图表专栏,AI 绘制图表专栏,教你用 AI 工具将数据转化为直观图表。涵盖曲线图数据输入、流程图逻辑梳理等,附 Excel 联动、格式美化技巧,适合学生、职场人快速制作专业图表,提升数据展示效果。
6️⃣ ⚡ 点击进入 AI 的工具集专栏,AI 的工具集专栏,盘点主流 AI 工具:ChatGPT、DeepSeek、 Claude、Gemini、Copilot 等。解析各工具优势,附使用场景与技巧,帮你根据需求选工具,快速上手提升效率,覆盖办公、创作、开发等场景。
7️⃣ ⚡ 点击进入 AI 的智能体专栏,AI 的智能体专栏,解析智能体自主运行原理,包括任务拆解、环境交互等。教你用大模型搭建简单智能体,附多智能体协作案例,适合想探索 AI 自主系统的开发者入门。
8️⃣ ⚡ 点击进入 AI 的大模型专栏,AI 的大模型专栏,详解大模型部署步骤,从本地搭建到云端部署。含 API 调用教程、应用开发案例,教你将大模型集成到项目,掌握企业级 AI 应用开发技能,应对实际业务需求。
一、单元测试与 Prompt 结合的核心价值
在软件开发流程中,单元测试是保障代码质量的关键环节------它针对最小功能模块(如函数、方法)验证逻辑正确性,提前暴露边界值错误、参数异常等问题。但传统单元测试编写存在两大痛点:一是效率低 ,开发者需手动覆盖多场景(正常输入、异常输入、边界值),尤其复杂函数需反复调试测试逻辑;二是覆盖度不均,易因思维盲区遗漏关键场景(如空值传入、数据类型错误),导致测试流于形式。
而 Prompt 驱动的单元测试生成,能通过"自然语言描述代码功能+测试规则",让大语言模型(LLM)快速输出符合规范的测试用例,核心价值体现在三方面:
- 效率提升:将测试用例编写时间从小时级压缩至分钟级,尤其适合接口密集、逻辑重复的模块(如数据处理函数、工具类方法);
- 覆盖度优化:通过在 Prompt 中明确"强制覆盖异常场景、边界值、业务约束",可系统性覆盖开发者易忽略的测试点;
- 规范统一:通过 Prompt 预设测试框架(如 JUnit、Pytest)、断言风格(如 AssertJ、Hamcrest),确保团队测试代码风格一致,降低维护成本。
二、单元测试 Prompt 的核心设计原则
要让 LLM 生成"可直接运行、覆盖全面"的测试用例,Prompt 需避免模糊描述(如"帮我测试这个函数"),需遵循"代码信息完整+测试规则明确+输出格式约束"三大原则,具体拆解如下:
| 设计原则 | 核心要求 | 反例 vs 正例 |
|---|---|---|
| 代码信息完整 | 提供待测试函数/方法的完整代码 (含参数类型、返回值、注释)、业务背景(如"该函数用于计算订单折扣,满100减20,满200减50"),避免 LLM 因信息缺失猜测逻辑 | 反例:"测试一个计算折扣的函数" 正例:"待测试函数:public double calculateDiscount(double orderAmount) { // 业务规则:订单金额≥200减50,100≤金额<200减20,不足100无折扣 return ...; }" |
| 测试规则明确 | 明确测试场景范围 (正常场景/异常场景/边界值)、测试框架 (如 Pytest 5.x、JUnit 4)、断言要求(如"需验证返回值误差≤0.01""异常场景需断言抛出 IllegalArgumentException") | 反例:"生成测试用例,覆盖所有情况" 正例:"生成 Pytest 测试用例,需覆盖3类场景:1. 正常场景(订单金额80、150、250);2. 边界值(99.99、100、199.99、200);3. 异常场景(负数金额、0元金额),异常场景需断言抛出 ValueError" |
| 输出格式约束 | 规定测试用例的结构模板 (如"类名+测试方法名+注释+代码")、命名规范(如测试方法名遵循"test_函数名_场景描述"),确保可直接复制到项目中运行 | 反例:"输出测试代码即可" 正例:"输出格式要求:1. 测试类名:TestOrderDiscount;2. 测试方法名:test_calculateDiscount_正常场景_金额80(无折扣)、test_calculateDiscount_边界值_金额100(减20)等;3. 每个测试方法前加场景说明注释;4. 代码需导入 pytest,使用 assert 断言返回值" |
三、不同场景的单元测试 Prompt 实战案例
场景1:Python 工具函数(Pytest 框架)
待测试代码
python
def format_phone_number(phone: str) -> str:
"""
格式化手机号:移除非数字字符,若长度为11位则按"138-1234-5678"格式处理,否则返回原字符串(仅保留数字)
:param phone: 原始手机号(可能含空格、-、+86等字符)
:return: 格式化后的手机号
"""
# 移除所有非数字字符
pure_digit = ''.join(filter(str.isdigit, phone))
# 处理11位手机号
if len(pure_digit) == 11:
return f"{pure_digit[:3]}-{pure_digit[3:7]}-{pure_digit[7:]}"
# 非11位则返回纯数字
return pure_digit
单元测试 Prompt
请基于以下信息生成 Pytest 单元测试用例:
1. 待测试函数:上述 format_phone_number(phone: str) -> str
2. 测试框架:Pytest 7.x,无需导入多余包(仅需 import pytest 和待测试函数)
3. 测试场景要求(需全部覆盖):
- 正常场景:原始手机号含空格(如"138 1234 5678")、含-(如"138-1234-5678")、含+86(如"+8613812345678")
- 边界场景:11位纯数字(如"13812345678")、10位纯数字(如"1381234567")、12位纯数字(如"138123456789")
- 异常场景:空字符串("")、纯非数字(如"abcdef")、混合特殊字符(如"138*1234#5678")
4. 输出格式要求:
- 测试类名:TestPhoneFormatter
- 测试方法名:遵循"test_formatPhone_场景_输入特征"(如 test_formatPhone_正常场景_含空格)
- 每个方法前加1-2句注释,说明测试目的
- 使用 assert 断言返回值是否符合预期
预期输出
python
import pytest
from your_module import format_phone_number # 需替换为实际模块名
class TestPhoneFormatter:
def test_formatPhone_正常场景_含空格(self):
# 测试目的:原始手机号含空格,应移除空格并按11位格式处理
assert format_phone_number("138 1234 5678") == "138-1234-5678"
def test_formatPhone_正常场景_含横杠(self):
# 测试目的:原始手机号含-,应移除-并按11位格式处理
assert format_phone_number("138-1234-5678") == "138-1234-5678"
def test_formatPhone_正常场景_含+86(self):
# 测试目的:原始手机号含+86,应移除非数字并按11位格式处理
assert format_phone_number("+8613812345678") == "138-1234-5678"
def test_formatPhone_边界场景_11位纯数字(self):
# 测试目的:11位纯数字输入,应直接按格式处理
assert format_phone_number("13812345678") == "138-1234-5678"
def test_formatPhone_边界场景_10位纯数字(self):
# 测试目的:10位纯数字输入,仅保留数字不格式化
assert format_phone_number("1381234567") == "1381234567"
def test_formatPhone_边界场景_12位纯数字(self):
# 测试目的:12位纯数字输入,仅保留数字不格式化
assert format_phone_number("138123456789") == "138123456789"
def test_formatPhone_异常场景_空字符串(self):
# 测试目的:空字符串输入,返回空字符串
assert format_phone_number("") == ""
def test_formatPhone_异常场景_纯非数字(self):
# 测试目的:纯非数字输入,返回空字符串(无数字可保留)
assert format_phone_number("abcdef") == ""
def test_formatPhone_异常场景_混合特殊字符(self):
# 测试目的:含特殊字符的手机号,应移除特殊字符并按11位格式处理
assert format_phone_number("138*1234#5678") == "138-1234-5678"
技巧点分析
- 该 Prompt 通过"场景分类+输入示例"(如正常场景明确"含空格、-、+86"),避免 LLM 遗漏常见输入格式;
- 异常场景中明确"空字符串、纯非数字",覆盖了开发者易忽略的"无数字可保留"场景,确保测试完整性;
- 输出格式中规定"方法名+注释",便于后续维护时快速定位测试场景对应的代码逻辑。
场景2:Java 业务接口(JUnit 5 + AssertJ 框架)
待测试代码
java
import java.math.BigDecimal;
/**
* 订单服务接口:处理订单金额计算(含运费、折扣)
*/
public class OrderService {
/**
* 计算订单最终支付金额
* @param orderAmount 商品总金额(不能为空,且≥0)
* @param discountAmount 折扣金额(不能为空,且≥0,且≤orderAmount)
* @param freight 运费(不能为空,且≥0)
* @return 最终支付金额 = 商品总金额 - 折扣金额 + 运费
* @throws IllegalArgumentException 若参数不符合约束(如discountAmount>orderAmount)
*/
public BigDecimal calculatePayAmount(BigDecimal orderAmount, BigDecimal discountAmount, BigDecimal freight) {
// 参数合法性校验
if (orderAmount == null || orderAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("商品总金额不能为空且需≥0");
}
if (discountAmount == null || discountAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("折扣金额不能为空且需≥0");
}
if (discountAmount.compareTo(orderAmount) > 0) {
throw new IllegalArgumentException("折扣金额不能超过商品总金额");
}
if (freight == null || freight.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("运费不能为空且需≥0");
}
// 计算最终金额
return orderAmount.subtract(discountAmount).add(freight);
}
}
单元测试 Prompt
请基于以下信息生成 JUnit 5 + AssertJ 单元测试用例:
1. 待测试类/方法:OrderService 类的 calculatePayAmount(BigDecimal orderAmount, BigDecimal discountAmount, BigDecimal freight) 方法
2. 测试框架:JUnit 5(需使用 @Test、@DisplayName 注解)、AssertJ 3.x(使用 org.assertj.core.api.Assertions 静态方法)
3. 测试场景要求(需全部覆盖):
- 正常场景:无折扣(discountAmount=0)、有折扣(discountAmount<orderAmount)、含运费(freight>0)
- 边界场景:discountAmount=orderAmount(折扣等于商品金额)、freight=0(免运费)、orderAmount=0(赠品订单,无商品金额)
- 异常场景:orderAmount=null、orderAmount负数、discountAmount>orderAmount、freight负数(每个异常需断言抛出 IllegalArgumentException,且异常信息匹配)
4. 输出格式要求:
- 测试类名:OrderServiceTest,添加 @DisplayName("订单服务测试类")
- 测试方法:每个场景1个方法,添加 @DisplayName("场景描述")(如 @DisplayName("正常场景:有折扣+含运费"))
- 异常测试:使用 JUnit 5 的 assertThrows 捕获异常,用 AssertJ 断言异常信息(如 assertThat(exception.getMessage()).isEqualTo("xxx"))
- 代码需完整导入依赖(如 import org.junit.jupiter.api.*; import org.assertj.core.api.Assertions; import java.math.BigDecimal;)
预期输出
java
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.assertj.core.api.Assertions;
import java.math.BigDecimal;
@DisplayName("订单服务测试类")
public class OrderServiceTest {
// 初始化待测试对象(所有测试方法共用)
private final OrderService orderService = new OrderService();
@Test
@DisplayName("正常场景:无折扣+含运费")
void testCalculatePayAmount_noDiscount_withFreight() {
// 给定:商品金额100元,折扣0元,运费10元
BigDecimal orderAmount = new BigDecimal("100.00");
BigDecimal discountAmount = new BigDecimal("0.00");
BigDecimal freight = new BigDecimal("10.00");
// 当:调用计算方法
BigDecimal result = orderService.calculatePayAmount(orderAmount, discountAmount, freight);
// 则:最终金额应为 100 - 0 + 10 = 110 元
Assertions.assertThat(result).isEqualByComparingTo(new BigDecimal("110.00"));
}
@Test
@DisplayName("正常场景:有折扣+含运费")
void testCalculatePayAmount_withDiscount_withFreight() {
// 给定:商品金额200元,折扣50元,运费15元
BigDecimal orderAmount = new BigDecimal("200.00");
BigDecimal discountAmount = new BigDecimal("50.00");
BigDecimal freight = new BigDecimal("15.00");
// 当:调用计算方法
BigDecimal result = orderService.calculatePayAmount(orderAmount, discountAmount, freight);
// 则:最终金额应为 200 - 50 + 15 = 165 元
Assertions.assertThat(result).isEqualByComparingTo(new BigDecimal("165.00"));
}
@Test
@DisplayName("边界场景:折扣等于商品金额+免运费")
void testCalculatePayAmount_discountEqualsOrderAmount_freeFreight() {
// 给定:商品金额150元,折扣150元,运费0元
BigDecimal orderAmount = new BigDecimal("150.00");
BigDecimal discountAmount = new BigDecimal("150.00");
BigDecimal freight = new BigDecimal("0.00");
// 当:调用计算方法
BigDecimal result = orderService.calculatePayAmount(orderAmount, discountAmount, freight);
// 则:最终金额应为 150 - 150 + 0 = 0 元
Assertions.assertThat(result).isEqualByComparingTo(new BigDecimal("0.00"));
}
@Test
@DisplayName("边界场景:赠品订单(商品金额0)+含运费")
void testCalculatePayAmount_orderAmountZero_withFreight() {
// 给定:商品金额0元,折扣0元,运费8元
BigDecimal orderAmount = new BigDecimal("0.00");
BigDecimal discountAmount = new BigDecimal("0.00");
BigDecimal freight = new BigDecimal("8.00");
// 当:调用计算方法
BigDecimal result = orderService.calculatePayAmount(orderAmount, discountAmount, freight);
// 则:最终金额应为 0 - 0 + 8 = 8 元
Assertions.assertThat(result).isEqualByComparingTo(new BigDecimal("8.00"));
}
@Test
@DisplayName("异常场景:商品金额为null")
void testCalculatePayAmount_orderAmountNull_throwException() {
// 给定:商品金额null,其他参数合法
BigDecimal orderAmount = null;
BigDecimal discountAmount = new BigDecimal("20.00");
BigDecimal freight = new BigDecimal("10.00");
// 当且则:调用方法抛出异常,且信息匹配
IllegalArgumentException exception = Assertions.assertThatThrownBy(() ->
orderService.calculatePayAmount(orderAmount, discountAmount, freight)
).isInstanceOf(IllegalArgumentException.class)
.hasMessage("商品总金额不能为空且需≥0");
}
@Test
@DisplayName("异常场景:折扣金额超过商品金额")
void testCalculatePayAmount_discountExceedOrderAmount_throwException() {
// 给定:折扣60元 > 商品金额50元
BigDecimal orderAmount = new BigDecimal("50.00");
BigDecimal discountAmount = new BigDecimal("60.00");
BigDecimal freight = new BigDecimal("5.00");
// 当且则:调用方法抛出异常,且信息匹配
IllegalArgumentException exception = Assertions.assertThatThrownBy(() ->
orderService.calculatePayAmount(orderAmount, discountAmount, freight)
).isInstanceOf(IllegalArgumentException.class)
.hasMessage("折扣金额不能超过商品总金额");
}
@Test
@DisplayName("异常场景:运费为负数")
void testCalculatePayAmount_freightNegative_throwException() {
// 给定:运费-5元(非法),其他参数合法
BigDecimal orderAmount = new BigDecimal("300.00");
BigDecimal discountAmount = new BigDecimal("80.00");
BigDecimal freight = new BigDecimal("-5.00");
// 当且则:调用方法抛出异常,且信息匹配
IllegalArgumentException exception = Assertions.assertThatThrownBy(() ->
orderService.calculatePayAmount(orderAmount, discountAmount, freight)
).isInstanceOf(IllegalArgumentException.class)
.hasMessage("运费不能为空且需≥0");
}
}
技巧点分析
- 该 Prompt 针对 Java 业务方法的"参数约束多、异常场景明确"特点,通过"异常场景+异常信息匹配"的要求,确保测试用例不仅捕获异常,还验证异常原因的准确性(避免因错误的异常信息导致问题排查困难);
- 使用 JUnit 5 的
@DisplayName和 AssertJ 的流式断言,让测试报告更易读(如"正常场景:有折扣+含运费"),符合企业级测试代码的可读性要求; - 初始化待测试对象为类成员变量,避免每个测试方法重复创建,体现了测试代码的简洁性设计------这是 Prompt 中"隐含的代码优化要求"(通过"测试类结构合理"的潜台词实现)。
场景3:前端 JavaScript 函数(Jest 框架)
待测试代码
javascript
/**
* 过滤用户列表:根据角色和活跃度筛选
* @param {Array} userList - 用户列表,每个用户格式:{ id: number, name: string, role: string, activeDays: number }
* @param {string} targetRole - 目标角色(如"admin""user",传"all"表示不筛选角色)
* @param {number} minActiveDays - 最小活跃天数(传0表示不筛选活跃度)
* @returns {Array} 筛选后的用户列表(按 activeDays 降序排列)
*/
function filterUserList(userList, targetRole, minActiveDays) {
// 角色筛选
const roleFiltered = targetRole === 'all'
? userList
: userList.filter(user => user.role === targetRole);
// 活跃度筛选
const activeFiltered = roleFiltered.filter(user => user.activeDays >= minActiveDays);
// 按活跃天数降序排列
return activeFiltered.sort((a, b) => b.activeDays - a.activeDays);
}
单元测试 Prompt
请基于以下信息生成 Jest 单元测试用例:
1. 待测试函数:上述 filterUserList(userList, targetRole, minActiveDays)
2. 测试框架:Jest 29.x(使用 test() 函数、expect() 断言)
3. 测试场景要求(需全部覆盖):
- 正常场景:筛选指定角色(如 targetRole="admin")、筛选最小活跃度(如 minActiveDays=30)、角色+活跃度组合筛选
- 边界场景:targetRole="all"(不筛选角色)、minActiveDays=0(不筛选活跃度)、用户列表为空数组、用户列表含1个元素
- 异常场景:userList 不是数组(如传对象、null)、targetRole 不是字符串(如传数字)、minActiveDays 不是数字(如传字符串)
4. 输出格式要求:
- 每个测试用例用 test("场景描述", () => { ... }) 包裹
- 测试数据:在每个用例内定义清晰的 userList 示例(如 const mockUsers = [ { id: 1, ... }, ... ])
- 排序验证:需断言筛选结果按 activeDays 降序排列(如 expect(result[0].activeDays).toBeGreaterThanOrEqual(result[1].activeDays))
- 异常场景:需断言函数抛出 Error,且错误信息含"参数类型错误"
预期输出
javascript
// 导入待测试函数(若为单独文件,需用 require/import,此处假设函数在当前文件)
// const { filterUserList } = require('./userUtils');
// 测试用例集
describe('filterUserList 函数测试', () => {
// 公共测试数据(多个用例复用)
const mockUsers = [
{ id: 1, name: '张三', role: 'admin', activeDays: 60 },
{ id: 2, name: '李四', role: 'user', activeDays: 30 },
{ id: 3, name: '王五', role: 'admin', activeDays: 45 },
{ id: 4, name: '赵六', role: 'user', activeDays: 15 }
];
test('正常场景:筛选角色为 admin 的用户', () => {
// 给定:目标角色 admin,不筛选活跃度(minActiveDays=0)
const targetRole = 'admin';
const minActiveDays = 0;
// 当:调用筛选函数
const result = filterUserList(mockUsers, targetRole, minActiveDays);
// 则:结果应含2个 admin 用户,且按 activeDays 降序
expect(result.length).toBe(2);
expect(result.map(user => user.id)).toEqual([1, 3]); // 张三(60天)> 王五(45天)
expect(result[0].activeDays).toBeGreaterThanOrEqual(result[1].activeDays);
});
test('正常场景:筛选活跃天数≥30的用户(不限制角色)', () => {
// 给定:角色 all,最小活跃天数30
const targetRole = 'all';
const minActiveDays = 30;
// 当:调用筛选函数
const result = filterUserList(mockUsers, targetRole, minActiveDays);
// 则:结果应含3个用户(张三60、李四30、王五45),按活跃天数降序
expect(result.length).toBe(3);
expect(result.map(user => user.activeDays)).toEqual([60, 45, 30]);
});
test('边界场景:用户列表为空数组', () => {
// 给定:空用户列表,任意筛选条件
const emptyUserList = [];
const targetRole = 'user';
const minActiveDays = 20;
// 当:调用筛选函数
const result = filterUserList(emptyUserList, targetRole, minActiveDays);
// 则:结果应为空数组
expect(result).toEqual([]);
});
test('边界场景:minActiveDays=0(不筛选活跃度)', () => {
// 给定:筛选角色 user,minActiveDays=0
const targetRole = 'user';
const minActiveDays = 0;
// 当:调用筛选函数
const result = filterUserList(mockUsers, targetRole, minActiveDays);
// 则:结果应含2个 user 用户(李四30、赵六15),按活跃天数降序
expect(result.length).toBe(2);
expect(result.map(user => user.id)).toEqual([2, 4]);
});
test('异常场景:userList 不是数组(传对象)', () => {
// 给定:userList 为对象(非法类型)
const invalidUserList = { id: 1, name: '错误数据' };
const targetRole = 'admin';
const minActiveDays = 30;
// 当且则:调用函数抛出错误,信息含"参数类型错误"
expect(() => filterUserList(invalidUserList, targetRole, minActiveDays))
.toThrowError(/参数类型错误/);
});
test('异常场景:minActiveDays 不是数字(传字符串)', () => {
// 给定:minActiveDays 为字符串"30"(非法类型)
const targetRole = 'all';
const minActiveDays = '30';
// 当且则:调用函数抛出错误,信息含"参数类型错误"
expect(() => filterUserList(mockUsers, targetRole, minActiveDays))
.toThrowError(/参数类型错误/);
});
});
技巧点分析
- 该 Prompt 针对前端 JavaScript 函数"参数类型灵活(易传错类型)、依赖数组操作"的特点,重点覆盖了"非数组输入、非数字参数"等前端常见错误场景,避免因类型松散导致的线上问题;
- 使用 Jest 的
describe包裹用例集,配合公共mockUsers数据,减少代码冗余;同时通过"断言结果的 activeDays 数组"(如toEqual([60, 45, 30])),精准验证排序逻辑,避免"排序失效"的隐性问题; - 异常场景中使用正则表达式(
/参数类型错误/)断言错误信息,既保证了错误类型的准确性,又避免因错误信息 wording 微调导致测试用例失效(比精确匹配更灵活)。
四、单元测试 Prompt 的调优技巧
即使遵循核心设计原则,初次生成的测试用例仍可能存在"场景遗漏、代码不可运行"等问题,需通过以下技巧调优 Prompt,提升输出质量:
技巧1:补充"反例约束",避免无效场景
当 LLM 生成冗余或不符合业务逻辑的测试用例时(如测试"订单金额计算"时生成"负数折扣",但业务中折扣不可能为负),需在 Prompt 中添加"反例约束",明确"不允许的场景"。
调优前 Prompt 片段 :"生成订单金额计算的测试用例,覆盖正常和异常场景"
调优后 Prompt 片段:"生成订单金额计算的测试用例,覆盖正常场景(折扣≥0且≤订单金额)、异常场景(订单金额<0、运费<0);注意:不允许生成'折扣<0'的测试场景(业务中折扣为非负数)"
技巧2:增加"代码上下文",解决依赖问题
当待测试函数依赖其他工具类(如 Java 的 BigDecimal、Python 的 pandas)时,LLM 可能因未明确依赖导入而生成缺失包的代码。此时需在 Prompt 中补充"依赖导入要求"。
调优前 Prompt 片段 :"生成 Python 数据处理函数的测试用例"
调优后 Prompt 片段:"生成 Python 数据处理函数的测试用例,待测试函数依赖 pandas(需导入 import pandas as pd),测试用例中需包含 pd.DataFrame 类型的测试数据,并在代码开头导入 pandas"
技巧3:指定"断言粒度",提升测试精准度
当测试用例需要验证复杂对象的多个字段(如用户对象的 id、role、activeDays)时,LLM 可能仅断言"结果长度",忽略字段细节。需在 Prompt 中明确"断言粒度",要求"验证关键字段的值"。
调优前 Prompt 片段 :"断言筛选后的用户列表正确"
调优后 Prompt 片段:"断言筛选后的用户列表:1. 长度正确;2. 每个用户的 id 和 role 符合预期(如 id 为1、3,role 为 admin);3. activeDays 排序正确"
技巧4:针对"框架特性"定制 Prompt
不同测试框架有独特语法(如 JUnit 5 的 @ParameterizedTest、Pytest 的 @pytest.mark.parametrize),当需要生成参数化测试用例(用多组数据测试同一逻辑)时,需在 Prompt 中明确框架的参数化语法。
示例 Prompt 片段:"生成 Pytest 参数化测试用例,使用 @pytest.mark.parametrize 装饰器,测试数据为 [ (80, 0, 10, 90), (200, 50, 15, 165), (150, 150, 0, 0) ](每组数据对应 orderAmount、discountAmount、freight、预期结果),断言返回值与预期结果一致"
五、常见问题与解决方案
在使用 Prompt 生成单元测试的过程中,可能遇到以下问题,需针对性调整 Prompt 或处理生成结果:
| 常见问题 | 原因分析 | 解决方案 |
|---|---|---|
| 生成的测试用例缺少关键场景(如未测试边界值) | Prompt 中仅笼统要求"覆盖正常和异常场景",未明确列举边界值(如"11位手机号"的边界是10位、11位、12位) | 在 Prompt 中"明确列出边界场景示例"(如"边界场景:订单金额=100(满减阈值)、99.99(不满阈值)、100.01(超阈值)") |
| 测试代码存在语法错误(如 Java 缺少分号、Python 缩进错误) | LLM 对代码语法的细节处理可能存在偏差,尤其当 Prompt 未明确"代码格式要求"时 | 在 Prompt 中添加"格式约束"(如"Java 代码需严格遵循驼峰命名,每行末尾加分号;Python 代码使用4空格缩进,避免制表符");生成后手动检查语法(或用 IDE 自动格式化) |
| 异常场景未断言错误信息(仅断言抛出异常类型) | Prompt 中仅要求"断言抛出 IllegalArgumentException",未明确"需验证错误信息" | 在 Prompt 中补充"异常信息断言要求"(如"每个异常场景需断言抛出的 IllegalArgumentException 信息为'xxx'") |
| 测试用例依赖未定义的变量(如未创建 mock 数据) | Prompt 未明确"测试数据的定义位置"(如是否在测试方法内创建、是否复用公共数据) | 在 Prompt 中规定"测试数据的组织方式"(如"每个测试用例内定义独立的 mock 数据,变量名以 mock 开头,如 mockUserList") |
六、实战总结
Prompt 驱动的单元测试生成,核心是"将测试需求转化为 LLM 可理解的结构化指令"------需明确"待测试代码信息、测试场景范围、输出格式约束"三大要素,避免模糊描述。不同语言/框架的测试用例,需针对性调整 Prompt 中的"框架语法、参数类型、业务约束":
- 后端语言(Java/Python):重点覆盖"参数合法性校验、异常信息匹配、数值精度(如 BigDecimal 比较)";
- 前端语言(JavaScript/TypeScript):重点覆盖"非数组/非数字输入、DOM 相关依赖(如需 mock document)、排序/过滤逻辑";
- 参数化测试 :明确框架的参数化语法(如 JUnit 5 的
@ParameterizedTest、Pytest 的@pytest.mark.parametrize),减少重复代码。
最终生成的测试用例,需结合"人工校验+IDE 运行验证":人工检查场景覆盖度,IDE 验证代码可运行性,确保测试用例真正起到"保障代码质量"的作用------Prompt 是效率工具,而非替代开发者对测试逻辑的思考。
联系博主
xcLeigh 博主,全栈领域优质创作者,博客专家,目前,活跃在CSDN、微信公众号、小红书、知乎、掘金、快手、思否、微博、51CTO、B站、腾讯云开发者社区、阿里云开发者社区等平台,全网拥有几十万的粉丝,全网统一IP为 xcLeigh。希望通过我的分享,让大家能在喜悦的情况下收获到有用的知识。主要分享编程、开发工具、算法、技术学习心得等内容。很多读者评价他的文章简洁易懂,尤其对于一些复杂的技术话题,他能通过通俗的语言来解释,帮助初学者更好地理解。博客通常也会涉及一些实践经验,项目分享以及解决实际开发中遇到的问题。如果你是开发领域的初学者,或者在学习一些新的编程语言或框架,关注他的文章对你有很大帮助。
亲爱的朋友,无论前路如何漫长与崎岖,都请怀揣梦想的火种,因为在生活的广袤星空中,总有一颗属于你的璀璨星辰在熠熠生辉,静候你抵达。
愿你在这纷繁世间,能时常收获微小而确定的幸福,如春日微风轻拂面庞,所有的疲惫与烦恼都能被温柔以待,内心永远充盈着安宁与慰藉。
至此,文章已至尾声,而您的故事仍在续写,不知您对文中所叙有何独特见解?期待您在心中与我对话,开启思想的新交流。
💞 关注博主 🌀 带你实现畅游前后端!
🏰 大屏可视化 🌀 带你体验酷炫大屏!
💯 神秘个人简介 🌀 带你体验不一样得介绍!
🥇 从零到一学习Python 🌀 带你玩转Python技术流!
🏆 前沿应用深度测评 🌀 前沿AI产品热门应用在线等你来发掘!
💦 注 :本文撰写于CSDN平台 ,作者:xcLeigh (所有权归作者所有) ,https://xcleigh.blog.csdn.net/,如果相关下载没有跳转,请查看这个地址,相关链接没有跳转,皆是抄袭本文,转载请备注本文原地址。

📣 亲,码字不易,动动小手,欢迎 点赞 ➕ 收藏,如 🈶 问题请留言(或者关注下方公众号,看见后第一时间回复,还有海量编程资料等你来领!),博主看见后一定及时给您答复 💌💌💌