欢迎光临
我们一直在努力

利用pytest和selenium的自动化实例

案例实现目的

        利用利用pytest和selenium的结合进行百度自动化搜索

        进行百度分别搜索selenium、pytest、黑神话悟空的自动化

1.首先先配置要搜索的内容,采用YAML

# 百度搜索测试数据(YAML 格式)
#
# 数据结构说明:
# – 这是一个列表(以 – 开头),每个元素是一个测试用例
# – 每个测试用例是字典,包含 3 个字段:
# case_name: 测试用例名称(用于日志输出)
# keyword: 搜索关键词(输入到百度搜索框)
# expected_in_title: 预期在页面标题中出现的文字(用于断言)
#
# 执行效果:
# pytest 会为这 3 组数据生成 3 个独立的测试用例
# 分别搜索 "selenium"、"pytest"、"黑神话悟空"

– case_name: "搜索selenium"
keyword: "selenium"
expected_in_title: "selenium"

– case_name: "搜索pytest"
keyword: "pytest"
expected_in_title: "pytest"

– case_name: "搜索黑神话悟空"
keyword: "黑神话悟空"
expected_in_title: "黑神话"

2.配置浏览器的元素定位也要YAML

# 百度搜索页面定位元素配置(YAML 格式)
#
# 设计理念:
# – 将所有元素定位器集中管理,便于维护
# – 使用自定义格式 "类型=值"(如 "id=kw"),提高可读性
# – 修改定位器只需改此文件,无需改动 Python 代码
#
# 支持的定位类型:
# id, name, css, xpath, class, tag, link_text, partial_link_text

baidu_page:
# 搜索输入框(百度首页的文本输入框)
search_input: "id=chat-textarea"

# 搜索按钮(点击后触发搜索)
search_button: "id=chat-submit-button"

# 搜索结果容器(搜索结果页面的主容器)
# CSS 选择器: #content_left 表示 id="content_left" 的元素
search_results: "css=#content_left"

# 单个搜索结果项(每条结果的 class="result")
# 用于统计搜索结果数量
result_item: "css=.result"

# 结果标题链接(每条结果的标题部分)
# CSS 选择器: .t a 表示 class="t" 的元素下的 <a> 标签
result_title: "css=.t a"

# 页面底部(百度首页的 footer 区域)
# 可用于验证页面完整性
footer: "css=#foot"

3.配置浏览器的启动conftest.py

import pytest
from selenium import webdriver
import chromedriver_autoinstaller

@pytest.fixture(scope="module")
def driver():
"""
Pytest Fixture:创建和管理 Chrome 浏览器驱动

作用域说明:
– scope="module": 整个测试模块(test_baidu_search.py)共享一个浏览器实例
– 所有测试用例执行完毕后才会关闭浏览器
– 优点:速度快(不用每次打开/关闭浏览器)
– 缺点:测试之间可能有状态污染(需要清理)

生命周期:
1. 测试开始前:创建浏览器驱动
2. yield drv: 将驱动交给测试用例使用
3. 测试结束后:执行 drv.quit() 关闭浏览器

:return: Chrome WebDriver 实例
"""
# 自动检测本地 Chrome 版本并安装匹配的 ChromeDriver
# 解决版本不兼容问题(无需手动下载驱动)
chromedriver_autoinstaller.install()

options = webdriver.ChromeOptions()

# 可选配置:无头模式(后台运行,不显示浏览器窗口)
# 适用场景:CI/CD 流水线、服务器环境
# 调试时建议注释掉,方便观察浏览器操作
# options.add_argument("–headless")

# 禁用浏览器扩展(提高测试性能,减少干扰)
options.add_argument("–disable-extensions")

# 创建 Chrome 浏览器实例
drv = webdriver.Chrome(options=options)

# 设置隐式等待:查找元素时最多等待 5 秒
# 与显式等待(WebDriverWait)配合使用,双重保障
drv.implicitly_wait(5)

# yield 关键字:将驱动交给测试用例使用
# yield 之前的代码在测试前执行,之后的代码在测试后执行
yield drv

# 所有测试结束后关闭浏览器(释放资源)
drv.quit()

4.通过的方法base_page.py

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

class BasePage:
"""
页面对象基类(所有页面继承它)
统一封装定位、点击、输入等操作,自带显式等待(10秒超时)
定位器格式:"类型=值" 例如 "id=kw", "css=.btn"
"""

