news 2026/4/16 12:53:47

【自动化测试】pytest 语法与核心概念

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【自动化测试】pytest 语法与核心概念

pytest 语法与核心概念

    • Part 1: pytest 语法与核心概念
      • 1.1 基础语法
      • 1.2 配置文件 (pytest.ini, pyproject.toml, setup.cfg)
    • Part 2: pytest 装饰器详解与样例
      • 2.1 `@pytest.fixture` - 核心依赖注入与资源管理
      • 2.2 `@pytest.mark` - 标记与控制
      • 2.3 `@pytest.mark.parametrize` - 数据驱动测试
    • Part 3: Playwright 页面元素选择器详解与样例
      • 3.1 Locator API (推荐方式)
      • 3.2 Locator 对象的链式操作与过滤
      • 3.3 智能等待 (Implicit Waits)
    • 总结

Part 1: pytest 语法与核心概念

1.1 基础语法

  • 测试函数命名:Pytest 会自动发现并运行以test_开头的函数或_test结尾的函数。
    deftest_addition():assert1+1==2deftest_string_contains():assert"hello"in"hello world"
  • 断言:直接使用 Python 的assert语句。Pytest 会捕获失败的断言并提供清晰的错误信息。
    deftest_list_length():my_list=[1,2,3]assertlen(my_list)==3assertlen(my_list)>5# This will fail with a clear message

1.2 配置文件 (pytest.ini, pyproject.toml, setup.cfg)

可以用来配置 pytest 的默认行为、添加插件、定义标记等。

pytest.ini 示例

[tool:pytest] # 标记注册,方便 IDE 识别 markers = smoke: marks tests as part of the smoke suite ui: marks tests related to UI interactions slow: marks tests as slow # 默认命令行参数 addopts = -v -x --tb=short # 指定测试路径 testpaths = tests # 忽略某些路径 norecursedirs = .git dist build *.egg

Part 2: pytest 装饰器详解与样例

2.1@pytest.fixture- 核心依赖注入与资源管理

作用:提供测试所需的数据或对象,并管理它们的创建(setup)和销毁(teardown)。

基本用法

importpytest@pytest.fixturedefsample_data():"""提供一个简单的数据示例."""data={"key":"value","number":42}returndatadeftest_use_sample_data(sample_data):"""使用 fixture 提供的数据."""assertsample_data["key"]=="value"assertsample_data["number"]==42

yield与 teardown

importtempfileimportos@pytest.fixturedeftemp_file_path():"""创建临时文件,并在测试后删除."""temp_dir=tempfile.mkdtemp()temp_file=os.path.join(temp_dir,"temp.txt")withopen(temp_file,'w')asf:f.write("Test content")yieldtemp_file# 返回文件路径给测试函数# Teardown: 测试函数执行完毕后,清理临时文件os.remove(temp_file)os.rmdir(temp_dir)deftest_read_temp_file(temp_file_path):withopen(temp_file_path,'r')asf:content=f.read()assertcontent=="Test content"

scope参数

@pytest.fixture(scope="session")# 整个测试会话期间只执行一次defexpensive_resource():print("\n--- Creating expensive resource (e.g., database connection) ---")resource=create_expensive_resource()yieldresourceprint("\n--- Cleaning up expensive resource ---")destroy_resource(resource)@pytest.fixture(scope="function")# 每个测试函数执行一次 (默认)defper_test_setup():print("\n--- Per-test setup ---")yieldprint("\n--- Per-test teardown ---")@pytest.fixture(scope="class")# 每个测试类执行一次defper_class_data():print("\n--- Per-class setup ---")data=load_class_data()yielddataprint("\n--- Per-class teardown ---")# Test functions using fixturesdeftest_a(expensive_resource,per_test_setup):assertTruedeftest_b(expensive_resource,per_test_setup):assertTrue

autouse=True

