尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

四种自动化测试模型详解:线性、模块化、数据驱动、关键字驱动怎么选?

四种自动化测试模型详解:线性、模块化、数据驱动、关键字驱动怎么选? 最近面试了一个做自动化测试的候选人聊到测试模型时对方把框架和脚本混在一起讲越说越绕。我意识到一个现象很多做了两三年自动化的测试工程师能熟练操作Selenium、pytest、JMeter但如果你问他“当前项目用的是哪种自动化测试模型”大多数人答不上来。自动化测试模型这个概念虽然古早却直接决定了自动化项目的维护成本、扩展空间和团队门槛。它和框架不是一回事模型更像一种组织脚本、数据和页面对象的结构思想框架只是承载思想的容器。这篇文章我准备把四种最常见的自动化测试模型——线性模型、模块化驱动模型、数据驱动模型、关键字驱动模型——用实际可运行的例子拆开讲一遍把每种模型的优缺点说透顺便讲讲我在实际项目里怎么选型。如果你正在搭自动化体系或者手里已有的自动化脚本越来越难维护那这篇文章应该对你有点用。我不准备讲太学院派的理论尽量用真实的脚本片段和你平时会遇到的问题来说明。1. 先厘清一个概念模型、框架和脚本到底在说什么1.1 自动化的三个层次很多团队选自动化工具时上来就比 Selenium 还是 Cypress、pytest 还是 Robot Framework比了半天选错方向是因为把三个层次混在一起了。第一个层次是测试脚本就是具体的一段代码完成某个页面操作、接口调用或断言动作。第二个层次是测试框架提供执行入口、结果收集、报告生成这些基础设施它解决的是“怎么跑”的问题。第三个层次才是测试模型描述的是脚本与脚本之间、脚本与数据之间、脚本与页面对象之间如何组织解决的是“脚本怎么写才不容易烂”的问题。用一个不太精确但好记的说法框架是房子模型是房子的户型。同样是 100 平米的房子户型设计得好不好直接决定了住进去之后舒不舒服。很多自动化项目框架选得很主流但脚本写出来还是一团乱麻本质上就是户型设计出了问题。1.2 四种模型分类的依据谁掌握变化四种模型划分的底层逻辑其实只有一个问题变化发生的时候改哪里线性模型里变化直接改脚本脚本即用例改需求等于重写脚本。模块化驱动模型里变化被拆到公共模块和用例脚本两边改动尽量收敛在一个模块内。数据驱动模型把变化赶进数据文件脚本逻辑保持稳定改数据就行。关键字驱动模型把变化更彻底地赶进了一张表格非技术人员也能通过改表格来新增用例。可以这样理解模型本质上是在回答“测试数据、业务流程、对象定位、执行逻辑这四样东西分别放在哪个位置”。选模型不是选工具而是选一套应对变更的策略。1.3 为什么选错模型会让自动化成本失控我见过不少自动化项目死在 3 到 6 个月这个阶段原因往往不是工具不行而是脚本之间的复用关系从一开始就没设计对。举个例子一个电商下单流程的 UI 自动化如果全部按线性脚本写十条用例里可能六条都要走“登录-搜索-加购-结算”这段流程就要被复制六遍。等到前端按钮改了定位那不是改一处是改六处。改完发现漏了一个回归报告里就会出现莫名其妙的失败。这就是线性模型在变更频发场景下的真实成本。反过来如果一开始就把登录、加购这些动作抽成模块改动就收敛在一个地方用例脚本本身基本不动。这个差异在项目前期不明显但到了脚本量两三百条的时候差距就是几天的维护工时和几个小时的维护工时的区别。2. 线性模型实例从一段“最笨但能跑”的脚本说它的生存空间2.1 一个最原始的线性脚本长什么样线性模型也叫脚本录制模型指一条用例就是一段从头到尾顺序执行的脚本不做任何抽象和封装。它的典型形态是录制回放或者手工一步步写出的 WebDriver 脚本。我用 Python 加 Selenium 写一个最典型的登录脚本示例from selenium import webdriver from selenium.webdriver.common.by import By driver webdriver.Chrome() driver.get(https://demo.example.com/login) username driver.find_element(By.ID, username) password driver.find_element(By.ID, password) login_btn driver.find_element(By.ID, loginBtn) username.send_keys(admin) password.send_keys(123456) login_btn.click() # 断言登录成功 assert driver.find_element(By.CLASS_NAME, user-info).text admin driver.quit()这段脚本从打开浏览器到断言成功一条路走到黑没有函数、没有类、没有数据分离。很多测试工程师入行的第一条脚本都是这个形态。你把它跑起来登录流程没问题断言也正确好像一切都很顺利。这恰恰是线性模型最迷惑人的地方单条脚本写起来太爽了爽到让人忽略了它未来会带来的维护问题。2.2 优点三步见效调试直接线性模型最大的优点是门槛低、见效快。只要会基础编程按着操作顺序把步骤翻译成代码脚本就完成了。对完全没接触过自动化的团队来说用线性模型做第一个 POC概念验证可以在一两天内跑通一个真实场景让团队成员亲眼看到自动化的价值这个心理层面的作用比技术本身重要得多。它的调试体验也很直接。哪一步失败就定位哪一行不需要翻模块调用链不需要理解封装层级报错信息就是当前行。因为这种做法临时任务的脚本通常都会用线性模型来写。还有一个容易被忽视的优点线性脚本在执行顺序上完全可控不需要考虑测试执行框架带来的随机顺序、用例并发、数据污染等问题。当你只需要验证一个孤立功能时线性脚本是最快的方式。2.3 缺点改了需求就得重写脚本线性模型的致命伤是复用率为零和变更成本极高。先说复用。六条用例都要登录脚本就复制六遍登录代码。一旦登录按钮的 id 变了六个地方都要改。下一个工程师接手看到一段几百行的线性脚本描述一个复杂业务流程他得从第一行读到最后一行才能知道脚本在做什么这个理解成本是非常沉重的。再往下还有个问题线性脚本没有把“业务动作”和“底层定位”分开。断言的是一段 xpath 表达式点击的也是一段 xpath 表达式整个脚本被定位器占满了。业务人员看脚本根本看不懂纯靠代码维护一旦接口字段、页面结构、交互方式发生变化脚本就是一次大面积重写。这种脚本数量一旦到上百条维护自动化用例的时间可能超过手工执行用例的时间项目也就走到了“自动化负收益”的境地。2.4 什么情况下我反而会主动用线性模型虽然前面把线性模型说得不堪但实践中我其实会主动用而且用得不少。第一类场景是临时冒烟验证。比如产品经理说新版本改了一个按钮样式我需要快速确认页面还能不能正常打开提交写一个二三十行的线性脚本跑一遍用完就删不心疼。第二类场景是环境数据准备比如测试环境需要批量造出十个不同等级的商品这本质上是一次性任务不值得为它设计模块。第三类是自动化起步团队做 POC我通常建议先用线性脚本快速让团队看到可行性等大家接受自动化这条路了再立刻转入模块化改造。关键判断标准就一条这段脚本会不会被复用、会不会在业务变更后还要继续活很久。不会就不封装会就必须做结构化设计。3. 模块化驱动模型把重复动作变成可复用的零件3.1 模块化模型的核心思想封装与分层模块化驱动模型也叫模块化测试框架核心思想是分治和复用。把测试过程拆成一个个独立的公共模块每个模块完成一个单独的业务动作或技术动作用例脚本再把这些模块组装起来。拿最熟悉的场景类比一个手工测试工程师做回归测试不会每次测试都从打开浏览器、输入用户名密码开始他一定有个“登录测试账号”的习惯性操作。模块化模型就是把类似的动作固化成标准化零件用的时候直接取零件组装。分层设计通常有工具层、操作层、用例层三层工具层封装浏览器驱动、等待机制、通用日志等基础能力操作层封装具体业务动作比如登录、搜索、创建订单一个动作对应一个类或函数用例层只负责编排业务步骤和断言结果不在里面写具体的定位器也不写脏逻辑。3.2 一个登录场景的分层实例我用一个简单但真实的分层例子讲清楚。以页面对象模式Page Object Model为例先把登录页面抽象成类# 操作层登录页面对象 from selenium.webdriver.common.by import By class LoginPage: def __init__(self, driver): self.driver driver def open(self, url): self.driver.get(url) def login(self, username, password): self.driver.find_element(By.ID, username).send_keys(username) self.driver.find_element(By.ID, password).send_keys(password) self.driver.find_element(By.ID, loginBtn).click() def get_login_user(self): return self.driver.find_element(By.CLASS_NAME, user-info).text然后写用例层直接调用操作层的方法# 用例层 def test_login_success(): driver webdriver.Chrome() login_page LoginPage(driver) login_page.open(https://demo.example.com/login) login_page.login(admin, 123456) assert login_page.get_login_user() admin driver.quit()对比一下线性模型里的登录脚本最直观的差异是现在用例层只表达了三件事——打开登录页、执行登录、断言结果每一件事的复杂度都被“藏”到了操作层里。另一个用例也需要登录时不必复制这段流程直接创建一个 LoginPage 对象调用 login 方法就行。定位器如果变化只改 LoginPage 类里的 locator所有调用它的用例全部受益。这就是模块化模型把“变化点”收敛到一处的核心价值。3.3 优点与缺点模块化模型在四种模型里属于性价比最高的中庸之选对多数测试团队的收益最稳定。优点方面第一是复用性高只写一次登录逻辑就能在成百上千条用例中被反复调用。第二是变更局部化页面结构变了定位器修改范围被限制在对应的页面对象里不会牵一发动全身。第三是用例可读性好用例层读起来像一份用例步骤清单新同学接手也容易看懂。第四是支持并行开发不同人员负责不同页面的对象封装只要接口约定清楚模块之间互不干扰。缺点方面它比线性模型有更高的设计门槛要求团队具备基本的代码结构能力。如果封装得不好容易出现“过度抽象”问题为了复用而建很深的继承关系、为了参数灵活搞了七八个可选参数最后用例层一行调用背后是三四十行调用链出了问题要层层追查。模块间的依赖关系如果设计得不清楚还会出现循环依赖A 模块调 B 模块、B 模块又调回 A 模块。3.4 模块化模型最容易掉进去的坑模块化模型最大的坑不是封装得不够多而是封装得过于拼命。我见过一个项目页面对象封装到第五层基类套基类一个按钮的定位器要经过四个类的继承才能找到。后来产品做了一个页面改版改一个定位器足足动了四个文件因为基类里存了通用定位规则子类又重写覆盖。他团队的人查看定位器要沿着继承链一层层找这种抽象已经变成了负资产。我的实操经验是页面对象保留“这个页面上有什么”和“这个页面能做什么”就足够了不要追求把所有相关页面都塞进一个超大类更不要在工具层里塞业务判断。跨页面的业务流比如下单流程应该写在用例层或者专门的服务层里不要让页面对象之间互相调用去拼凑业务。抽象是有代价的每多一层封装调一次试一次的成本就会增加适度为止。4. 数据驱动模型让测试数据和脚本彻底分离4.1 数据驱动模型的应用场景与思想数据驱动模型的核心思想是测试流程固定变化集中在数据。脚本只负责执行逻辑测试数据从代码里抽离出来放到外部文件或数据库里一条脚本配合多组数据运行。典型应用场景是接口自动化测试。同样一个登录接口要覆盖正常登录、密码错误、用户不存在、参数为空、并发登录等场景接口的调用步骤完全一样变的只是请求参数和期望结果。这种情况下把数据写在脚本里会让代码极度膨胀数据驱动模型就是为此而生。UI 测试里也很常见比如一个表单提交页面输入框的组合非常多数据驱动可以让你只维护一段表单填写逻辑和一份测试数据每条数据跑一遍流程覆盖率比人工手写脚本高出不少。4.2 一个带数据源的参数化实例我用接口测试举例这是数据驱动模型最纯粹的形态。测试数据放在 JSON 文件里[ {username: admin, password: 123456, expect: 200, desc: 正常登录}, {username: admin, password: wrong, expect: 401, desc: 密码错误}, {username: , password: 123456, expect: 422, desc: 用户名为空} ]测试脚本从数据文件读取数据使用 pytest 参数化逐条执行import pytest import requests def load_json(file): with open(file, r, encodingutf-8) as f: return json.load(f) pytest.mark.parametrize(case, load_json(test_data/login_data.json)) def test_login_api(case): resp requests.post( https://demo.example.com/api/login, json{username: case[username], password: case[password]} ) assert resp.status_code case[expect], f{case[desc]} 失败实际状态码 {resp.status_code}新增一条用例只需要往 JSON 文件里加一个对象脚本一行都不用改。执行失败时描述信息会被带进 pytest 输出定位是哪一条数据出的问题也很方便。这里有个值得注意的设计细节每条数据都带一个 desc 字段这是我最常提醒团队的。很多团队的数据驱动用例失败后报告只显示参数值和期望值完全不告诉你这条数据是干什么的排查问题要对着原始数据顺藤摸瓜。加一个描述字段报告可读性会明显改善。4.3 优点与代价数据驱动模型的优点非常清晰。第一脚本和数据解耦业务测试人员经过简单培训后可以直接维护数据文件不需要碰代码。第二一条脚本覆盖大量数据组合用极少的代码量获取极高的覆盖率。第三回归执行效率高同一逻辑跑十几条数据对接口兼容性、边界值覆盖的帮助是直接的。但它的代价也常被忽略。数据文件本身会成为新的维护对象一旦数据文件里的用例积累到几百条不加管理的用例会变成垃圾堆。数据自身的问题也可能掺进来比如测试数据里的脏值导致接口返回 500这在自动化报告里会表现为产品缺陷实际却是数据责任人这个误报会让开发团队对自动化报告失去信任。还有一类场景不适合数据驱动测试步骤本身随数据发生剧烈变化的场景。比如有的用例需要先登录后下单有的用例只需要验证未登录拦截它们流程差异太大硬塞进同一个数据驱动模板脚本里全是 if-else 分支代码难看执行也乱。4.4 数据文件失控的前兆数据驱动模型做一段时间后会暴露一些失控征兆我列几个常见信号数据文件里出现大量重复用例同一个参数组合被不同的人各加了一遍所有业务场景塞进同一个 Excel 文件里面十几个工作表单各自为政数据增长很快但没有人清理无效历史数据自动化失败的比例里数据本身原因占到三成以上。我的建议是数据按业务模块拆分成多个文件比如登录数据、订单数据、支付数据分开维护每条数据必须带描述字段和归属标识数据文件纳入版本管理任何修改走代码评审流程定期执行数据治理把重复、无效、过期数据清掉。做到这几点数据驱动模型才能长期保持健康。5. 关键字驱动模型把人人能写的表格变成可执行用例5.1 关键字驱动的本质业务动作词表关键字驱动模型在自动化平台里最常见Robot Framework、Katalon Studio以及很多低代码自动化平台本质都是在做关键字驱动。它的核心思想是把测试步骤抽象成一组“关键字”每个关键字对应一个底层操作函数用例则写成一张按顺序填充关键字的表格由执行引擎逐行解释执行。再形象一点线性模型像一份事无巨细的菜谱每一步都写“拿起锅”“打开火”“倒油”关键字驱动模型则像一份标准菜谱只写“热锅”“下油”“爆香”厨师执行引擎知道这些动作怎么完成写菜谱的人不需要懂厨具操作。这意味着编写用例的人不需要了解底层代码只要知道关键字表怎么填就能组织测试流程。这是关键字驱动模型与前面三种模型最本质的区别前三种模型要求写用例的人具备编程能力关键字驱动模型对业务测试同事很友好。5.2 表格用例与执行器骨架假设我们要用关键字驱动实现一个登录场景用例表格长这样关键字定位信息参数open_url无https://demo.example.com/logininput_textidusernameadmininput_textidpassword123456clickidloginBtn无assert_textclassuser-infoadmin执行引擎读取表格的每一行根据关键字分发到具体的函数def execute_step(step, driver): keyword step[关键字] if keyword open_url: driver.get(step[参数]) elif keyword input_text: driver.find_element(*parse_locator(step[定位信息])).send_keys(step[参数]) elif keyword click: driver.find_element(*parse_locator(step[定位信息])).click() elif keyword assert_text: actual driver.find_element(*parse_locator(step[定位信息])).text assert actual step[参数], f断言失败期望 {step[参数]}实际 {actual} def execute_test_case(steps): driver webdriver.Chrome() for row_number, step in enumerate(steps, start1): try: execute_step(step, driver) except Exception as exc: raise AssertionError(f第 {row_number} 行执行失败关键字 {step[关键字]}: {exc})表格里的每一行都被翻译成一次真实操作哪天产品加了一个“记住登录状态”的选项只需要新增一个勾选关键字所有会用这张表的人都能在用例里增加这一行无需改代码。这种扩展方式在测试团队和非技术业务人员混杂的场景里体验是革命性的。5.3 优点和缺点关键字驱动模型的优点第一用例可读性非常高表格本身就像自然语言写成的测试步骤评审的时候业务人员也能看得懂。第二业务人员可以参与用例编写把测试设计能力从代码中解放出来。第三关键字是跨页面、跨场景复用的同一个输入关键字可以在所有表单场景里使用。第四它是实现测试平台化和低代码自动化的基础。但代价同样大执行引擎和关键字库的设计在前期投入很大相当于做一个小型开发框架。关键字的粒度非常难把握关键字如果定义得太细比如“输入文本”“点击按钮”本质上只是把一句代码换成一个单元格表格会变得冗长写用例的人依然要理解控件定位如果定义得太粗比如一个“下单流程”关键字它又回到了模块化模型的老路失去灵活性。调试也是个问题。关键字模型执行出错后报错信息往往在引擎里用例编写者很难直接从表格看出问题根因是什么。如果引擎没有做完善的日志和步骤追踪一次失败定位可能需要同时看三层内容。5.4 做关键字驱动时最需要小心的地方我在实际项目里部署关键字驱动模型通常要求团队满足三个条件才算合格。第一关键字定义必须按“业务动作”而不是“控件操作”来设计。“输入文本”“点击按钮”不是业务关键字“填写登录信息”“提交订单”才是。只有业务关键字才能让不懂代码的测试人员直接使用。第二执行引擎必须对每一行用例做差分日志记录执行到了哪一行、每个关键字的入参出参、耗时、失败原因。没有这个日志关键字驱动就是黑盒出问题只能对着表格发呆。第三关键字的文档必须同步维护。每个关键字的名称、参数列表、适用前提、返回值都写清楚不然同一个操作有人叫 input_text有人叫 enter_text关键字库很快就会变成一团混乱。还有一个提醒关键字驱动模型不等于完全不需要代码。参与设计关键字库和引擎的人需要比普通测试开发更高的工程能力。如果你团队里没有这样的人不要轻易走这条路硬上的话关键字库会变成一座新的维护山。6. 四种模型横评优缺点对照表与我的选型经验6.1 一张表看完四者的差异把四种模型放到一张表里对比维度选五个编写门槛、复用性、变更成本、对人员要求、典型适用场景。对比维度线性模型模块化驱动数据驱动关键字驱动脚本编写门槛最低中等中等引擎门槛高用例编写低代码复用性无高脚本层面高关键字层面高需求变更成本高需整体重写低局部修改低改数据即可低改表格即可编写者技术要求会基础代码会编程设计能力会编程数据规范用例编写者无需编程典型适用场景冒烟验证、一次性脚本UI 回归、常规业务自动化接口测试、表单类批量验证平台化测试、低代码自动化这张表只能当参考不能当教条。比如数据驱动模型在接口测试中写起来很容易但在复杂 UI 流程中同样会碰到前置条件和执行顺序管理的问题实战难度完全不同。6.2 选型要看团队构成而不是功能列表我见过许多团队在选择自动化测试模型时把四种模型的优点列出来试图做一个“集所有优点于一身”的方案最后做出来的东西四不像。选型第一原则是看这个团队到底有什么人。如果团队全是功能测试工程师没有专职测试开发我通常会推荐关键字驱动模型。因为只有它能做到让功能测试人员不写代码就维护用例其他模型写出来之后大概率没人维护。如果团队有一两个测试开发加若干会基础代码的测试工程师模块化模型加数据驱动是最稳的组合测试开发负责封装底层组件测试工程师负责写用例和补数据分工清晰成本可控。如果项目是被开发团队主导的比较适合数据驱动模型和关键字驱动模型让测试人员专注生产数据和业务场景把自动化执行能力收拢到一个平台里。团队构成决定了模型能活多久功能清单决定不了。6.3 混合模型才是多数项目的常态实际项目里很少会有一个项目从头到尾只使用一种模型。我在项目里最常见到的组合是“模块化 数据驱动”页面对象做模块化封装测试数据和参数化按数据驱动的方式外置UI 和接口测试都用这套逻辑。还有一些项目在单个自动化平台上引入了关键字驱动给业务人员提供低代码编写入口底层仍然是模块化封装这种“外壳关键字、内里模块化”的架构在大规模测试团队里效果很好。我自己的默认组合是接口自动化优先数据驱动因为接口测试流程最稳定UI 回归测试优先模块化模型加页面对象因为页面变更天然适合局部收敛临时验证用线性脚本用完就丢如果团队有平台化诉求再在现有关键字驱动和模块化之间搭一层轻量桥给业务人员开一个可用的用例编辑入口。不要追求纯正模型要按模块的稳定性和团队能力混合使用。6.4 从一种模型迁移到另一种时怎么过渡如果项目里已经有大量线性脚本想往模块化或数据驱动迁移不要一次推翻重写。彻底重写会导致两个后果迁移周期太长业务等不起新旧脚本并行期间回归能力断档没人敢发版本。正确做法是先搭骨架。把最常用的操作比如登录、搜索、创建订单先抽成模块再把经常变化的数据比如账号、关键字、期望结果外置到数据文件最后把线性脚本一条条移植到新用例结构上跑通一条删一条旧的。迁移期间新旧脚本并存我给团队的建议是设置一个“迁移窗口期”以新用例通过量和旧用例衰减量为两个指标每周看进展。等新用例覆盖率达到对应模块手工用例的八成以上再下线旧脚本。这个过程会比较慢但它不会打断正常的测试交付节奏。我个人在实际项目中的一个深刻体会是模型选择的真正关键点不在于选得高级而在于选得合适。很多团队在自动化初期过于迷信某些酷炫的框架和模型做出来的东西看起来很完整实际用起来却没人维护。与其这样不如从小处着手先把线性脚本跑起来再逐步演进到模块化、数据驱动甚至在合适的时机尝试关键字驱动。真正决定自动化项目命运的从来不只是一个模型的名字而是你把变化点放在了正确的位置并且有人持续用心维护它。最后再分享一个细节无论你最终选了哪种模型确保新加入团队的同事能根据报告、日志和数据文件在三十分钟内定位到一条失败用例的原因这个标准达标了模型才算落地成功。
返回列表