def __init__(self, driver):
# 保存浏览器驱动实例,让所有方法共用同一个浏览器窗口
self.driver = driver
# 创建 10 秒的显式等待器,供所有方法使用(避免重复创建)
self.wait = WebDriverWait(driver, 10)

def find_element(self, locator):
"""等待元素出现在 DOM 中(不一定可见),找到即返回"""
by = self._get_by_type(locator)
by_value = self._get_by_value(locator)
if not by:
raise ValueError(f"不支持的定位方式")
# presence_of_element_located:只等元素出现,不管是否被遮挡
return self.wait.until(EC.presence_of_element_located((by, by_value)))

def click(self, locator):
"""查找元素并点击"""
element = self.find_element(locator)
element.click()

def input_text(self, locator, text):
"""输入框输入文本(自动清空原有内容,并等待元素变为可交互状态)"""
# element_to_be_clickable:比 find_element 更严格,要求元素可见且可操作(避免点击/输入失败)
element = self.wait.until(EC.element_to_be_clickable((
self._get_by_type(locator),
self._get_by_value(locator)
)))
try:
element.clear() # 先清空
except Exception:
pass # 非标准输入框清空失败则跳过,继续输入
element.send_keys(text)

def _get_by_type(self, locator):
"""从 "id=kw" 中取出 "id",并转为 Selenium 的 By.ID 常量"""
by_type, _ = locator.split("=", 1)
by_map = {
"id": By.ID,
"name": By.NAME,
"css": By.CSS_SELECTOR,
"xpath": By.XPATH,
"class": By.CLASS_NAME,
"tag": By.TAG_NAME,
"link_text": By.LINK_TEXT,
"partial_link_text": By.PARTIAL_LINK_TEXT,
}
return by_map.get(by_type.lower())

def _get_by_value(self, locator):
"""从 "id=kw" 中取出 "kw"(定位的值)"""
_, by_value = locator.split("=", 1)
return by_value

def get_text(self, locator):
"""获取元素的可见文本"""
element = self.find_element(locator)
return element.text

def get_page_title(self):
"""获取当前浏览器标签页的标题"""
return self.driver.title

def wait_for_element_visible(self, locator, timeout=10):
"""
等待元素在页面上可见(宽高>0且未隐藏)
与 find_element 的区别:find_element 只等 DOM 存在,不管是否可见
"""
by_type, by_value = locator.split("=", 1)
by_map = {
"id": By.ID,
"css": By.CSS_SELECTOR,
"xpath": By.XPATH,
"class": By.CLASS_NAME,
}
by = by_map.get(by_type.lower())
# visibility_of_element_located:必须实际显示在页面上
return self.wait.until(EC.visibility_of_element_located((by, by_value)))

def take_screenshot(self, filename):
"""截取当前屏幕并保存为 PNG 图片(常用于失败时保留证据)"""
self.driver.save_screenshot(filename)

5.业务操作baidu_page.py

import yaml
from base_page import BasePage

class BaiduPage(BasePage):
"""
百度首页页面对象
继承 BasePage,拥有点击、输入、等待等基础能力
所有定位器从 locators.yaml 加载,便于维护
"""

def __init__(self, driver):
"""初始化:加载定位器配置文件"""
super().__init__(driver)

# 从 YAML 加载页面元素定位信息
with open("locators.yaml", "r", encoding="utf-8") as f:
locators = yaml.safe_load(f)
self.locators = locators["baidu_page"] # 只取百度页面的部分

def open(self, url="https://www.baidu.com"):
"""打开百度首页(默认),并最大化窗口"""
self.driver.get(url)
self.driver.maximize_window()

def search(self, keyword):
"""执行搜索:输入关键词 + 点击搜索按钮"""
self.wait_for_element_visible(self.locators["search_input"]) # 确保输入框已加载
self.input_text(self.locators["search_input"], keyword)
self.click(self.locators["search_button"])

def get_search_results_count(self):
"""获取搜索结果列表中 .result 元素的数量(找不到则返回 0)"""
try:
results = self.driver.find_elements("css selector", ".result")
return len(results)
except Exception:
return 0

def get_first_result_title(self):
"""获取第一个搜索结果的标题文本(找不到返回空字符串)"""
try:
first_result = self.wait_for_element_visible(self.locators["result_title"])
return first_result.text
except Exception:
return ""