@pytest.fixture(scope="function",autouse=True)# 自动应用于所有函数级测试defauto_log():print("\n[LOG] Starting a test function")yieldprint("[LOG] Finished a test function")deftest_one():# 不需要显式声明 auto_logassertTruedeftest_two():# 不需要显式声明 auto_logassert1==1

params参数 (与parametrize类似)

@pytest.fixture(params=[1,2,3],ids=["one","two","three"])# ids 提供更清晰的测试名称defnumber_fixture(request):returnrequest.paramdeftest_number_properties(number_fixture):assertnumber_fixture>0# 这个测试会被执行 3 次,分别传入 1, 2, 3

2.2@pytest.mark- 标记与控制

@pytest.mark.skip@pytest.mark.skipif

importsys@pytest.mark.skip(reason="Feature not implemented yet")deftest_incomplete_feature():pass@pytest.mark.skipif(sys.platform=="win32",reason="Does not run on Windows")deftest_unix_specific():# This test will be skipped on Windowspass

@pytest.mark.xfail- 预期失败

@pytest.mark.xfail(reason="Known bug #12345")deftest_known_buggy_functionality():assertbuggy_function()=="expected_result"# 如果返回 "expected_result",则标记为 XPASS# 如果返回其他值或抛出异常,则标记为 XFAIL@pytest.mark.xfail(strict=True,reason="Must fix this bug")# strict=True 意味着如果 XPASS,则测试失败deftest_critical_buggy_functionality():assertcritical_buggy_function()=="fixed_result"

自定义标记

# 在 pytest.ini 中注册标记 (如上所示)@pytest.mark.smokedeftest_smoke_login():assertlogin("user","pass")==True@pytest.mark.ui@pytest.mark.slowdeftest_ui_heavy_scenario():# Complex UI testpass# 运行时过滤:# pytest -m "smoke" # 只运行 smoke 标记的测试# pytest -m "ui and not slow" # 运行 ui 但不运行 slow 标记的测试# pytest -m "not slow" # 运行所有非 slow 标记的测试

2.3@pytest.mark.parametrize- 数据驱动测试

基本用法

@pytest.mark.parametrize("input_val, expected_output",[(2,4),(3,9),(4,16),(-1,1),])deftest_square(input_val,expected_output):assertsquare(input_val)==expected_output# 此测试会运行 4 次defsquare(x):returnx*x

多参数与ids

@pytest.mark.parametrize("username, password, expected_success",[("admin","secret",True),("user","wrongpass",False),("","any",False),],ids=["valid_admin","invalid_pass","empty_user"])deftest_login(username,password,expected_success):result=attempt_login(username,password)assertresult==expected_success# 输出中会显示 test_login[valid_admin], test_login[invalid_pass], etc.defattempt_login(username,password):# Simulate login logicreturnusername=="admin"andpassword=="secret"

Part 3: Playwright 页面元素选择器详解与样例

3.1 Locator API (推荐方式)

这些方法基于用户可见性语义化,更稳定。

方法说明样例
page.get_by_role(role, name=...)按 ARIA 角色定位,如button,link,textbox,checkbox,radio.page.get_by_role("button", name="Submit")
page.get_by_text(text)按元素内部可见文本定位。page.get_by_text("Sign In").click()
page.get_by_label(text)按关联的<label>标签文本定位输入框等。page.get_by_label("Email:").fill("user@example.com")
page.get_by_placeholder(text)placeholder属性定位。page.get_by_placeholder("Search...").fill("query")
page.get_by_alt_text(text)按图片的alt属性定位。page.get_by_alt_text("Logo").click()
page.get_by_title(text)title属性定位。page.get_by_title("Help").click()
page.get_by_test_id(test_id_value)data-testid属性定位(最推荐用于测试)。page.get_by_test_id("submit-btn").click()

HTML 示例

