ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

自动化测试用例设计:从手工思维到稳定可重复的工程实践

自动化测试用例设计:从手工思维到稳定可重复的工程实践 干测试这行久了你一定会遇到这样的场面手工用例写得漂漂亮亮评审一遍过版本提测后人肉点一遍全绿。然后你把用例原封不动交给自动化去实现结果跑了一周天天有失败的组长过来问这套自动化测试到底能不能用我得说大多数自动化测试用例写不好真不是工具没选对也不是代码能力不够而是思维没转过来——你还在用写手工用例的方式写自动化用例编写。这篇内容我分几个层面把这件事讲透手工用例和自动化用例的差异、一条好用例背后的设计开关、Web/接口/App三种场景的具体写法、AI辅助生成用例的边界最后聊聊评审和面试里真正拉分的点。1. 自动化用例和手工用例的“翻译陷阱”照搬必挂1.1 手工用例的思考顺序和自动化脚本完全相反很多人第一次接触自动化最自然的想法就是把手工用例转成脚本。我在团队里带过不少新人基本都会犯同样的错。手工用例通常是这样写的打开页面输入用户名输入密码点击登录断言出现“欢迎回来”。这部分看起来没问题但手工用例背后缺了一堆东西。手工执行时你的眼睛会自动处理很多事页面没加载完你会等弹窗没关你会主动关验证码你会偷偷找开发要登录失败你还会顺手看看网络是不是断了。但脚本没有这个本能。脚本看到的是一个一个的元素、一次一次的请求、一段一段的响应。手工用例强调“步骤覆盖”自动化用例强调“结果可验证、过程可重复、失败可定位”。这两者的思考顺序完全相反。手工用例是“按用户路径走一遍看有没有问题”自动化用例是“把每一步变成代码让机器反复走每一次走的条件都一样”。所以你写自动化用例时不能问“用户会怎么操作”要问“这一步的输入是什么、前置状态是什么、我怎么验证它做对了”。1.2 能跑一次不算数稳定可重复才是分水岭我见过最典型的案例是这样的某同事写了一条注册用例本地跑一次通过了开心得不行。第二天回归这条用例挂了。查了半天原因是注册用的手机号是写死的昨天注册过之后今天数据库里已经有这个号码再注册就提示“手机号已存在”。这就是手工用例翻译成自动化后最常见的坑之一手工用例里你每次都可以用同一个手机号注册完大不了换一个但自动化脚本每次执行都在同一套环境数据是有残留的。一次通过不是通过能重复执行才算通过机器连续跑一百次不出幺蛾子才算稳定。我当时的处理方式很简单注册类用例的手机号改成时间戳动态生成断言也改成对“注册成功”这个状态的判断而不是对具体文案的判断。从那以后这条用例很少再挂。自动化用例的本质不是“把手工步骤用代码写出来”而是“把一条路径设计成一段在任意时间、任意环境下都能稳定复现的验证逻辑”。2. 一条好用自动化用例的六个隐藏开关2.1 用例优先级与层级进回归还是进冒烟很多人写自动化用例不管三七二十一先写它一百条再说最后全塞进一个文件每天定时执行。跑一个多小时红了一大半没人看也没有人第一时间修。这说明用例分层没做好。自动化用例至少要分两层冒烟层和回归层。冒烟层是核心主流程数量少、执行快、稳定性要求极高每天在CI上跑挂了第一时间响警报。回归层是功能细节、边界、异常流的覆盖数量多可以放夜间任务跑允许少量波动但需要自动整理失败清单和业务模块归属。判断一条用例进哪一层标准只有一个如果它红了团队是否愿意马上停下手里的事去处理。如果不愿意那它就不该出现在冒烟层。把用例分层想清楚比用例数量重要得多。2.2 测试数据设计把自己从数据泥潭里救出来自动化用例对数据的敏感程度超出大多数人的预期。我总结出一句话用例可以重跑数据必须可重置。具体来说有三种数据方案固定数据适合只读类用例比如查询订单、加载详情页。优点是简单缺点是可能被其他用例修改。动态数据适合新增类用例比如注册用户、创建订单。用时间戳、随机数生成唯一值避免冲突。前置数据构造适合强依赖初始状态的用例比如“支付已完成”的订单下单后需要手动改库或调用造数接口。比较成熟的做法是结合后两种用例执行前先清理或造数执行后做清理。接口自动化里可以用更快的“直接删库/调后端接口”方式UI自动化里至少要保证数据能通过界面或数据库回到初始状态。2.3 断言粒度断言用户可感知的结果而不是实现细节断言是自动化用例的灵魂但怎么断言是个学问。最差的断言是只断言状态码比如接口返回200就当成功。问题是有些接口业务逻辑出错时也返回200只是在响应体里放了错误码。更差的断言是断言了与用户无关的内部字段比如数据库自增id等于某个值一旦数据变化就误报。我现在的标准是断言用户或业务方真正关心的结果。接口用例断言三层响应状态、核心业务字段、关键业务状态。比如创建订单接口断言响应里的order_id非空订单状态是CREATED如果再进一步可以查询数据库确认订单数据落库。UI用例断言用户能看到的结果比如登录后右上角显示用户名而不是断言某个被隐藏的日志节点。2.4 前置条件和后置清理用例独立性的根本用例之间最怕相互依赖。A用例建了订单B用例默认这个订单存在结果A挂了B也跟着挂排查的时候看着一大片红色其实源头只有一个。规避这个问题靠的是独立性。独立性的实现要领是每条用例都在自己的前置条件里把数据准备好不依赖其他用例的执行结果。比如要测订单取消先自己在用例开头创建一个订单再去取消它。后置清理不是可选项是必选项尤其是会产生脏数据的用例。干净的数据环境能让你在排查失败时聚焦在代码和产品逻辑上而不是花费大量时间分析“到底是数据污染还是真的bug”。2.5 等待策略与超时动态等待才是UI自动化的命根子做UI自动化最烦的就是元素加载不稳定。不少人写用例时会用固定等待import time time.sleep(3)这种写法不能说完全错但它有一个致命缺陷快的时候浪费三秒慢的时候三秒根本不够。正确做法是动态等待轮询查找元素直到出现为止。Selenium里有WebDriverWaitAppium里也有类似机制。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, login-button)) )动态等待的核心逻辑是给一个合理的最长等待时间比如10秒每0.5秒检查一次元素一旦出现就立即继续等不到才报超时。我见过很多自动化用例从“三天一小挂”变成“两周不挂”就是靠把一堆sleep改成显式等待。2.6 用例命名和分层让失败信息自己说话最后一个小开关是命名。很多团队用例名写的是test_1、test_2失败了一看完全不知道是哪块业务。一条好用例的名字应该能直接说明三件事测什么模块、什么场景、预期什么结果。我的习惯是模块_场景_预期结果比如“order_create_success_with_valid_params”或者中文命名里写“创建订单_正常参数_返回草稿状态”。这样每次失败报告出来不需要打开代码就能基本定位问题。分层方面建议按页面或模块组织目录同一个模块的用例放一起千万不要把所有用例都堆在一个文件里。3. 三类最常见的自动化用例写法实战拆解3.1 接口自动化用例从入参设计到断言的完整模板接口自动化的投入产出比是最高的也是很多团队自动化的起点。它不需要处理复杂的元素定位关注的是入参、出参、状态码、业务逻辑。我通常用Pythonrequestspytest这组工具轻量、生态好。先看一个典型的接口用例import requests import time def test_create_order_success(): # 动态数据避免重复 order_no fauto_{int(time.time())} payload { order_no: order_no, user_id: 10001, items: [{sku_id: A001, quantity: 2}], address: 测试地址 } resp requests.post(https://api.example.com/v1/orders, jsonpayload) # 第一层断言状态码 assert resp.status_code 200 body resp.json() # 第二层断言业务状态 assert body[code] 0 # 第三层断言核心字段 assert body[data][order_status] CREATED assert body[data][order_no] order_no # 第四层断言数据库校验可选 # assert db.query(select status from orders where order_no%s, order_no) CREATED很多接口用例只写前两层遇到几次线上bug后我才发现第三层和第四层才真正有价值。比如第一次写这条用例时接口返回了code0但order_status是PENDING业务上订单还没创建成功这时候只看code0就漏掉了问题。接口用例的数据设计可以更灵活我习惯用pytest的fixture做数据准备和环境切换把base_url、认证token这些公共信息放在conftest里统一管理避免每一条用例都写死。3.2 Web UI自动化用例Selenium场景下的用例化写法UI自动化的核心价值不是替代手工测试而是覆盖“高频、重复、基础”的回归路径。Selenium是目前应用最广的Web自动化框架配合pytest或TestNG都很好用。我写UI用例时有一个原则步骤精简到最少但验证要做足。看一个登录用例的例子from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import pytest pytest.fixture def driver(): driver webdriver.Chrome() driver.maximize_window() yield driver driver.quit() def test_login_success(driver): driver.get(https://example.com/login) # 遇到动态加载用显式等待 WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, username)) ).send_keys(tester001) driver.find_element(By.ID, password).send_keys(password123) driver.find_element(By.ID, loginBtn).click() # 断言用户感知结果 WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.CLASS_NAME, welcome)) ) assert 欢迎回来 in driver.find_element(By.CLASS_NAME, welcome).textUI用例的稳定性大头在等待和元素定位上。wait用显式等待替代sleep元素定位优先用id、name等稳定属性少用动态生成的class或绝对xpath。另外一条UI用例里尽量只测一条路径不要在登录用例里顺便把下单也测了否则登录失败时下单相关的日志会干扰你定位问题。3.3 App自动化用例Appium下容易被忽视的稳定策略App自动化比起Web自动化还要面对设备状态、app启动速度、弹窗、网络切换这些额外的不确定因素。Appium是主流的App自动化框架支持iOS和Android但稳定性的挑战比Web更大。写App用例时我会额外注意三件事。第一件事是启动策略每次执行用例时优先冷启动app避免一个用例跑到一半app还在上一个页面导致定位失败。第二件事是元素定位Android端优先用resource-idiOS端优先用accessibility idtext定位在中文环境下常常因为编码问题出意外。第三件事是系统弹窗处理特别是iOS的定位权限弹窗、Android的广告弹窗最好在启动脚本里统一处理掉不然它们会在任意一步执行时突然出现打断流程。如果遇到控件无法用层级结构定位的场景比如某些自定义游戏引擎渲染出来的元素可以考虑用图像识别的方式比如SikuliX这类工具来做补充。但图像识别对分辨率、环境亮度都很敏感稳定性天然不如原生控件定位我一般只把它当作最后手段不会用来覆盖大面积用例。4. AI辅助自动化用例编写能用但别裸奔4.1 让Claude/Cursor这类工具生成用例的正确姿势这两年AI辅助写代码越来越常见Claude、Cursor、Codex这些工具也确实能生成像模像样的自动化用例。但我观察到一个现象让AI直接生成一堆用例很容易真正把它生成的用例喂到现有项目里还能稳定跑却没那么简单。问题在于AI不熟悉你的项目和业务。你直接说“帮我写一个登录用例”它写出来的东西大概率是标准化的demo代码能跑通自家测试站但放到你们的业务系统里就废了。正确的姿势是给它足够的上下文。我现在的做法是把被测页面的HTML结构或接口文档片段贴给它同时告诉它项目的登录方式、被测环境的地址、已有的封装函数。比如我会这样描述我们有一个订单查询页面输入订单号后点击查询接口是GET /v1/orders/{order_no}返回JSON字段status值为SUCCESS表示成功。请写一条pytest用例调用已有的api_client.get_order方法动态生成订单号断言status为SUCCESS并校验订单金额等于100元。这样AI生成的用例至少大方向是对的我再人工调整一下细节速度比从零写快很多。AI的真正价值是帮你把重复的骨架搭好你只需要做业务判断和审查。4.2 AI生成用例的失效场景和人工把关重点AI生成的自动化用例有两个明显盲区。第一个盲区是业务规则。比如一个促销活动用例要求用户只有在未领取过优惠券的前提下才能参与这种“状态依赖”的逻辑AI如果不知道你们业务里有这个限制写出来的用例就是错的。第二个盲区是新功能本身。AI的训练数据有截止时间新上线的功能、新的交互模式它是不知道的只能靠你补充知识。所以我的把关重点有三个数据是否冲突、断言是否充分、清理是否到位。AI生成的用例经常动态数据用不好比如用了固定的“test_user”第一次跑通过第二次跑就报“用户已存在”。另外断言容易写得过于简单只检查一个响应码或一个元素存在。这些都需要人工逐条审。我见过有些团队急着上AI自动化让AI一口气生成了几百条用例看都没看就塞进CI结果第一天就把构建搞红了。我的态度是AI不是不能用来写自动化用例但生成出来的代码一定要有人审用例设计环节可以借力质量审核环节不能放手。4.3 一个AI辅助生成接口用例的实操示例这里放一个我在实际项目中用AI辅助写接口用例的简略过程。当时要给一个订单退款功能写接口用例我把接口文档摘了一段给Claude然后补充了三点退款金额不能大于订单实付金额、同一订单不能重复退款、退款后订单状态变成REFUNDED。AI给出的第一条用例是正常的“退款成功”场景没问题。第二条它写了“退款金额大于实付金额”这条它猜到了边界但断言只检查了状态码400。我手动补充了响应体里错误码的断言因为开发用的错误码体系比状态码更详细。第三条“重复退款”AI写得很粗略只调了一次接口我改成连续退两次第二次的断言完全不一样从“成功”变成了“失败并返回已退款”。这个例子说明AI可以帮你把用例的骨架铺开比如想到正常流和部分异常流但真正的边界条件、状态流转细节还是要人工去补、去改。我不觉得这是坏事自动化测试本来就是需要业务理解的工作AI替不了这部分。5. 用例评审与面试高手验证你自动化用例功底的三个方向5.1 用例评审时老手真正会挑的毛病我在团队里做过不少次用例评审发现资深测试看自动化用例重点从来不是“代码写得好不好看”而是下面三个问题。第一个问题是这条例你能不能独立跑。如果一条用例需要另一条用例先执行或者需要固定某个测试账号存在评委大概率会打回。独立性的标准是单独挑出这条用例它能自动完成所有前置准备跑完不影响其他用例。第二个问题是断言匹配的是“用户价值”还是“实现细节”。评委看到断言里有数据库自增id、有某个内部traceId就会问一句“如果页面显示正确但底层字段变了这条用例是要报错还是该正常”如果你答不上来说明你的断言粒度选错了。第三个问题是失败之后好不好定位。好的用例失败信息会告诉你业务模块、场景、预期和实际值差的用例只会抛出一个ElementNotFound异常连是哪一步找不到也不清楚。很多团队会给用例加日志每完成一个关键步骤就记录一句话失败时Stack trace前直接能看到最后一步做什么省去大量排查时间。5.2 三个高频自动化用例面试题与答题思路面试的时候自动化用例相关的题我见得太多了常问的其实就那么几道但多数人答不到点子上。第一道“一条好的自动化测试用例有哪些特征”很多人张口就背稳定、快速、独立、可重复这些都对但我会期待你说出“断言是针对业务结果的”“数据是可重置的”“用例之间没有依赖”这种有实操味道的点。更老练的还会补一句“用例要能被快速执行并对失败信息做到可追溯”。第二道“如何保证UI自动化用例的稳定性”这是个经典送命题。多数人答用显式等待、用稳定定位符。这两个都没错但我会再追问“如果页面发生了ajax异步加载你怎么定位”其实是要你答显式等待和轮询机制的具体配置。再进一步有经验的会说“从根本上减少UI层用例的数量把核心逻辑下沉到接口层UI只覆盖主流程”。这个回答通常是最拉分的。第三道“接口自动化用例和UI自动化用例的取舍”这里我想听的不是“接口快UI慢”这种谁都知道的话而是你能否结合场景支付流程这种涉及多渠道回调的UI层必须覆盖订单状态变更这种逻辑复杂的接口层优先。能说出“用接口覆盖业务规则用UI覆盖用户主流程”这个组合思路基本就能过关。回到写用例本身我最后分享一个自己一直用的习惯。写完一条用例我会假装自己是另一个同事盯着这条用例问三件事如果测试数据变了它会不会稳如果元素变了失败信息我能看懂吗如果三个月没人维护它还能跑吗这三个问题能过滤掉至少一半的无效自动化用例。自动化用例不是写得越多越好是留下来的每一条都能稳定地证明一件具体的事。
返回列表