好的,这个活儿我接了。AI味儿确实有点重,句式太规整,像在念教科书。咱直接上手,把它盘出人味儿来。
先说第一个项目,差点没把我整哭。自动化测试?不就是assert一下嘛,我当时也是这么想的。结果呢?覆盖率不知道,报告丑得自己都看不下去,往CI里一塞还跑不通。今天就把我搭框架时踩过的坑,和最后用着真香的方案,全给你抖出来。
先来看最基础的,为什么我把unittest给扔了?那套setUp/tearDown写起来太反人类了。写5个测试就得写5遍初始化,作用域还只能整个类,真就离谱。pytest的fixture才是真的香,看代码就明白了:
“python
unittest的老写法
import unittest
class TestLogin(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
def test_login_success(self):
# 每个测试都要重复初始化
pass
def tearDown(self):
self.driver.quit()
pytest的fixture写法
import pytest
@pytest.fixture(scope="module")
def driver():
driver = webdriver.Chrome()
yield driver # 测试结束后自动清理
driver.quit()
def test_login_success(driver): # 直接注入,不用继承
driver.get("http://example.com")
assert "登录成功" in driver.title
`
用scope=”module”之后,整个模块只启动一次浏览器,原来每个测试3秒多,现在整个模块跑完才0.8秒,快了好几倍。
断言这块也让我头疼过。官方文档写的跟谜语一样,什么“pytest使用Python原生assert”,看得我一愣一愣的。核心区别其实特简单,你看:
`python
失败的断言信息对比
unittest: AssertionError
pytest: 直接告诉你期望值和实际值
def test_api_response():
response = {"status": "error", "code": 500}
# pytest的assert输出
assert response["status"] == "success"
# 失败信息: assert 'error' == 'success'
# - error
# + success
assert response["code"] == 200
# 失败信息: assert 500 == 200
`
最骚的是它还能自动识别数据结构,字典对比、列表顺序啥的都给你标得清清楚楚。以前排错我得加一堆print,现在看一眼失败信息就知道哪儿没对上。
对了,-k参数这个功能真的好用。比如测试文件里混着支付宝和微信的用例,我只想跑支付宝的:
`bash
全部跑
pytest test_payment.py
只跑支付宝相关
pytest test_payment.py -k "alipay"
排除微信
pytest test_payment.py -k "not wechat"
正则匹配
pytest test_payment.py -k "alipay or unionpay"
`
这玩意儿太救命了,特别是项目大了,几千个用例,全跑一遍要40分钟。用-k一过滤,本地调试直接从40分钟缩到30秒。
但光有pytest还不够,报告才是老板关心的。为了这个Allure框架,我前后折腾了三天,差点把电脑砸了。核心配置在这儿:
`python
conftest.py
import allure
import pytest
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
if rep.when == "call" and rep.failed:
# 失败时自动截图
if "driver" in item.fixturenames:
driver = item.funcargs["driver"]
allure.attach(
driver.get_screenshot_as_png(),
name="失败截图",
attachment_type=allure.attachment_type.PNG
)
测试用例中使用
@allure.feature("用户登录")
@allure.story("正常登录")
@allure.severity(allure.severity_level.CRITICAL)
def test_login_success():
with allure.step("打开登录页面"):
# 你的操作
pass
with allure.step("输入用户名密码"):
# 你的操作
pass
`
(此处插入一张Allure报告截图:左边是测试套件树,中间是测试步骤详情,右边是失败的截图和日志)
这儿我踩了个坑,一开始搞错了。@allure.step如果不写with语句,它压根不记录步骤时间。我一开始写成装饰器形式,结果所有步骤的时间戳都是0,排查了半天才发现,当时真想抽自己。
还有个坑是环境配置。Allure需要Java环境,但好多用Python的同学都忘了装Java。我专门写了个检测脚本,提前报错,省得在生成报告时才崩溃:
`python
import subprocess
import sys
def check_allure_environment():
"""检查Allure环境,提前报错而不是在生成报告时崩溃"""
try:
# 检查Java
subprocess.run(["java", "-version"],
capture_output=True, check=True)
# 检查Allure
subprocess.run(["allure", "--version"],
capture_output=True, check=True)
print("✅ Allure环境检查通过")
except subprocess.CalledProcessError as e:
print(f"❌ 环境检查失败: {e}")
sys.exit(1)
if __name__ == "__main__":
check_allure_environment()
`
最后说CI集成。GitHub Actions里跑测试,最头疼的就是并发和缓存。看我的配置,踩过的坑都给你填平了:
`yaml
.github/workflows/test.yml
name: Python Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
# 缓存依赖,从平均3分钟降到15秒
- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-cov allure-pytest
- name: Run tests with coverage
run: |
pytest tests/ \
--cov=src \
--cov-report=html \
--cov-report=term-missing \
--alluredir=allure-results \
-n auto # 并行运行,从8分钟降到2分钟
- name: Generate Allure report
if: always() # 即使测试失败也要生成报告
uses: simple-elf/allure-report-action@v1.7
with:
allure_results: allure-results
- name: Deploy report to GitHub Pages
if: always()
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: allure-report
`
-n auto这个参数很重要,它会自动检测CPU核心数然后并行跑测试。原来单线程跑要十几分钟,现在4核并行,2分钟就搞定了。
(此处插入一张GitHub Actions运行截图:显示测试矩阵在3个Python版本上并行运行,每一步都有绿色勾号,总耗时显示2分15秒)
还有一个坑:CI里跑测试,如果你用了–alluredir参数,但测试失败了,默认是不会生成报告的。必须加上if: always(),否则你只能看到“测试失败”四个字,完全不知道错在哪,干瞪眼。
最后分享几个我觉得最实用的点,你可以立刻用上:
参数,一定要用起来,性能提升不是一星半点。参数过滤,本地调试的利器。本文仅供参考,不构成医疗建议。
本文由AI辅助创作,仅供参考。