<formid="loginForm"><labelfor="email">Email Address:</label><inputtype="email"id="email"name="email"placeholder="Enter your email"data-testid="email-input"><labelfor="password">Password:</label><inputtype="password"id="password"name="password"data-testid="password-input"><inputtype="checkbox"id="remember"name="remember"data-testid="remember-checkbox"><labelfor="remember">Remember me</label><inputtype="radio"id="male"name="gender"value="male"data-testid="gender-male"><labelfor="male">Male</label><inputtype="radio"id="female"name="gender"value="female"data-testid="gender-female"><labelfor="female">Female</label><buttontype="submit"data-testid="submit-btn">Log In</button></form>

Playwright 样例

# conftest.py (典型 fixture 设置)importpytestfromplaywright.sync_apiimportsync_playwright@pytest.fixture(scope="session")defbrowser():withsync_playwright()asp:browser=p.chromium.launch(headless=False)# Set headless=True for CIyieldbrowser browser.close()@pytest.fixturedefpage(browser):context=browser.new_context()page=context.new_page()yieldpage context.close()# test_form_interactions.pydeftest_form_filling_and_submission(page):page.goto("https://your-test-site.com/login.html")# Replace with your URL# Fill inputs using various locatorspage.get_by_label("Email Address:").fill("test@example.com")# Using label textpage.get_by_test_id("password-input").fill("SecurePass123!")# Using test-id# Interact with checkboxes and radio buttonsremember_checkbox=page.get_by_test_id("remember-checkbox")gender_male_radio=page.get_by_test_id("gender-male")gender_female_radio=page.get_by_test_id("gender-female")# Check initial stateassertnotremember_checkbox.is_checked()assertnotgender_male_radio.is_checked()assertnotgender_female_radio.is_checked()# Perform actionsremember_checkbox.check()# Check the boxgender_male_radio.check()# Select male radio# Verify state after actionassertremember_checkbox.is_checked()assertgender_male_radio.is_checked()assertnotgender_female_radio.is_checked()# Female should be unchecked now# Submit the formpage.get_by_test_id("submit-btn").click()# Wait for navigation or specific element after submission# Example: expect(page).to_have_url("/dashboard")# Example: expect(page.get_by_test_id("welcome-message")).to_be_visible()deftest_element_visibility_and_interaction(page):page.goto("https://your-test-site.com/some-page.html")# Wait for an element to be visible before interactingbutton=page.get_by_role("button",name="Load More")button.wait_for(state="visible")# Explicit wait if needed, though often automaticbutton.click()# Use text-based locatorstatus_message=page.get_by_text("Loading...")# Playwright waits for this element to appearassertstatus_message.is_visible()# Use CSS selector if necessary (though less preferred)loading_spinner=page.locator(".spinner")# CSS class# Wait for it to disappearloading_spinner.wait_for(state="detached")# Verify final statenew_content=page.get_by_test_id("loaded-content")assertnew_content.is_visible()# test_with_parametrization.py@pytest.mark.parametrize("username, password, expected_message",[("valid_user","valid_pass","Welcome"),("invalid_user","wrong_pass","Invalid credentials"),("","any_pass","Username is required"),])deftest_login_scenarios(page,username,password,expected_message):page.goto("https://your-test-site.com/login.html")page.get_by_test_id("username-input").fill(username)page.get_by_test_id("password-input").fill(password)page.get_by_test_id("submit-btn").click()# Wait for and assert error/success messagefromplaywright.sync_apiimportexpect message_element=page.get_by_test_id("message")# Assume there's a message divexpect(message_element).to_contain_text(expected_message)

3.2 Locator 对象的链式操作与过滤

deftest_locating_within_containers(page):page.goto("https://your-test-site.com/products.html")# Locate a container firstproduct_list=page.locator("#product-list")# Then find elements within that containerfirst_product_name=product_list.locator(".product-name").firstassertfirst_product_name.text_content()=="Product A"# Filter locators based on inner text or other conditionsdelete_buttons=page.get_by_role("button",name="Delete")# Find the delete button for a specific product nametarget_delete_button=delete_buttons.filter(has_text="Product B").first target_delete_button.click()# Get nth elementthird_item=page.locator(".list-item").nth(2)# Gets the 3rd item (0-indexed)third_item.click()

