你是不是也遇到过这些场景?
每天重复点击同样的功能点,点到怀疑人生。版本上线前通宵回归,几百个功能点一个一个验。终于下定决心做自动化,写完脚本后发现——维护成本比手工还高。
大多数团队自动化失败,不是因为技术不行,而是方法错了。
今天这篇文章,从零开始,手把手教你搭建一套可落地、可维护、可扩展的Web自动化测试框架。
一、技术选型:为什么是这套组合?
Selenium:Web自动化领域的事实标准,支持所有主流浏览器,生态最成熟。
Pytest:Python测试框架天花板。语法简洁、断言强大、插件丰富。
Allure:测试报告不仅好看,还包含步骤、截图、日志、趋势图。
二、环境准备:三步搞定
第一步:安装Python
访问 python.org 下载3.8以上版本,安装时记得勾选”Add Python to PATH”。
python --version
第二步:安装依赖
mkdir web_auto_test && cd web_auto_test
python -m venv venv
venv\Scripts\activate # Windows
source venv/bin/activate # Mac/Linux
pip install selenium pytest allure-pytest webdriver-manager
第三步:验证环境
pip list
能看到 selenium、pytest、allure-pytest 就说明安装成功。
三、第一个自动化用例:5分钟上手
需求:打开百度 → 搜索”Selenium自动化测试” → 验证搜索结果
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
def test_baidu_search():
# 1. 启动Chrome浏览器
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install())
)
try:
# 2. 打开百度
driver.get("https://www.baidu.com")
driver.maximize_window()
# 3. 输入搜索关键词
search_box = driver.find_element(By.ID, "kw")
search_box.send_keys("Selenium自动化测试")
# 4. 点击搜索按钮
driver.find_element(By.ID, "su").click()
# 5. 验证结果
assert "Selenium" in driver.title, "标题验证失败"
results = driver.find_elements(By.CSS_SELECTOR, ".result")
assert len(results) > 0, "未找到搜索结果"
print(f" 测试通过!找到 {len(results)} 条结果")
finally:
driver.quit()
if __name__ == "__main__":
test_baidu_search()
pytest test_baidu_search.py -v
四、企业级项目结构:Page Object模式
Page Object模式的核心思想:把页面元素和操作封装成类,测试用例只关心业务逻辑。
推荐项目结构
web_auto_test/
├── config/
│ └── config.py # 环境配置
├── pages/
│ ├── base_page.py # 基础页面类
│ └── login_page.py # 登录页面
├── tests/
│ ├── conftest.py # pytest配置
│ └── test_login.py # 测试用例
├── reports/ # 测试报告
└── pytest.ini
基础页面类
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
"""所有页面对象的基类"""
def __init__(self, driver):
self.driver = driver
self.timeout = 10
def find_element(self, locator):
return WebDriverWait(self.driver, self.timeout).until(
EC.presence_of_element_located(locator)
)
def click(self, locator):
self.find_element(locator).click()
def input_text(self, locator, text):
element = self.find_element(locator)
element.clear()
element.send_keys(text)
def is_visible(self, locator):
try:
WebDriverWait(self.driver, 5).until(
EC.visibility_of_element_located(locator)
)
return True
except:
return False
五、实战:登录自动化测试
配置文件
# config/config.py
BASE_URL = "https://your-test-site.com"
USERNAME = "test_user"
PASSWORD = "test_password"
HEADLESS = False
登录页面对象
from selenium.webdriver.common.by import By
from pages.base_page import BasePage
class LoginPage(BasePage):
USERNAME_INPUT = (By.ID, "username")
PASSWORD_INPUT = (By.ID, "password")
LOGIN_BUTTON = (By.CSS_SELECTOR, "button[type='submit']")
ERROR_MSG = (By.CLASS_NAME, "error-msg")
def open(self):
self.driver.get(BASE_URL + "/login")
return self
def login(self, username, password):
self.input_text(self.USERNAME_INPUT, username)
self.input_text(self.PASSWORD_INPUT, password)
self.click(self.LOGIN_BUTTON)
return self
def get_error_message(self):
if self.is_visible(self.ERROR_MSG):
return self.find_element(self.ERROR_MSG).text
return None
def is_login_success(self):
return "dashboard" in self.driver.current_url
Pytest配置和Fixtures
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from config.config import *
@pytest.fixture(scope="function")
def driver():
options = webdriver.ChromeOptions()
if HEADLESS:
options.add_argument("--headless")
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
driver.maximize_window()
driver.implicitly_wait(5)
yield driver
driver.quit()
@pytest.fixture(scope="function")
def login_page(driver):
from pages.login_page import LoginPage
return LoginPage(driver)
登录测试用例
import pytest
import allure
from config.config import *
@allure.feature("登录模块")
class TestLogin:
@allure.story("正常登录")
def test_login_success(self, login_page):
login_page.open()
login_page.login(USERNAME, PASSWORD)
assert login_page.is_login_success(), "登录失败"
@allure.story("异常登录")
@pytest.mark.parametrize("username,password,expected_error", [
("wrong_user", PASSWORD, "用户名或密码错误"),
(USERNAME, "wrong_pass", "用户名或密码错误"),
("", PASSWORD, "请输入用户名"),
(USERNAME, "", "请输入密码"),
])
def test_login_failed(self, login_page, username, password, expected_error):
login_page.open()
login_page.login(username, password)
error_msg = login_page.get_error_message()
assert expected_error in str(error_msg)
六、运行测试与生成报告
# 运行所有测试
pytest tests/ -v --alluredir=reports/allure-results
# 失败重试一次
pytest tests/ -v --alluredir=reports/allure-results --reruns 1
# 生成并打开报告
allure serve reports/allure-results
七、常见问题与解决方案
问题1:元素定位失败
# 不推荐:固定等待
import time
time.sleep(3)
# 推荐:智能等待
from selenium.webdriver.support.ui import WebDriverWait
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "username"))
)
问题2:动态加载元素
# 等待loading消失
WebDriverWait(driver, 10).until(
EC.invisibility_of_element_located((By.CLASS_NAME, "loading"))
)
问题3:跨浏览器支持
# Chrome
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
# Firefox
from webdriver_manager.firefox import GeckoDriverManager
driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()))
八、总结
这篇文章我们搭建了一套完整的Web自动化测试框架:
- Selenium WebDriver — 浏览器自动化
- Pytest — 测试用例管理
- Allure — 可视化报告
- Page Object模式 — 可维护代码结构
三个核心原则:
- 选对场景比写代码更重要
- 架构先行,Page Object让代码更易维护
- 持续集成,让自动化真正落地
————————————————-
你在自动化测试中遇到过哪些问题?欢迎在评论区交流!