拓冰建站拓冰建站
首页 / 资讯中心 / 正文

软件测试零基础入门:测试基础与while循环实操指南

很多准备入行软件测试的同学最常问的一个问题不是“测试方法有哪些”而是“我到底应该先学什么为什么看了很多理论一进项目还是不会干活”这个问题我在辅导新人时经常遇到。网上关于软件测试的资料非常零散讲概念的只讲概念讲工具的只讲工具讲代码的又默认你已经会编程了。本文想做的事情是把零基础入行最需要补的两块短板一次性串起来——测试基础和代码基础。这里选了一个很典型、也足够简单的代码知识点while 循环把它放进测试用例设计、接口自动化、项目实战里去理解。读完本文你会掌握软件测试是什么一条完整的测试流程长什么样测试用例怎么从需求里一步步拆出来while 循环的语法、退出条件、常见死循环坑while 循环在自动化测试中的真实用法请求重试、服务等待、批量造数一个登录模块 商品查询接口的自动化测试小项目从启动被测服务到 pytest 执行全部跑通。内容覆盖入门和进阶两个部分新手建议按顺序读有基础的同学可以直接跳到第 4 章或第 5 章。1. 软件测试基础先搞清楚测什么、怎么测1.1 软件测试是什么如果只用一句话解释软件测试就是通过人工或自动化的方式验证软件是否满足需求并在发现不满足时把问题暴露出来。很多新人会把“测试”理解成“找茬”实际上测试的核心不只是找 bug。一个测试工程师更要关注的是功能是否完整该有的功能有没有功能是否正确不该有的行为有没有异常情况下系统是否容易崩溃用户实际使用是否顺畅性能、兼容性、安全性是否达标。从职业发展角度来说软件测试是一个比较适合作为技术入门的岗位因为它的起点不需要像开发那样掌握全套框架但它对逻辑能力、细心程度、沟通表达能力要求很高。后续如果走自动化测试、性能测试、测试开发方向也需要慢慢把代码能力补起来。1.2 一条完整的软件测试流程在真实项目中测试工作不会上来就写用例。通常先有需求再谈计划然后才是设计用例、执行和回归。需求评审 → 测试计划 → 用例设计 → 用例评审 → 执行测试 → 缺陷管理 → 回归测试 → 测试报告这八个步骤中新手最容易忽略的是“需求评审”。很多人认为测试只是照着开发结果看一遍实际并不是。需求评审阶段测试人员要从用户角度提出疑问某个字段到底是不是必填登录失败 5 次是当天锁定还是永久锁定这个“删除”是逻辑删除还是物理删除超时时间是多少超过后用户看到什么提示这些细节如果不提前确认测试用例就没法写准确。即使你当前只是自学练手也要养成一个习惯所有用例都要有需求依据不能靠猜。1.3 测试基础的知识框架“测试基础”这个词听起来很虚但从面试和实际工作来看它一般包含下面几块知识模块要掌握的内容测试理论黑盒测试、白盒测试、静态测试、动态测试等概念测试流程测试计划、用例设计、执行、缺陷管理、测试报告用例设计等价类、边界值、场景法、错误推测、因果图缺陷管理bug 优先级、bug 生命周期、bug 复现步骤怎么写工作协作需求评审、用例评审、与开发和产品沟通技巧技术基础Linux、数据库、网络协议、简单脚本编写很多人在网上搜“软件测试学习路线”看到一大堆内容之后反而不知道从哪里开始。我的建议是先做一轮手工功能测试项目再补自动化。手工测试能帮你建立对质量的敏感度自动化只是把重复的工作用代码替代并不能帮你理解业务逻辑。2. 测试用例设计从需求到可执行步骤2.1 常用黑盒测试方法设计测试用例时最常用的方法有这几种等价类划分把输入数据划分为有效等价类和无效等价类每一类取一个代表值。边界值分析针对输入范围的边界进行测试最容易发现 bug 的地方往往是边界。场景法模拟用户真实操作路径覆盖主流程和备选流程。错误推测法根据经验猜测哪里容易出错比如密码是否会被空格干扰、用户输入超长内容是否崩溃。因果图/判定表当多个输入条件之间有组合关系时使用。对于新手来说等价类和边界值是最基础、最容易上手的两个方法。下面用一个登录功能来演示。2.2 登录功能用例设计示例假设需求如下用户名必填长度为 620 个字符只能包含字母、数字、下划线密码必填长度为 616 个字符用户名和密码正确才能登录成功连续错误 5 次账号锁定 30 分钟。根据这个需求我们可以抽取一部分用例用例编号用例名称前置条件测试步骤测试数据预期结果TC-LOGIN-001登录成功用户已注册输入正确用户名密码点击登录admin / 123456登录成功跳转首页TC-LOGIN-002密码错误用户已注册输入正确用户名、错误密码admin / 000000提示“用户名或密码错误”TC-LOGIN-003用户名为空无密码输入正确用户名为空空 / 123456提示“用户名不能为空”TC-LOGIN-004密码为空无用户名输入正确密码为空admin / 空提示“密码不能为空”TC-LOGIN-005用户名长度边界用户已注册输入 6 位合法用户名和正确密码abc123 / 123456登录成功TC-LOGIN-006用户名长度上边界用户已注册输入 20 位合法用户名和正确密码20 位合法字符 / 123456登录成功TC-LOGIN-007用户名超长无输入 21 位合法字符21 位合法字符 / 123456提示格式不正确无法提交TC-LOGIN-008密码长度下边界用户已注册输入正确用户名和 6 位密码admin / abc456登录成功TC-LOGIN-009密码超长无输入 17 位密码admin / 17位密码提示密码长度不合法TC-LOGIN-010连续失败 5 次无用同一账号连续输错密码 5 次admin / 错误密码第 5 次后账号锁定设计用例时有一个很重要的原则不要把多个验证点堆在一个用例里。比如“用户名错误且密码为空再点击登录”预期结果到底怎么算很难判断是哪一个字段的错误提示。除非是验证多个错误同时存在时的提示优先级否则不要混在一起。2.3 用例评审与维护用例写完之后还要做评审主要看三点需求覆盖是否完整主流程、异常流程、边界值是否都覆盖到了用例之间是否重复、冗余预期结果是否清晰别人拿过来也能照着执行。当需求变更时不能只改代码和页面测试用例也要同步更新。很多项目后期用例失去价值就是因为需求改了好几版用例却还停留在最初版本。3. while 循环自动化测试脚本中的循环基础3.1 为什么测试工程师要掌握 while 循环做手工测试时不需要写代码但一旦进入自动化测试就绕不开最基本的语法。while 循环是入门阶段使用频率很高的一个语句主要用在三类场景轮询等待被测服务还没启动完成脚本需要每隔几秒探测一次直到服务可用失败重试接口因为网络抖动或服务端临时异常导致请求失败需要循环重试批量处理循环读取多条测试数据批量执行断言。可能有人会问很多语言都有 for 循环为什么还需要 whilefor 循环适合“明确知道循环次数”的情况比如遍历一个长度为 5 的列表。但“不知道什么时候结束、只知道满足某个条件就继续”的情况更适合用 while。自动化测试里的重试和轮询恰恰是最典型的例子。3.2 while 基本语法与执行流程先来看 Python 中最简单的一个 while 循环count 1 while count 5: print(当前执行第, count, 次) count count 1 print(循环结束)这段代码的执行过程是检查count 5是否成立成立就进入循环体打印当前次数然后count加 1再次检查条件当count变成 6 时条件不再成立退出循环。也就是说while 循环由两部分组成循环条件和循环体。循环条件返回 True 时执行循环体返回 False 时结束循环。有一点需要特别注意循环体里要有一个能让条件最终变成 False 的语句。上面的例子中如果缺少count count 1count 永远等于 1循环将永远执行下去。C 语言、Java、JavaScript 中的 while 循环思路也完全一样int count 1; while (count 5) { printf(%d\n, count); count; }语法符号略有差别但判断逻辑是通用的。测试入门阶段如果精力有限建议先专心把 Python 的 while 用熟后面需要时再迁移到其他语言。3.3 break、continue 与循环中的 else实际应用中我们很少让 while 循环靠“条件自然不成立”来结束更多时候需要中途改变流程。break立即结束整个循环continue跳过本次循环体的剩余代码直接进入下一次条件判断。先看 break 的用法num 10 while True: num num - 1 if num 5: break print(退出时 num , num)这段代码是一个典型的“死循环风险”写法条件永远是 True但循环体内部通过 break 强制退出。输出结果是退出时 num 5再看 continuei 0 while i 6: i i 1 if i % 2 0: continue print(i)当 i 是偶数时continue 会让程序跳过 print直接进入下一次条件判断。最终输出的是奇数1 3 5Python 的 while 循环还支持一个比较少见的else子句times 0 while times 3: print(尝试次数:, times 1) times times 1 else: print(3 次机会已用完)这里要特别留意语义只有当 while 循环正常结束、没有被 break 中断时else 里的代码才会执行。如果循环是通过 break 跳出else 不会执行。这个特性很适合做登录次数限制之类的逻辑。3.4 避免死循环入门最常见的坑写 while 循环时新手最容易遇到的就是死循环程序一直运行既不报错也不退出严重时 CPU 占用率会飙得很高。典型错误一忘记更新循环变量。count 1 while count 5: print(当前执行第, count, 次) # 缺少 count count 1典型错误二循环条件写成了恒为 True但循环体里没有 break。while True: print(程序卡在这里了)典型错误三在循环体内部用 continue 时不小心把更新变量的代码放在了 continue 之后导致变量永远不更新。i 0 while i 5: continue i i 1避免死循环的核心方法很简单明确这个循环什么时候退出循环体里必须有让条件改变的语句如果写成while True心里要清楚 break 触发的位置如果担心意外死循环可以设置一个最大次数计数超过后主动 break。4. while 循环在测试中的典型应用场景4.1 场景一登录失败重试限制自动化测试中经常要模拟用户反复输错密码的场景。我们可以用 while 实现一个简易的“三次机会”登录逻辑MAX_ATTEMPTS 3 attempt 0 while attempt MAX_ATTEMPTS: username input(请输入用户名: ) password input(请输入密码: ) if username tester and password 123456: print(登录成功) break attempt attempt 1 print(账号或密码错误剩余次数:, MAX_ATTEMPTS - attempt) else: print(连续错误次数过多请稍后再试)运行过程可以这样理解第一次输入正确break 会立即结束循环else 不会执行三次都输错while 条件最终不成立正常结束后执行 else提示稍后再试。这个逻辑看起来简单却是很多登录模块的真实业务规则。手工测试时我们会手工测三次自动化测试时则可以用脚本批量验证。4.2 场景二等待被测服务启动自动化测试最头疼的问题之一是环境不稳定。被测服务没启动完就跑用例第一个报错往往是连接失败。所以在执行测试之前可以用 while 循环写一个“服务探测”脚本import time import requests def wait_for_service(url, timeout30, interval2): 在 timeout 秒内每隔 interval 秒探测一次服务直到服务可用。 start_time time.time() attempt 0 while time.time() - start_time timeout: attempt attempt 1 try: resp requests.get(url, timeout3) if resp.status_code 500: print(f服务已就绪第 {attempt} 次探测成功状态码: {resp.status_code}) return True else: print(f第 {attempt} 次探测返回 {resp.status_code}继续等待...) except requests.RequestException as exc: print(f第 {attempt} 次探测失败: {exc}) time.sleep(interval) print(等待超时服务未在指定时间内就绪) return False if __name__ __main__: wait_for_service(http://127.0.0.1:5000/api/products)这里的循环条件是“当前时间距离开始时间不超过 timeout”。每循环一次睡眠 interval 秒避免 CPU 空转。如果服务一直没有启动最多等待 timeout 秒后退出。4.3 场景三接口请求失败自动重试接口自动化中网络抖动是很常见的情况。一次请求失败就直接失败会让自动化用例变得很不稳定。因此更合理的做法是加一个重试机制。下面用 while 实现一个简单的请求重试函数import time import requests def request_with_retry(method, url, max_retries3, interval1, **kwargs): 使用 while 循环对 HTTP 请求做简单重试。 :param method: get/post :param url: 请求地址 :param max_retries: 最大重试次数 :param interval: 每次重试的间隔秒数 :param kwargs: 透传给 requests 的参数 attempt 0 while attempt max_retries: attempt attempt 1 try: resp requests.request(method, url, timeoutkwargs.pop(timeout, 5), **kwargs) # 2xx、3xx、4xx 都直接返回只有服务端异常或网络异常才重试 if resp.status_code 500: return resp print(f第 {attempt} 次请求返回 {resp.status_code}服务端异常准备重试) except requests.RequestException as exc: print(f第 {attempt} 次请求异常: {exc}) if attempt max_retries: raise if attempt max_retries: time.sleep(interval) raise RuntimeError(请求失败超过最大重试次数)这段代码有几个细节值得注意并不是所有 4xx 都要重试。400、401 通常是客户端问题重试多少次都一样网络异常和 5xx 更适合重试每次重试之间要 sleep否则会给服务器造成过大的瞬时压力超过最大次数后一定要给出异常不能无限循环下去。5. 项目实战登录与商品查询接口自动化测试5.1 项目需求纸上谈兵再多不如一个能跑起来的项目。这一节我们实现一个“登录 商品查询”的接口自动化测试小项目。被测对象是一个用 Flask 写的简易商城后端提供两个接口POST /api/login传入用户名和密码登录成功返回 tokenGET /api/products需要携带登录后获得的 token才能查询商品列表。项目中使用 while 循环的地方是封装登录和商品接口的请求重试工具。这样即使被测服务启动较慢也不会因为第一次连接失败就立刻挂掉。说明这个后端只是教学用的模拟服务token 生成方式做了极大简化不能用于真实业务。真实项目中必须有完整的认证方案、权限校验和链路追踪。5.2 项目结构创建项目目录learn_test结构如下learn_test/ ├── app.py # 被测系统简易 Flask 服务 ├── requirements.txt # 依赖文件 ├── conftest.py # pytest 全局 fixture ├── api/ │ ├── __init__.py │ ├── client.py # 接口请求封装 │ └── retry.py # 基于 while 的请求重试工具 └── testcases/ ├── __init__.py ├── test_login.py # 登录接口测试用例 └── test_products.py # 商品查询接口测试用例依赖文件内容flask2.0 requests2.25 pytest7.0安装依赖pip install -r requirements.txt如果安装速度慢可以指定国内镜像源这里不展开。实际使用中请根据你的 Python 版本选择兼容的依赖版本。5.3 被测服务实现文件路径learn_test/app.pyimport hashlib import time from flask import Flask, request, jsonify app Flask(__name__) # 模拟用户数据 USERS { admin: 123456, tester: 654321, } # 模拟商品数据 PRODUCTS [ {id: 1, name: 软件测试实战图书, price: 59.0}, {id: 2, name: 无线鼠标, price: 39.9}, {id: 3, name: 机械键盘, price: 199.0}, ] def create_token(username): 仅为教学演示生成 token生产环境禁止使用。 raw f{username}-{time.time()} return hashlib.md5(raw.encode(utf-8)).hexdigest() app.post(/api/login) def login(): data request.get_json(silentTrue) or {} username (data.get(username) or ).strip() password data.get(password) or if not username or not password: return jsonify({code: 40001, message: 用户名和密码不能为空}), 400 if USERS.get(username) ! password: return jsonify({code: 40002, message: 用户名或密码错误}), 401 token create_token(username) return jsonify({code: 0, message: ok, data: {token: token}}) app.get(/api/products) def products(): token request.headers.get(Authorization, ) if not token.startswith(Bearer ): return jsonify({code: 40003, message: 未登录或 token 失效}), 401 return jsonify({code: 0, message: ok, data: PRODUCTS}) if __name__ __main__: app.run(host127.0.0.1, port5000, debugTrue)代码中的接口都加了状态码语义这样测试用例可以同时校验 HTTP 状态码和业务 code 字段更接近真实接口测试习惯。5.4 接口请求封装与 while 重试工具文件路径learn_test/api/retry.pyimport time import requests def request_with_retry(method, url, max_retries3, interval1, **kwargs): 基于 while 循环的 HTTP 请求重试工具。 attempt 0 while attempt max_retries: attempt attempt 1 try: resp requests.request( method, url, timeoutkwargs.pop(timeout, 5), **kwargs ) if resp.status_code 500: return resp print(f第 {attempt} 次请求返回 {resp.status_code}准备重试) except requests.RequestException as exc: print(f第 {attempt} 次请求异常: {exc}) if attempt max_retries: raise if attempt max_retries: time.sleep(interval) raise RuntimeError(请求失败超过最大重试次数)文件路径learn_test/api/client.pyfrom api.retry import request_with_retry BASE_URL http://127.0.0.1:5000 class ApiClient: def __init__(self, base_urlBASE_URL): self.base_url base_url self.token def login(self, username, password): resp request_with_retry( post, f{self.base_url}/api/login, json{username: username, password: password}, timeout3, ) body resp.json() if body.get(code) 0: self.token body[data][token] return resp, body def get_products(self, with_tokenTrue): headers
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门