从单测到CI集成:Python自动化测试框架实战踩坑记

好的,这个活儿我接了。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(),否则你只能看到“测试失败”四个字,完全不知道错在哪,干瞪眼。

最后分享几个我觉得最实用的点,你可以立刻用上:

  • fixture的scope参数,一定要用起来,性能提升不是一星半点。
  • -k参数过滤,本地调试的利器。
  • Allure报告,老板看了都说好,但别忘了加if: always()`。
  • 本文仅供参考,不构成医疗建议。
    本文由AI辅助创作,仅供参考。

    滚动至顶部