3.3 智能等待 (Implicit Waits)

Playwright 的一大优点是内置了智能等待机制。大多数操作(如.click(),.fill(),.is_visible()等)都会自动等待元素达到所需状态。

deftest_smart_waiting(page):page.goto("https://your-test-site.com/delayed-content.html")# Click a button that triggers async content loadingpage.get_by_test_id("load-data-btn").click()# No need for time.sleep()!# The following line waits automatically for the element to become visibledynamic_content=page.get_by_test_id("dynamic-content")assertdynamic_content.is_visible()assert"Loaded"indynamic_content.text_content()# Using expect for assertions also waits implicitlyfromplaywright.sync_apiimportexpect expect(dynamic_content).to_contain_text("Data Loaded Successfully")

总结

  • pytest:通过fixture管理资源和依赖,通过mark控制测试行为,通过parametrize实现数据驱动。
  • Playwright:利用get_by_*等语义化 Locator API 定位元素,利用内置等待机制简化测试代码,使测试更稳定、更易读。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 14:28:34

PingFangSC苹方字体:打破平台壁垒的专业网页字体解决方案

PingFangSC苹方字体&#xff1a;打破平台壁垒的专业网页字体解决方案 【免费下载链接】PingFangSC PingFangSC字体包文件、苹果平方字体文件&#xff0c;包含ttf和woff2格式 项目地址: https://gitcode.com/gh_mirrors/pi/PingFangSC 还在为不同操作系统下网页字体显示效…

作者头像 李华
网站建设 2026/4/11 2:59:57

Ubuntu系统如何设置开机自启?测试镜像给出答案

Ubuntu系统如何设置开机自启&#xff1f;测试镜像给出答案 在实际使用Ubuntu系统的过程中&#xff0c;我们常常需要让某些脚本或程序在系统启动时自动运行。无论是用于初始化环境、启动后台服务&#xff0c;还是执行健康检查任务&#xff0c;开机自启功能都是运维和开发中非常…

作者头像 李华
网站建设 2026/4/15 21:01:23

MGeo推理时间波动原因排查:系统负载影响实测

MGeo推理时间波动原因排查&#xff1a;系统负载影响实测 1. 背景与问题引入 你有没有遇到过这种情况&#xff1a;同样的模型、同样的输入&#xff0c;两次推理的时间却差了不少&#xff1f;有时候快得像闪电&#xff0c;有时候又慢得让人怀疑人生。这并不是你的错觉——在实际…

作者头像 李华
网站建设 2026/4/14 6:50:24

苹方字体如何让Windows平台实现15%用户停留增长?

苹方字体如何让Windows平台实现15%用户停留增长&#xff1f; 【免费下载链接】PingFangSC PingFangSC字体包文件、苹果平方字体文件&#xff0c;包含ttf和woff2格式 项目地址: https://gitcode.com/gh_mirrors/pi/PingFangSC 还在为跨平台字体显示不一致而苦恼&#xff…

作者头像 李华
网站建设 2026/4/16 12:02:14

UI-TARS Desktop:开启智能桌面操控新时代的完整实践手册

UI-TARS Desktop&#xff1a;开启智能桌面操控新时代的完整实践手册 【免费下载链接】UI-TARS-desktop A GUI Agent application based on UI-TARS(Vision-Lanuage Model) that allows you to control your computer using natural language. 项目地址: https://gitcode.com/…

作者头像 李华
网站建设 2026/4/16 12:02:26

Speech Seaco Paraformer批量识别提速:GPU并行处理优化方案

Speech Seaco Paraformer批量识别提速&#xff1a;GPU并行处理优化方案 1. 背景与痛点&#xff1a;为什么需要批量识别加速&#xff1f; 在实际语音识别场景中&#xff0c;我们常常面临大量录音文件的转写需求——比如会议记录、访谈资料、课程录音等。虽然 Speech Seaco Par…

作者头像 李华