def is_results_displayed(self):
"""判断搜索结果区域(#content_left)是否可见,用于验证搜索是否成功"""
try:
self.wait_for_element_visible(self.locators["search_results"])
return True
except Exception:
return False

6.测试用例执行test_baidu_search.py

import pytest
import yaml
from baidu_page import BaiduPage

def ddt(yaml_path, **kwargs):
"""
数据驱动测试(Data-Driven Testing)装饰器

作用:
– 从 YAML 文件读取多组测试数据
– 自动将每组数据作为参数传递给测试函数
– 实现"一份测试代码,多组数据运行"

工作原理:
1. 读取 YAML 文件,解析为列表(每个元素是一组测试数据)
2. 使用 pytest.mark.parametrize 装饰测试函数
3. pytest 会为每组数据生成一个独立的测试用例

示例:
YAML 中有 3 组数据 → 自动生成 3 个测试用例
test_baidu_search[data0]
test_baidu_search[data1]
test_baidu_search[data2]

:param yaml_path: YAML 数据文件的路径,如 "test_data.yml"
:param kwargs: 传递给 pytest.mark.parametrize 的其他参数(可选)
:return: pytest 参数化装饰器
"""
with open(yaml_path, "r", encoding="utf-8") as f:
data_list = yaml.safe_load(f)

return pytest.mark.parametrize("data", data_list, **kwargs)

@ddt("test_data.yml")
def test_baidu_search(driver, data):
"""
百度搜索测试用例(数据驱动版本)

测试场景:
从 test_data.yml 读取多组搜索关键词,依次执行搜索并验证结果

测试步骤:
1. 打开百度首页
2. 输入搜索关键词
3. 点击搜索按钮
4. 验证搜索结果页面是否正确加载

:param driver: Chrome 浏览器驱动(来自 conftest.py 的 fixture)
scope="module" 表示整个测试模块共享一个浏览器实例
:param data: 从 test_data.yml 加载的测试数据(字典类型)
包含字段:case_name, keyword, expected_in_title
"""
# 初始化百度页面对象(传入共享的浏览器驱动)
baidu_page = BaiduPage(driver)

# ========== 步骤1:打开百度首页 ==========
print(f"\\n{'=' * 60}")
print(f"测试用例: {data['case_name']}")
print(f"搜索关键词: {data['keyword']}")
print(f"{'=' * 60}")

baidu_page.open()

# ========== 步骤2 & 3:执行搜索(输入关键词 + 点击按钮)==========
baidu_page.search(data["keyword"])

# ========== 步骤4:验证搜索结果 ==========

# 断言1:搜索结果区域必须显示
assert baidu_page.is_results_displayed(), "搜索结果未显示"

# 断言2:页面标题必须包含预期关键词
page_title = baidu_page.get_page_title()
print(f"页面标题: {page_title}")
print(f"预期包含: {data['expected_in_title']}")

# 使用 lower() 转为小写进行比较(不区分大小写)
assert data["expected_in_title"].lower() in page_title.lower(), \\
f"页面标题 '{page_title}' 不包含预期关键词 '{data['expected_in_title']}'"

# 断言3:搜索结果数量必须大于 0
results_count = baidu_page.get_search_results_count()
print(f"搜索结果数量: {results_count}")

assert results_count > 0, "没有搜索结果"

print(f"✅ 测试通过: {data['case_name']}")

7.配置文件如生成日志等操作

[pytest]
# 遇到失败不停止,执行所有测试
maxfail = 0

# 详细输出模式,显示 print 内容和日志
addopts = -v -s –tb=short –log-cli-level=INFO

# 日志配置
log_cli = true
log_cli_level = INFO
log_cli_format = %(asctime)s [%(levelname)s] %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S

# 日志文件配置(可选:生成日志文件)
log_file = logs/pytest_execution.log
log_file_level = DEBUG
log_file_format = %(asctime)s [%(levelname)s] %(name)s – %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S

# 禁用警告信息
filterwarnings = ignore::DeprecationWarning

# 测试文件命名模式
python_files = test_*.py

# 测试函数命名模式
python_functions = test_*

运行test_baidu_search.py就可以就看到结果了

赞(0)
未经允许不得转载:171主机测评 » 利用pytest和selenium的自动化实例
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址