请简述 Web 自动化测试执行过程中,为了提高测试效率/成功率采取了哪些措施?
主要从以下几个方面提高 Web 自动化测试的效率和成功率:
优化等待机制: 使用显式等待,等待元素加载完成后再操作,减少因页面加载不及时导致的失败。
提高脚本稳定性: 使用 POM 页面对象模型,封装公共操作,减少重复代码,方便维护。
提高执行效率: 使用数据驱动测试、并行执行等方式,减少重复操作,缩短测试时间。
增强异常处理: 对元素定位失败、超时等异常进行捕获,并添加日志和截图,方便排查问题。
优化测试用例: 优先执行核心业务和高风险用例,及时发现重要问题。
持续集成: 将自动化测试集成到 Jenkins 等 CI 工具中,实现自动执行和测试结果反馈。
记忆口诀:等待、稳定、提速、异常、用例、集成。
如何保证 Web 自动化测试的稳定性(降低脚本易用性衰减与误报率)?
主要从以下几个方面保证 Web 自动化测试的稳定性:
优化元素定位: 优先使用稳定的 ID、CSS Selector 等定位方式,避免使用容易变化的绝对 XPath。
合理设置等待: 使用显式等待,等元素加载完成、可见或可点击后再操作,减少因页面加载时序导致的失败。
做好代码封装: 使用 POM 页面对象模型,将元素定位和业务操作分离,页面变化时只需修改对应模块。
减少环境干扰: 保证测试环境、测试数据和浏览器版本相对稳定,避免外部因素影响测试结果。
完善异常处理: 对超时、元素找不到等异常进行日志记录和截图,方便定位问题。
降低误报率: 脚本失败后先分析原因,区分真实业务缺陷、脚本问题和环境问题,不要简单地将所有失败都判定为 Bug。
记忆口诀:定位稳、等待准、封装好、环境稳、日志全、误报分。
现在有一个接口自动化测试框架,如果要切换或扩展到 Web UI 自动化,应该如何设计与改造?
如果要从接口自动化扩展到 Web UI 自动化,我会采用保留公共模块、扩展 UI 专属模块的方式进行改造。
复用公共模块:保留原有的配置管理、日志、测试数据、断言、报告等模块。
引入 UI 工具:使用 Selenium 或 Playwright 实现浏览器启动、元素定位、点击、输入等操作。
采用 POM 模式:将页面元素定位和操作封装在 Page 层,测试用例只负责业务流程和结果校验,提高代码复用性。
封装公共方法:增加显式等待、异常处理、失败截图、浏览器管理等功能,提高脚本稳定性。
统一测试管理:继续使用 Pytest 管理用例,通过参数化、标记和并行执行控制测试范围,并整合测试报告。
接入持续集成:通过 Jenkins 定时或按需执行 UI 自动化,失败时保存截图和日志,方便定位问题。
总结来说,就是复用原有的公共能力,新增 UI 自动化执行层和页面对象层,统一测试执行、报告和持续集成流程。
请谈谈对 Python 异常处理机制(try-except-finally)的理解与应用。
Python 的异常处理主要通过try-except-finally实现,目的是捕获和处理程序运行过程中出现的异常,避免程序意外中断。
try:放置可能发生异常的代码。
except:捕获并处理异常,可以针对不同类型的异常进行处理。
finally:无论是否发生异常,通常都会执行,常用于释放资源,比如关闭文件、数据库连接等。
else:当
try中没有发生异常时执行,可以用于处理正常执行的逻辑。
在软件测试中,我通常会使用异常处理来捕获接口请求超时、数据解析失败等异常,同时记录错误日志,方便定位问题。需要注意的是,不能直接捕获所有异常后忽略,否则可能掩盖真正的缺陷。
try: result = 10 / 0 except ZeroDivisionError as e: print(f"发生异常:{e}") else: print("程序正常执行") finally: print("执行清理操作")这里会捕获除零异常,执行except,最后执行finally。
记忆口诀:try尝试执行,except捕获异常,else处理正常结果,finally执行清理。
Web UI 自动化测试过程中,如果有一个用例运行失败,你会如何排查和定位问题?
如果 Web UI 自动化测试用例运行失败,我会按照以下步骤进行排查:
查看日志和报告:先查看 Pytest 测试报告和错误日志,确认失败的用例、报错信息以及具体失败位置。
检查页面状态:查看失败时的页面截图,确认页面是否正常加载,是否出现弹窗、跳转异常或页面元素未显示等情况。
检查元素定位:确认元素定位方式是否正确,页面结构是否发生变化,元素是否被遮挡或不可交互。
检查等待机制:判断是否存在页面加载慢、异步请求未完成等问题,必要时使用显式等待,避免单纯依赖固定休眠时间。
检查测试数据和环境:确认测试账号、数据状态、网络连接及浏览器版本等是否正常。
复现并修复问题:手动复现失败场景,判断是业务功能缺陷、自动化脚本问题还是环境问题,针对原因修复后重新执行回归测试。
总结:我会先通过日志、报错堆栈和失败截图缩小范围,再从元素定位、等待机制、测试数据和环境等方面逐一排查,最后确认根因并验证修复结果。
三种等待方式(强制等待、隐式等待、显式等待)的区别是什么?分别在什么场景下使用?
Selenium 中常见的等待方式有三种:强制等待、隐式等待和显式等待。
1. 强制等待(time.sleep)
固定等待指定时间,不管页面是否已经加载完成,时间到了才继续执行。
场景:临时调试,或者确实需要固定等待的特殊场景。
缺点:容易浪费时间,等待时间不足时仍然会失败。
2. 隐式等待(implicitly_wait)
设置一个全局等待时间,在查找元素时,如果元素没有立即出现,就会在规定时间内持续尝试查找。
场景:简单项目中,作为元素查找的基础等待机制。
缺点:只能针对元素查找,不能直接判断元素是否可点击、是否可见等状态。
3. 显式等待(WebDriverWait)
针对特定元素或条件进行等待,满足条件后立即继续执行,超时则抛出异常。
场景:等待元素出现、元素可点击、页面跳转完成等。
优点:等待条件灵活,效率和稳定性通常更好,是实际 UI 自动化中优先推荐的方式。
对比项 | 强制等待 | 隐式等待 | 显式等待 |
|---|---|---|---|
等待方式 | 固定时间 | 查找元素时轮询 | 根据指定条件轮询 |
等待范围 | 当前代码位置 | 全局元素查找 | 指定操作或条件 |
灵活性 | 低 | 一般 | 高 |
推荐程度 | 尽量少用 | 按需使用 | 优先使用 |
import time from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 1. 强制等待 time.sleep(3) # 2. 隐式等待:全局设置 driver.implicitly_wait(10) # 3. 显式等待:等待按钮可点击 button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, "submit")) ) button.click()面试总结: 强制等待是固定等,隐式等待是查找元素时等,显式等待是满足指定条件才继续。在实际项目中,我会优先使用显式等待,减少不必要的等待时间,提高脚本的稳定性。
在 Web 自动化中如何操作下拉选择框(Select)?
在 Web 自动化中,如果下拉框是 HTML 原生的select标签,可以使用 Selenium 提供的Select类进行操作。
常见的选择方式有三种:
根据文本选择:使用
select_by_visible_text()。根据 value 属性选择:使用
select_by_value()。根据索引选择:使用
select_by_index(),索引从 0 开始。
如果是自定义下拉框,例如使用 Vue 或 React 实现的下拉组件,通常不能直接使用Select类,需要先点击下拉框,再定位并点击对应的选项。
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select element = driver.find_element(By.ID, "city") select = Select(element) # 根据文本选择 select.select_by_visible_text("北京") # 根据 value 属性选择 select.select_by_value("shanghai") # 根据索引选择,索引从 0 开始 select.select_by_index(2)面试总结: 原生下拉框使用Select类,通过文本、value 或索引选择;自定义下拉框则通过普通元素定位和点击来操作。操作前需要确认下拉框类型,避免使用错误的方法。
如何处理页面中的弹出框(Alert/Confirm/Prompt 及自定义弹窗)?
在 Web UI 自动化中,我会根据弹出框的类型选择不同的处理方式,主要分为浏览器原生弹窗和页面自定义弹窗。
1. Alert 提示框
使用switch_to.alert切换到弹窗,然后调用accept()确认关闭。
2. Confirm 确认框
调用accept()确认,调用dismiss()取消。
3. Prompt 输入框
使用send_keys()输入内容,再调用accept()确认,也可以使用dismiss()取消。
4. 自定义弹窗
自定义弹窗属于网页 DOM 的一部分,通常使用 Selenium 的元素定位方式找到弹窗中的按钮或输入框,再进行点击、输入等操作。必要时使用显式等待,确保弹窗已经出现。
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 1. Alert 弹窗 alert = driver.switch_to.alert print(alert.text) alert.accept() # 2. Confirm 确认框:取消操作 driver.switch_to.alert.dismiss() # 3. Prompt 输入框 alert = driver.switch_to.alert alert.send_keys("测试内容") alert.accept() # 4. 自定义弹窗:等待并点击关闭按钮 close_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, ".modal-close")) ) close_btn.click()注意:示例中的弹窗需要在对应操作前已经出现,实际项目中应先通过显式等待确认弹窗出现;自定义弹窗的 CSS 选择器也需要根据实际页面修改。
面试总结: 浏览器原生弹窗使用switch_to.alert,通过accept()、dismiss()和send_keys()操作;自定义弹窗则通过 DOM 元素定位和显式等待进行处理。
如何通过自动化脚本控制页面的滚动条?
在 Web UI 自动化中,我主要使用 Selenium 结合 JavaScript 或键盘操作来控制页面滚动,常见有以下三种方式:
JavaScript 滚动:通过
execute_script()控制页面滚动到指定位置,适合滚动到顶部、底部或指定坐标。滚动到指定元素:使用 JavaScript 的
scrollIntoView(),让目标元素滚动到可见区域,方便后续操作。键盘滚动:使用 Selenium 的
send_keys()模拟按上下方向键、Page Down 等按键,适合模拟用户操作。
from selenium.webdriver.common.by import By # 1. 滚动到页面底部 driver.execute_script( "window.scrollTo(0, document.body.scrollHeight)" ) # 2. 滚动到页面顶部 driver.execute_script("window.scrollTo(0, 0)") # 3. 滚动到指定元素 element = driver.find_element(By.ID, "submit") driver.execute_script( "arguments[0].scrollIntoView({block: 'center'})", element )面试总结: 我通常优先使用 JavaScript 控制滚动,尤其是scrollIntoView(),可以将目标元素滚动到可见区域,方便后续定位和操作。如果需要模拟真实用户的滚动行为,也可以使用键盘或鼠标滚轮操作。
如何处理网页中的嵌套框架(iframe/frame)?
在 Web UI 自动化中,iframe或frame中的元素不能直接从主页面定位,需要先切换到对应的框架,再进行元素操作。
Selenium 中常用三种切换方式:
根据索引切换:
switch_to.frame(0)。根据元素切换:先定位 iframe 元素,再传入
switch_to.frame()。根据 name 或 id 切换:
switch_to.frame("frame_name")。
如果是嵌套 iframe,需要逐层切换进去;操作完成后,可以使用parent_frame()返回上一层,或者使用default_content()返回最外层页面。
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待并切换到 iframe WebDriverWait(driver, 10).until( EC.frame_to_be_available_and_switch_to_it( (By.ID, "my_frame") ) ) # 操作 iframe 内的元素 driver.find_element(By.ID, "username").send_keys("test") # 返回主页面 driver.switch_to.default_content()面试总结: 处理 iframe 的关键是先切换框架,再定位元素。嵌套框架需要逐层切换,操作完成后及时返回主页面,避免因框架上下文不正确导致元素定位失败。
如何处理浏览器多选项卡/标签页(Tabs)之间的切换与跳转?
在 Web UI 自动化中,Selenium 通过window_handles获取所有窗口句柄,通过switch_to.window()切换到指定窗口或标签页。
常见操作有三种:
获取窗口句柄:使用
driver.window_handles获取所有窗口的句柄。切换窗口:使用
driver.switch_to.window(handle)切换到指定窗口。关闭窗口:使用
driver.close()关闭当前窗口;使用driver.quit()关闭整个浏览器会话。
实际项目中,如果点击链接后打开新标签页,我会先获取原窗口句柄,再等待新窗口出现,最后切换过去进行操作。不要直接假设新窗口一定是列表中的第二个。
from selenium.webdriver.support.ui import WebDriverWait # 保存原窗口句柄 original_window = driver.current_window_handle # 点击链接,打开新标签页 driver.find_element("id", "open_tab").click() # 等待新窗口出现 WebDriverWait(driver, 10).until( lambda d: len(d.window_handles) > 1 ) # 找到并切换到新标签页 for handle in driver.window_handles: if handle != original_window: driver.switch_to.window(handle) break # 在新标签页中操作 print(driver.title) # 关闭当前标签页,返回原标签页 driver.close() driver.switch_to.window(original_window)面试总结: 先保存原窗口句柄,再等待新窗口出现,通过句柄切换标签页;操作完成后,根据需要关闭当前窗口并切回原窗口。关键是正确管理窗口句柄,避免切换错误或操作已关闭的窗口。
你们项目中 UI 自动化和接口自动化的覆盖率分别是多少?
我们项目中,接口自动化的覆盖率相对较高,主要覆盖核心业务接口和高频回归场景;UI 自动化的覆盖率相对低一些,主要覆盖核心业务流程和关键功能。
接口自动化主要使用 Python、Requests 和 Pytest,重点验证接口的请求参数、响应结果、业务逻辑和异常场景。
UI 自动化主要使用 Selenium,重点覆盖登录、核心页面操作和关键业务流程。
整体上,我们采用接口自动化为主、UI 自动化为辅的策略,因为接口自动化执行速度快、维护成本相对较低,而 UI 自动化更适合验证用户实际操作流程。
需要注意的是,具体覆盖率应该以项目实际统计数据为准。如果面试官追问百分比,说明统计口径,例如核心接口覆盖率、核心业务流程覆盖率,而不是随意给出一个数字。
自动化测试脚本是如何进行调试(Debug)的?
在 Web UI 自动化测试中,我主要通过以下几种方式调试脚本:
查看报错信息:通过 Pytest 的报错信息和异常堆栈,定位具体失败的代码行,判断是元素定位、等待超时还是其他问题。
增加日志输出:使用
logging记录关键操作、页面状态和执行结果,方便追踪脚本的执行过程。使用断点调试:通过 PyCharm 或 VS Code 设置断点,逐步执行代码,检查变量值和程序运行状态。
检查页面元素:使用浏览器开发者工具检查 HTML 结构和元素属性,确认定位表达式是否正确。
截图和复现:在失败时保存页面截图,必要时结合浏览器控制台日志进行排查,并单独运行失败用例复现问题。
修复并回归验证:找到原因后修改脚本,再重新执行相关用例,确认问题已解决且没有引入新的问题。
import logging from selenium.webdriver.common.by import By logging.basicConfig(level=logging.INFO) logging.info("开始执行登录测试") try: button = driver.find_element(By.ID, "login") logging.info("已找到登录按钮") button.click() except Exception: logging.exception("登录操作失败") driver.save_screenshot("error.png") raise面试总结: 我通常先看报错堆栈,再结合日志、断点、浏览器开发者工具和失败截图定位问题,修复后重新执行用例验证。调试的关键是先找到根因,而不是简单地增加等待时间或反复重跑脚本。
pytest 框架中常用的插件和装饰器有哪些?
一、常用插件
pytest-html:生成 HTML 格式的测试报告,方便查看用例执行结果。
pytest-xdist:支持多进程并行执行测试用例,提高执行效率。
pytest-rerunfailures:支持失败用例自动重跑,辅助排查偶发性失败,但不能代替根因分析。
allure-pytest:生成 Allure 测试报告,展示用例步骤、执行结果和失败信息。
pytest-cov:统计 Python 代码覆盖率,主要用于代码覆盖率分析,不等同于测试用例覆盖率。
二、常用装饰器和标记
@pytest.mark.parametrize:参数化测试,一组测试逻辑可以使用多组数据执行。@pytest.mark.skip:无条件跳过某个测试用例。@pytest.mark.skipif:满足指定条件时跳过用例。@pytest.mark.xfail:标记预期会失败的用例。@pytest.mark.smoke:自定义标记,用于标识冒烟测试用例,需要提前注册标记。@pytest.fixture:定义测试前置、后置操作或测试资源,例如初始化浏览器、准备测试数据和清理环境。
import pytest @pytest.mark.parametrize("username, password", [ ("admin", "123456"), ("test", "654321") ]) def test_login(username, password): assert username != "" assert password != "" @pytest.fixture def setup_data(): print("准备测试数据") yield print("清理测试数据") @pytest.mark.skipif( True, reason="暂时跳过" ) def test_demo(): assert True插件主要用于扩展 Pytest 的能力,例如报告生成、并行执行和失败重跑;装饰器和标记主要用于参数化、用例跳过、预期失败及前后置管理。其中我会重点掌握parametrize、fixture、skip、skipif和xdist。
你在自动化测试中是如何做断言(Assert)的?
在自动化测试中,我主要使用 Pytest 自带的assert语句进行断言,判断实际结果是否符合预期结果。
常见的断言方式有以下几种:
状态断言:判断接口状态码是否为 200,或者页面元素是否显示。
数据断言:判断接口返回的字段值、数据类型是否符合预期。
业务断言:判断业务操作是否成功,例如登录后是否跳转到首页。
数据库断言:对于涉及数据库的场景,可以查询数据库,验证数据是否正确保存或更新。
实际项目中,我会根据测试场景选择合适的断言,同时在断言失败时结合日志和报错信息定位问题。
# 接口断言 assert response.status_code == 200 assert response.json()["code"] == 0 assert response.json()["data"]["name"] == "test" # Web UI 断言 assert driver.title == "首页" assert driver.find_element( "id", "welcome" ).is_displayed()面试总结: 我主要使用 Pytest 的assert,从状态、数据、业务流程和数据库等维度验证结果。断言不仅要判断请求是否成功,还要验证实际业务结果是否符合预期,避免出现状态码正常但业务逻辑错误的情况。
Python 中的 re 正则表达式模块常用方法有哪些?它们之间有什么区别?
Python 的re模块主要用于字符串匹配、查找、替换和分割,在接口自动化测试中,可以用来提取响应数据、匹配验证码格式或校验字符串。
常用方法主要有以下六种:
re.match():从字符串开头开始匹配,开头不符合就匹配失败。re.search():扫描整个字符串,找到第一个符合条件的位置就返回。re.findall():查找所有符合条件的内容,以列表形式返回。re.finditer():查找所有匹配结果,返回迭代器,适合逐个处理匹配对象。re.sub():替换符合正则表达式的内容。re.split():根据正则表达式匹配到的位置分割字符串。
import re text = "订单号:123,订单号:456" print(re.match(r"\d+", text)) # None print(re.search(r"\d+", text).group()) # 123 print(re.findall(r"\d+", text)) # ['123', '456'] print(re.sub(r"\d+", "*", text)) # 订单号:*,订单号:* print(re.split(r"\d+", text)) # ['订单号:', ',订单号:', '']方法 | 主要作用 | 返回结果 |
|---|---|---|
| 从开头匹配 | 匹配对象或 |
| 查找第一个匹配 | 匹配对象或 |
| 查找全部匹配 | 列表 |
| 逐个遍历匹配结果 | 迭代器 |
| 替换匹配内容 | 字符串 |
| 按匹配位置分割 | 列表 |
面试总结:match从头匹配,search查找第一个,findall查找全部,sub用于替换,split用于分割,finditer则适合逐个遍历匹配结果。
pytest 框架是如何实现数据驱动测试(DDT)的?
Pytest 主要通过@pytest.mark.parametrize装饰器实现数据驱动测试(DDT)。
它可以将测试数据和测试逻辑分离,同一个测试函数使用多组不同的数据执行,减少重复代码,提高测试效率。
实际项目中,我通常会把测试数据放在列表、字典、YAML 或 JSON 文件中,再通过参数化机制传入测试用例。例如,接口自动化测试中,可以使用不同的用户名、密码和预期结果,验证登录接口的正常和异常场景。
import pytest @pytest.mark.parametrize("username, password, expected", [ ("admin", "123456", 200), ("test", "wrong", 401), ("", "123456", 400) ]) def test_login(username, password, expected): response = send_login_request(username, password) assert response.status_code == expected这里的send_login_request()是示意函数,需要在实际项目中实现。
面试总结: Pytest 主要通过@pytest.mark.parametrize实现数据驱动测试,也可以结合 YAML、JSON 等外部数据文件管理测试数据。核心思想是一套测试逻辑,多组测试数据,分别执行并验证结果。
如果一个接口的响应出参有十几万条数据,应该如何进行校验和测试?
如果一个接口的响应数据有十几万条,我不会直接逐条人工检查,而是根据数据特点采用自动化校验,主要从以下几个方面入手:
校验整体结构:检查 HTTP 状态码、响应格式、字段类型和必填字段是否符合预期。
校验数据完整性:检查数据总量、是否存在重复数据、关键字段是否为空,以及是否存在缺失数据。
校验数据正确性:通过抽样、边界值检查,或者与数据库、基准数据进行比对,验证数据内容是否正确。
分批或分页校验:如果接口支持分页,可以逐页获取数据并进行校验,避免一次性加载大量数据导致内存占用过高。
检查性能表现:记录接口响应时间、数据传输量和内存使用情况,判断是否满足性能要求。
对于大量数据,我会使用 Python 编写自动化校验脚本,通过集合、计数、哈希值等方式提高校验效率。如果需要完整验证,就进行全量比对;如果成本较高,则结合风险进行抽样,但不会用抽样代替必须的完整性校验。
data = response.json()["data"] ids = set() for item in data: assert item.get("id") is not None assert item.get("name") is not None assert item["id"] not in ids, "发现重复 ID" ids.add(item["id"]) print(f"校验通过,共 {len(data)} 条数据")这段代码适用于响应数据已经加载到内存的情况。如果数据量更大,可以考虑流式解析或分页处理。
总结: 大数据量接口测试的重点是数据结构、完整性、正确性和性能。根据业务要求选择全量校验、分页校验或抽样校验,并尽量使用自动化脚本代替人工检查。
你觉得如何设计一个优秀的自动化测试框架?需要考虑哪些核心要素?
我认为,一个优秀的自动化测试框架,核心是易维护、可复用、执行稳定、方便扩展。设计时我主要考虑以下六个方面:
分层设计:将框架划分为配置层、数据层、公共方法层、业务封装层、测试用例层和报告层,降低模块之间的耦合。
数据驱动:使用 YAML、JSON 等文件管理测试数据,结合 Pytest 参数化,实现测试数据与测试逻辑分离。
公共方法封装:封装请求发送、元素定位、等待、断言、日志记录等公共功能,减少重复代码。
稳定性保障:合理使用显式等待、异常处理、测试数据隔离和资源清理,减少脚本失败和误报。
测试报告与日志:集成 Allure 等报告工具,记录执行结果、异常堆栈和失败截图,方便定位问题。
持续集成与扩展性:接入 Jenkins,实现自动执行、失败通知和回归测试,并支持并行执行,以及后续扩展接口、Web UI 等自动化测试类型。
此外,还需要考虑代码规范、版本管理、执行效率和框架的维护成本,避免为了追求复杂架构而过度设计。
总结: 我设计自动化测试框架时,会优先考虑分层解耦、公共能力复用、数据驱动、执行稳定、结果可追溯和持续集成,让框架不仅能够运行测试,还能长期维护并适应业务变化。
你在实际项目测试过程中,哪些地方或业务模块经常会出现 Bug?
在实际项目中,我认为 Bug 比较容易集中在以下几个方面:
复杂业务逻辑:例如角色创建、消息发送、内容生成等,涉及多个业务规则和状态流转,容易出现逻辑错误。
接口交互和数据一致性:多个接口之间存在依赖关系时,容易出现参数传递错误、数据不同步、重复提交等问题。
边界值和异常场景:例如输入为空、超长字符、网络超时、重复点击等,开发容易遗漏处理。
多端兼容问题:不同手机型号、操作系统和屏幕尺寸,可能导致页面显示异常或功能表现不一致。
并发和性能问题:例如多人同时操作、消息高频发送时,可能出现响应缓慢、数据重复或服务异常。
新功能影响旧功能:版本迭代后,修改某个模块可能影响其他模块,因此需要重点进行回归测试。
以我测试过的 AI 社交类 App 为例,消息发送、角色创建和 AI 内容生成这类涉及多模块交互的功能,我会重点关注正常流程、异常处理、数据一致性以及弱网场景。
面试总结: 我会重点关注业务逻辑复杂、接口依赖多、边界场景多以及跨模块交互频繁的地方,因为这些场景更容易暴露问题。同时结合历史缺陷分布和需求变更情况,动态调整测试重点。
针对业务需求“会员卡送附属卡,消费享受两倍积分,每 100 积分兑换指定奖励”,你会如何设计测试用例?
针对这个需求,我会从功能测试、边界值测试、异常测试、数据一致性测试和业务流程测试几个方面设计用例。
首先,我会确认几个业务规则:一张会员卡可以赠送几张附属卡、主卡和附属卡是否都享受双倍积分、消费金额与积分如何换算、积分不足 100 时是否可以兑换,以及兑换后积分如何扣减。
假设主卡和附属卡都享受双倍积分,每消费 1 元获得 1 积分,每满 100 积分可以兑换指定奖励,我会设计以下测试用例。
一、会员卡及附属卡功能
购买或办理会员卡,验证是否成功赠送附属卡。
验证主卡与附属卡的绑定关系是否正确。
验证附属卡是否可以正常使用。
验证重复领取、重复绑定或超出附属卡数量限制时,系统是否正确处理。
二、双倍积分功能
主卡消费 100 元,验证是否获得 200 积分。
附属卡消费 100 元,验证是否获得 200 积分。
消费 0 元,验证是否不产生积分。
消费金额为小数,例如 10.5 元,验证积分计算和取整规则是否正确。
退款后,验证积分是否按照业务规则扣回。
重复提交消费请求,验证是否会重复累计积分。
三、积分兑换功能
积分达到 100 分时,验证是否可以兑换指定奖励。
积分不足 100 分时,验证是否禁止兑换。
积分为 250 分时,验证是否可以兑换两次,剩余 50 分。
兑换成功后,验证积分是否正确扣减,奖励是否正确发放。
奖励库存不足、奖励已下架时,验证兑换是否失败并给出提示。
连续点击兑换或并发兑换时,验证是否出现重复扣分或重复发奖。
四、数据及异常测试
验证主卡和附属卡的消费积分是否按照业务规则累计到正确的账户。
验证积分流水、消费记录和账户余额是否一致。
模拟网络超时、重复请求等情况,验证积分和奖励数据是否正确。
验证积分临界值,例如 99、100、101 分时的兑换结果。
验证兑换过程中出现异常时,是否能够保证积分扣减与奖励发放的数据一致性。
面试总结: 我会重点关注双倍积分计算、100 积分兑换门槛、主附属卡的积分归属,以及重复请求和并发兑换等场景。同时结合等价类、边界值分析和异常场景设计测试用例,确保积分计算准确、奖励发放正确、数据一致。
Python 中的反射函数(getattr, setattr, hasattr 等)怎么用?应用场景是什么?
Python 的反射是指在程序运行过程中,动态获取、判断、调用或修改对象的属性和方法。
常用的反射函数主要有四个:
getattr():获取对象的属性或方法,也可以设置默认值。setattr():动态设置或修改对象的属性。hasattr():判断对象是否存在指定属性。delattr():删除对象的指定属性。
class Login: def __init__(self): self.username = "admin" def login(self): return "登录成功" obj = Login() # 获取属性 print(getattr(obj, "username")) # admin # 判断属性是否存在 print(hasattr(obj, "login")) # True # 动态调用方法 method = getattr(obj, "login") print(method()) # 登录成功 # 动态设置属性 setattr(obj, "password", "123456") print(obj.password) # 123456 # 删除属性 delattr(obj, "password")在自动化测试中的应用场景
关键字驱动测试:根据测试数据中的关键字,动态调用对应的测试方法,例如点击、输入、登录等操作。
动态调用接口:根据配置文件中的方法名称,选择对应的请求处理方法。
框架扩展:动态检查对象是否具有某个方法,方便进行插件管理或公共方法调用。
例如,在关键字驱动框架中,可以使用getattr()根据配置动态调用对应的方法,避免编写大量的if-else判断。
面试总结: 反射的核心是动态操作对象的属性和方法。在自动化测试框架中,常用于关键字驱动、动态方法调用和框架扩展。使用时要注意校验方法名称,避免调用不存在或不允许执行的方法。
Unittest 和 pytest 测试框架的区别是什么?
Unittest 和 pytest 都是 Python 的测试框架,主要区别在于用例编写方式、断言方式、前置后置处理和扩展能力。
用例编写: Unittest 通常需要继承
TestCase类,pytest 不需要继承,直接编写以test_开头的函数即可。断言方式: Unittest 使用
assertEqual()、assertTrue()等断言方法;pytest 直接使用 Python 的assert语句。前置后置: Unittest 使用
setUp()和tearDown();pytest 使用fixture,更加灵活。参数化: Unittest 可以通过循环等方式实现参数化;pytest 使用
@pytest.mark.parametrize,更加方便。扩展能力: pytest 插件丰富,支持测试报告、并行执行、失败重跑等功能,扩展性比较强。
总结: Unittest 是 Python 标准库自带的测试框架;pytest 使用更加简洁,功能和插件生态更丰富,因此在自动化测试项目中比较常用。
简单代码对比
Unittest: import unittest class TestLogin(unittest.TestCase): def test_login(self): self.assertEqual(1 + 1, 2) pytest: def test_login(): assert 1 + 1 == 2面试加分点: 我实际开展自动化测试时,更倾向于使用 pytest,因为它编写用例比较简洁,并且可以结合fixture、参数化和 Allure 报告等功能,提高自动化测试效率。
App 端和 Web 端的上线发布流程有什么区别?
App 端和 Web 端上线发布的主要区别在于:Web 端通常部署服务器后即可更新,而 App 端通常还需要经过应用商店审核和用户下载安装或更新。
1. Web 端发布流程:
开发完成后,测试人员进行功能、兼容性、接口和回归测试。
测试通过后,提交预发布环境验收。
验收通过后,开发或运维将代码部署到生产服务器。
发布后进行线上验证,检查核心功能和接口是否正常。
如果出现严重问题,可以回滚代码或恢复之前的版本。
2. App 端发布流程:
开发完成后,测试人员进行功能、兼容性、安装卸载、升级、弱网、性能等测试。
测试通过后,进行预发布验收,并打包生成 Android APK/AAB 或 iOS 安装包。
提交应用商店审核,审核通过后发布;企业内部应用也可能通过内部渠道分发。
发布后关注崩溃率、接口异常和用户反馈,必要时通过热修复、回滚服务端或发布新版本处理问题。
核心区别
对比项 | Web 端 | App 端 |
|---|---|---|
发布方式 | 部署服务器 | 打包后通过商店或其他渠道分发 |
更新方式 | 用户刷新页面通常即可获取新版本 | 通常需要下载或安装更新 |
重点测试 | 浏览器兼容性、页面和接口 | 设备兼容性、安装升级、系统权限、弱网、性能 |
发布限制 | 主要受部署流程和服务器环境影响 | 还可能受到应用商店审核、系统版本和渠道限制 |
回滚方式 | 通常可回滚代码或部署版本 | 服务端可以回滚,但客户端已安装版本不一定能立即回退 |
总结: Web 端上线更侧重服务器部署和线上验证;App 端除了功能测试,还要重点关注安装包、设备兼容性、应用商店审核和版本升级。两者上线后都需要进行线上监控和问题跟踪。