09.pytest的插件pytest-result-log

Pytest 插件 pytest-result-log 详解

1. 什么是 pytest-result-log

1.1 插件简介

pytest-result-log 是一个用于记录和管理 pytest 测试结果的插件。它可以将测试执行过程中的详细信息保存到日志文件中,方便后续分析和查看。这个插件特别适合需要长期保存测试历史、生成测试报告、或者进行测试数据分析的场景。

1.2 为什么需要 pytest-result-log

在日常测试工作中,我们经常会遇到以下问题:

  1. 测试结果丢失:命令行中的测试结果在终端关闭后就看不到了
  2. 历史记录缺失:无法查看之前的测试执行情况
  3. 结果分析困难:没有结构化的数据,难以进行统计分析
  4. 团队协作不便:无法方便地分享测试结果给团队成员

没有插件的情况

# 运行测试
pytest test_example.py

# 输出结果只在终端显示,关闭终端后就看不到了
======================== test session starts ==========================
platform win32 -- Python 3.9.0, pytest-7.0.0
collected 3 items

test_example.py ...                                              [100%]

======================== 3 passed in 0.05s ==========================

使用 pytest-result-log 后

# 运行测试并生成日志
pytest test_example.py --result-log=test_results.log

# 测试结果会保存到 test_results.log 文件中
# 可以随时查看、分析、分享

1.3 插件的主要功能

  • 记录测试结果:自动记录每个测试用例的执行结果
  • 保存详细信息:包括测试名称、执行时间、状态、错误信息等
  • 多种日志格式:支持文本、JSON、XML 等多种格式
  • 灵活配置:可以自定义日志内容和格式
  • 历史追踪:可以追加或覆盖日志文件

2. 安装 pytest-result-log

2.1 使用 pip 安装

基本安装命令

pip install pytest-result-log

指定版本安装

# 安装特定版本
pip install pytest-result-log==1.0.0

# 安装最新版本
pip install pytest-result-log --upgrade

2.2 从 requirements.txt 安装

创建 requirements.txt 文件

pytest>=7.0.0
pytest-result-log>=1.0.0

安装依赖

pip install -r requirements.txt

2.3 验证安装

方法 1:查看插件版本

pip show pytest-result-log

输出示例

Name: pytest-result-log
Version: 1.0.0
Summary: A pytest plugin for logging test results
Location: /path/to/site-packages

方法 2:查看 pytest 插件列表

pytest --trace-config

输出示例

============================= test session starts ==============================
platform win32 -- Python 3.9.0, pytest-7.0.0
cachedir: .pytest_cache
plugins: result-log-1.0.0

方法 3:查看帮助信息

pytest --help | grep result-log

3. 基本用法

3.1 最简单的使用方式

基本命令

pytest --result-log=test_results.log

示例

# 运行测试并生成日志文件
pytest test_example.py --result-log=test_results.log

生成的日志文件内容示例

2024-01-15 10:30:15,123 - INFO - Test session started
2024-01-15 10:30:15,124 - INFO - Collected 3 items
2024-01-15 10:30:15,200 - PASS - test_example.py::test_addition - 0.001s
2024-01-15 10:30:15,201 - PASS - test_example.py::test_subtraction - 0.001s
2024-01-15 10:30:15,202 - PASS - test_example.py::test_multiplication - 0.001s
2024-01-15 10:30:15,203 - INFO - Test session finished: 3 passed in 0.05s

3.2 指定日志文件路径

相对路径

# 保存到当前目录
pytest --result-log=test_results.log

# 保存到指定目录
pytest --result-log=logs/test_results.log

绝对路径

# Windows
pytest --result-log=C:UsersAdministratorDesktoptest_results.log

# Linux/Mac
pytest --result-log=/home/user/test_results.log

3.3 查看日志文件

使用文本编辑器

# Windows
notepad test_results.log

# Linux/Mac
nano test_results.log
# 或
vim test_results.log

使用命令行查看

# Windows PowerShell
Get-Content test_results.log

# Linux/Mac
cat test_results.log

# 查看最后几行
tail -n 20 test_results.log

4. 详细示例

4.1 示例 1:基本测试结果记录

测试文件:test_calculator.py

def test_addition():
    """测试加法"""
    assert 1 + 1 == 2

def test_subtraction():
    """测试减法"""
    assert 5 - 3 == 2

def test_multiplication():
    """测试乘法"""
    assert 2 * 3 == 6

def test_division():
    """测试除法"""
    assert 10 / 2 == 5

运行命令

pytest test_calculator.py --result-log=calculator_results.log -v

生成的日志文件:calculator_results.log

2024-01-15 10:30:15,123 - INFO - ============================= test session starts ==============================
2024-01-15 10:30:15,124 - INFO - platform win32 -- Python 3.9.0, pytest-7.0.0
2024-01-15 10:30:15,125 - INFO - rootdir: C:UsersAdministratorDesktoppython自动化测试
2024-01-15 10:30:15,126 - INFO - collected 4 items
2024-01-15 10:30:15,200 - PASS - test_calculator.py::test_addition - 0.001s
2024-01-15 10:30:15,201 - PASS - test_calculator.py::test_subtraction - 0.001s
2024-01-15 10:30:15,202 - PASS - test_calculator.py::test_multiplication - 0.001s
2024-01-15 10:30:15,203 - PASS - test_calculator.py::test_division - 0.001s
2024-01-15 10:30:15,204 - INFO - ============================= 4 passed in 0.05s ==============================

4.2 示例 2:记录失败的测试

测试文件:test_with_failures.py

def test_pass():
    """通过的测试"""
    assert True

def test_fail():
    """失败的测试"""
    assert False, "这是一个失败的测试"

def test_error():
    """出错的测试"""
    raise ValueError("这是一个错误")

运行命令

pytest test_with_failures.py --result-log=failures.log -v

生成的日志文件:failures.log

2024-01-15 10:35:20,100 - INFO - ============================= test session starts ==============================
2024-01-15 10:35:20,101 - INFO - collected 3 items
2024-01-15 10:35:20,200 - PASS - test_with_failures.py::test_pass - 0.001s
2024-01-15 10:35:20,201 - FAIL - test_with_failures.py::test_fail - 0.001s
2024-01-15 10:35:20,201 - ERROR - AssertionError: 这是一个失败的测试
2024-01-15 10:35:20,201 - ERROR - test_with_failures.py:5: in test_fail
2024-01-15 10:35:20,201 - ERROR -     assert False, "这是一个失败的测试"
2024-01-15 10:35:20,202 - ERROR - test_with_failures.py::test_error - 0.001s
2024-01-15 10:35:20,202 - ERROR - ValueError: 这是一个错误
2024-01-15 10:35:20,202 - ERROR - test_with_failures.py:9: in test_error
2024-01-15 10:35:20,202 - ERROR -     raise ValueError("这是一个错误")
2024-01-15 10:35:20,203 - INFO - ============================= 1 passed, 1 failed, 1 error in 0.05s ==============================

4.3 示例 3:记录跳过的测试

测试文件:test_with_skips.py

import pytest

def test_pass():
    """通过的测试"""
    assert True

@pytest.mark.skip(reason="功能尚未实现")
def test_skip():
    """跳过的测试"""
    assert False

@pytest.mark.skipif(True, reason="条件不满足")
def test_skipif():
    """条件跳过的测试"""
    assert True

运行命令

pytest test_with_skips.py --result-log=skips.log -v

生成的日志文件:skips.log

2024-01-15 10:40:30,100 - INFO - ============================= test session starts ==============================
2024-01-15 10:40:30,101 - INFO - collected 3 items
2024-01-15 10:40:30,200 - PASS - test_with_skips.py::test_pass - 0.001s
2024-01-15 10:40:30,201 - SKIP - test_with_skips.py::test_skip - 0.000s
2024-01-15 10:40:30,201 - SKIP - Reason: 功能尚未实现
2024-01-15 10:40:30,202 - SKIP - test_with_skips.py::test_skipif - 0.000s
2024-01-15 10:40:30,202 - SKIP - Reason: 条件不满足
2024-01-15 10:40:30,203 - INFO - ============================= 1 passed, 2 skipped in 0.05s ==============================

4.4 示例 4:记录参数化测试

测试文件:test_parametrize.py

import pytest

@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (3, 4, 7),
    (5, 6, 11),
])
def test_addition(a, b, expected):
    """参数化测试加法"""
    assert a + b == expected

运行命令

pytest test_parametrize.py --result-log=parametrize.log -v

生成的日志文件:parametrize.log

2024-01-15 10:45:40,100 - INFO - ============================= test session starts ==============================
2024-01-15 10:45:40,101 - INFO - collected 3 items
2024-01-15 10:45:40,200 - PASS - test_parametrize.py::test_addition[1-2-3] - 0.001s
2024-01-15 10:45:40,201 - PASS - test_parametrize.py::test_addition[3-4-7] - 0.001s
2024-01-15 10:45:40,202 - PASS - test_parametrize.py::test_addition[5-6-11] - 0.001s
2024-01-15 10:45:40,203 - INFO - ============================= 3 passed in 0.05s ==============================

4.5 示例 5:记录测试执行时间

测试文件:test_timing.py

import time

def test_fast():
    """快速测试"""
    time.sleep(0.1)
    assert True

def test_medium():
    """中等速度测试"""
    time.sleep(0.5)
    assert True

def test_slow():
    """慢速测试"""
    time.sleep(1.0)
    assert True

运行命令

pytest test_timing.py --result-log=timing.log -v

生成的日志文件:timing.log

2024-01-15 10:50:50,100 - INFO - ============================= test session starts ==============================
2024-01-15 10:50:50,101 - INFO - collected 3 items
2024-01-15 10:50:50,250 - PASS - test_timing.py::test_fast - 0.101s
2024-01-15 10:50:50,750 - PASS - test_timing.py::test_medium - 0.501s
2024-01-15 10:51:51,750 - PASS - test_timing.py::test_slow - 1.001s
2024-01-15 10:51:51,751 - INFO - ============================= 3 passed in 1.65s ==============================

5. 高级配置

5.1 日志格式配置

5.1.1 使用 JSON 格式

命令

pytest --result-log=results.json --result-log-format=json

生成的 JSON 日志示例

{
  "session": {
    "start_time": "2024-01-15T10:30:15.123",
    "platform": "win32",
    "python_version": "3.9.0",
    "pytest_version": "7.0.0"
  },
  "tests": [
    {
      "nodeid": "test_example.py::test_addition",
      "outcome": "passed",
      "duration": 0.001,
      "timestamp": "2024-01-15T10:30:15.200"
    },
    {
      "nodeid": "test_example.py::test_subtraction",
      "outcome": "passed",
      "duration": 0.001,
      "timestamp": "2024-01-15T10:30:15.201"
    }
  ],
  "summary": {
    "total": 2,
    "passed": 2,
    "failed": 0,
    "skipped": 0,
    "errors": 0
  }
}

5.1.2 使用 XML 格式

命令

pytest --result-log=results.xml --result-log-format=xml

生成的 XML 日志示例

<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="pytest" tests="2" failures="0" errors="0" skipped="0" time="0.05">
  <testcase classname="test_example" name="test_addition" time="0.001">
  </testcase>
  <testcase classname="test_example" name="test_subtraction" time="0.001">
  </testcase>
</testsuite>

5.1.3 使用文本格式(默认)

命令

pytest --result-log=results.log --result-log-format=text

5.2 日志级别配置

5.2.1 只记录失败的测试

命令

pytest --result-log=results.log --result-log-level=failures

示例

# test_example.py
def test_pass():
    assert True

def test_fail():
    assert False

生成的日志

2024-01-15 10:30:15,200 - FAIL - test_example.py::test_fail - 0.001s
2024-01-15 10:30:15,200 - ERROR - AssertionError

5.2.2 记录所有信息(包括通过的测试)

命令

pytest --result-log=results.log --result-log-level=all

5.2.3 只记录摘要信息

命令

pytest --result-log=results.log --result-log-level=summary

生成的日志

2024-01-15 10:30:15,203 - INFO - ============================= 3 passed in 0.05s ==============================

5.3 日志文件追加模式

5.3.1 追加到现有文件

命令

pytest --result-log=results.log --result-log-append

示例

# 第一次运行
pytest test_example.py --result-log=results.log --result-log-append

# 第二次运行(结果会追加到文件末尾)
pytest test_example.py --result-log=results.log --result-log-append

生成的日志文件

2024-01-15 10:30:15,200 - PASS - test_example.py::test_addition - 0.001s
2024-01-15 10:35:20,100 - PASS - test_example.py::test_addition - 0.001s

5.3.2 覆盖现有文件(默认)

命令

pytest --result-log=results.log
# 默认会覆盖现有文件

5.4 在 pytest.ini 中配置

pytest.ini 配置文件

[pytest]
# 基本配置
addopts = 
    --result-log=test_results.log
    --result-log-format=text
    --result-log-level=all
    -v

# 或者使用相对路径
result_log = logs/test_results.log
result_log_format = text
result_log_level = all

使用配置

# 直接运行 pytest,会自动使用配置文件中的设置
pytest

5.5 在 pyproject.toml 中配置

pyproject.toml 配置文件

[tool.pytest.ini_options]
addopts = [
    "--result-log=test_results.log",
    "--result-log-format=text",
    "--result-log-level=all",
    "-v"
]

[tool.pytest.ini_options.result_log]
file = "logs/test_results.log"
format = "text"
level = "all"

6. 实际应用场景

6.1 场景 1:持续集成(CI)环境

需求:在 CI 环境中运行测试并保存结果

解决方案

# GitHub Actions 示例
pytest 
  --result-log=ci_results.log 
  --result-log-format=json 
  --junitxml=junit.xml 
  --html=report.html

GitHub Actions 配置示例

name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.9'
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
      - name: Run tests
        run: |
          pytest --result-log=ci_results.log --result-log-format=json
      - name: Upload results
        uses: actions/upload-artifact@v2
        with:
          name: test-results
          path: ci_results.log

6.2 场景 2:测试历史追踪

需求:保存每次测试运行的结果,用于分析测试趋势

解决方案

# 使用时间戳命名日志文件
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
pytest --result-log=results/history_${TIMESTAMP}.log

# Windows PowerShell
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
pytest --result-log="results/history_${timestamp}.log"

Python 脚本示例

import subprocess
from datetime import datetime

def run_tests_with_logging():
    """运行测试并保存带时间戳的日志"""
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    log_file = f"results/history_{timestamp}.log"

    cmd = [
        "pytest",
        "--result-log", log_file,
        "--result-log-format=json",
        "-v"
    ]

    subprocess.run(cmd)
    print(f"测试结果已保存到: {log_file}")

if __name__ == "__main__":
    run_tests_with_logging()

6.3 场景 3:测试结果分析

需求:分析测试结果,生成统计报告

解决方案

import json
from datetime import datetime
from collections import Counter

def analyze_test_results(log_file):
    """分析测试结果日志"""
    with open(log_file, 'r', encoding='utf-8') as f:
        data = json.load(f)

    # 统计信息
    summary = data.get('summary', {})
    tests = data.get('tests', [])

    # 按状态统计
    outcomes = Counter(test['outcome'] for test in tests)

    # 计算平均执行时间
    durations = [test['duration'] for test in tests if 'duration' in test]
    avg_duration = sum(durations) / len(durations) if durations else 0

    # 找出最慢的测试
    slowest_tests = sorted(
        [(test['nodeid'], test.get('duration', 0)) for test in tests],
        key=lambda x: x[1],
        reverse=True
    )[:5]

    # 打印报告
    print("=" * 60)
    print("测试结果分析报告")
    print("=" * 60)
    print(f"总测试数: {summary.get('total', 0)}")
    print(f"通过: {summary.get('passed', 0)}")
    print(f"失败: {summary.get('failed', 0)}")
    print(f"跳过: {summary.get('skipped', 0)}")
    print(f"错误: {summary.get('errors', 0)}")
    print(f"n平均执行时间: {avg_duration:.3f}秒")
    print(f"n最慢的5个测试:")
    for nodeid, duration in slowest_tests:
        print(f"  {nodeid}: {duration:.3f}秒")
    print("=" * 60)

# 使用示例
if __name__ == "__main__":
    analyze_test_results("test_results.json")

6.4 场景 4:团队协作

需求:将测试结果分享给团队成员

解决方案

# 生成详细的测试日志
pytest 
  --result-log=team_results.log 
  --result-log-format=text 
  --result-log-level=all 
  --html=report.html 
  --self-contained-html

# 将日志文件发送给团队
# 可以通过邮件、共享文件夹、版本控制系统等方式分享

6.5 场景 5:调试失败的测试

需求:保存失败测试的详细信息,方便后续调试

解决方案

# 只记录失败的测试,包含详细错误信息
pytest 
  --result-log=failures.log 
  --result-log-level=failures 
  --tb=long 
  -v

生成的日志示例

2024-01-15 10:30:15,200 - FAIL - test_example.py::test_fail - 0.001s
2024-01-15 10:30:15,200 - ERROR - AssertionError: 测试失败
2024-01-15 10:30:15,200 - ERROR - 
2024-01-15 10:30:15,200 - ERROR - test_example.py:5: in test_fail
2024-01-15 10:30:15,200 - ERROR -     assert 1 == 2
2024-01-15 10:30:15,200 - ERROR - E   AssertionError
2024-01-15 10:30:15,200 - ERROR - 
2024-01-15 10:30:15,200 - ERROR - Captured stdout:
2024-01-15 10:30:15,200 - ERROR - 这是测试输出

7. 与其他插件配合使用

7.1 与 pytest-html 配合

同时生成 HTML 报告和日志文件

pytest 
  --result-log=test_results.log 
  --html=report.html 
  --self-contained-html

优势

  • HTML 报告:可视化展示,适合查看
  • 日志文件:结构化数据,适合分析

7.2 与 pytest-cov 配合

同时记录测试结果和覆盖率

pytest 
  --result-log=test_results.log 
  --cov=myproject 
  --cov-report=html 
  --cov-report=term

7.3 与 pytest-xdist 配合

并行执行测试并记录结果

pytest 
  --result-log=test_results.log 
  -n auto 
  --dist=worksteal

注意:并行执行时,日志文件可能会包含来自不同进程的混合输出。建议使用 JSON 格式,便于后续处理。

7.4 与 pytest-rerunfailures 配合

失败重试并记录所有尝试

pytest 
  --result-log=test_results.log 
  --reruns=3 
  --reruns-delay=2

日志会记录每次重试的结果

2024-01-15 10:30:15,200 - FAIL - test_example.py::test_flaky - 0.001s (尝试 1/3)
2024-01-15 10:30:17,200 - FAIL - test_example.py::test_flaky - 0.001s (尝试 2/3)
2024-01-15 10:30:19,200 - PASS - test_example.py::test_flaky - 0.001s (尝试 3/3)

8. 常见问题和解决方案

8.1 问题 1:日志文件太大

问题描述:测试用例很多时,日志文件会变得非常大。

解决方案 1:只记录失败的测试

pytest --result-log=results.log --result-log-level=failures

解决方案 2:使用压缩格式

# 使用 JSON 格式(通常比文本格式更紧凑)
pytest --result-log=results.json --result-log-format=json

解决方案 3:定期清理旧日志

import os
import glob
from datetime import datetime, timedelta

def clean_old_logs(log_dir, days=7):
    """清理7天前的日志文件"""
    cutoff_date = datetime.now() - timedelta(days=days)

    for log_file in glob.glob(os.path.join(log_dir, "*.log")):
        file_time = datetime.fromtimestamp(os.path.getmtime(log_file))
        if file_time < cutoff_date:
            os.remove(log_file)
            print(f"已删除旧日志: {log_file}")

if __name__ == "__main__":
    clean_old_logs("results", days=7)

8.2 问题 2:日志文件权限错误

问题描述:在某些系统上,可能没有写入日志文件的权限。

解决方案 1:检查文件权限

# Linux/Mac
ls -l test_results.log
chmod 644 test_results.log

# Windows
# 检查文件是否被其他程序占用

解决方案 2:使用相对路径

# 使用当前用户有权限的目录
pytest --result-log=./test_results.log

解决方案 3:创建日志目录

import os

# 确保日志目录存在
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)

# 然后运行测试
# pytest --result-log=logs/test_results.log

8.3 问题 3:日志格式不统一

问题描述:不同时间运行的测试,日志格式可能不一致。

解决方案:使用配置文件统一格式

# pytest.ini
[pytest]
addopts = 
    --result-log=test_results.log
    --result-log-format=json
    --result-log-level=all

8.4 问题 4:并行执行时日志混乱

问题描述:使用 pytest-xdist 并行执行时,日志输出可能混乱。

解决方案 1:使用 JSON 格式

pytest --result-log=results.json --result-log-format=json -n auto

解决方案 2:每个进程生成独立日志

# conftest.py
import os
import pytest

@pytest.fixture(scope="session", autouse=True)
def setup_logging():
    """为每个进程设置独立的日志文件"""
    worker_id = os.environ.get("PYTEST_XDIST_WORKER")
    if worker_id:
        log_file = f"test_results_worker_{worker_id}.log"
        pytest.ini_set("result_log", log_file)

8.5 问题 5:日志文件编码问题

问题描述:包含中文或其他特殊字符时,日志文件可能出现乱码。

解决方案:指定 UTF-8 编码

# 在 pytest.ini 中配置
[pytest]
result_log_encoding = utf-8

或者在代码中处理

# conftest.py
import pytest

def pytest_configure(config):
    """配置日志编码"""
    config.option.result_log_encoding = "utf-8"

9. 最佳实践

9.1 日志文件命名规范

推荐命名方式

# 使用时间戳
pytest --result-log=test_results_20240115_103015.log

# 使用项目名称
pytest --result-log=myproject_test_results.log

# 使用环境标识
pytest --result-log=test_results_prod.log
pytest --result-log=test_results_dev.log

9.2 日志文件组织

推荐的目录结构

project/
├── tests/
│   ├── test_example.py
│   └── ...
├── logs/
│   ├── test_results_20240115.log
│   ├── test_results_20240116.log
│   └── ...
├── reports/
│   ├── html/
│   └── json/
└── pytest.ini

9.3 日志保留策略

建议

  • 开发环境:保留最近 7 天的日志
  • 测试环境:保留最近 30 天的日志
  • 生产环境:保留最近 90 天的日志

实现示例

import os
import glob
from datetime import datetime, timedelta

def cleanup_old_logs(log_dir, days_to_keep=7):
    """清理旧日志文件"""
    cutoff = datetime.now() - timedelta(days=days_to_keep)

    for log_file in glob.glob(os.path.join(log_dir, "*.log")):
        file_time = datetime.fromtimestamp(os.path.getmtime(log_file))
        if file_time < cutoff:
            os.remove(log_file)
            print(f"已删除: {log_file}")

9.4 日志内容规范

建议包含的信息

  • 测试会话开始时间
  • 测试环境信息(Python 版本、pytest 版本等)
  • 每个测试的详细信息(名称、状态、时间、错误信息)
  • 测试会话摘要

9.5 性能考虑

优化建议

  1. 只记录必要信息:使用 --result-log-level 控制记录级别
  2. 使用合适的格式:JSON 格式便于程序处理,文本格式便于阅读
  3. 异步写入:对于大量测试,考虑使用异步日志写入
  4. 定期清理:避免日志文件积累过多

10. 完整示例项目

10.1 项目结构

myproject/
├── myproject/
│   ├── __init__.py
│   ├── calculator.py
│   └── utils.py
├── tests/
│   ├── __init__.py
│   ├── test_calculator.py
│   └── test_utils.py
├── logs/
│   └── .gitkeep
├── pytest.ini
├── requirements.txt
└── README.md

10.2 源代码文件

myproject/calculator.py

class Calculator:
    """计算器类"""

    def add(self, a, b):
        """加法"""
        return a + b

    def subtract(self, a, b):
        """减法"""
        return a - b

    def multiply(self, a, b):
        """乘法"""
        return a * b

    def divide(self, a, b):
        """除法"""
        if b == 0:
            raise ValueError("除数不能为0")
        return a / b

myproject/utils.py

def format_number(num):
    """格式化数字"""
    return f"{num:.2f}"

def validate_positive(num):
    """验证正数"""
    if num <= 0:
        raise ValueError("必须是正数")
    return True

10.3 测试文件

tests/test_calculator.py

import pytest
from myproject.calculator import Calculator

@pytest.fixture
def calc():
    """计算器 fixture"""
    return Calculator()

def test_add(calc):
    """测试加法"""
    assert calc.add(1, 2) == 3

def test_subtract(calc):
    """测试减法"""
    assert calc.subtract(5, 3) == 2

def test_multiply(calc):
    """测试乘法"""
    assert calc.multiply(2, 3) == 6

def test_divide(calc):
    """测试除法"""
    assert calc.divide(10, 2) == 5

def test_divide_by_zero(calc):
    """测试除零错误"""
    with pytest.raises(ValueError):
        calc.divide(10, 0)

tests/test_utils.py

import pytest
from myproject.utils import format_number, validate_positive

def test_format_number():
    """测试数字格式化"""
    assert format_number(3.14159) == "3.14"

def test_validate_positive():
    """测试正数验证"""
    assert validate_positive(5) is True

def test_validate_positive_fail():
    """测试正数验证失败"""
    with pytest.raises(ValueError):
        validate_positive(-1)

10.4 配置文件

pytest.ini

[pytest]
# 测试路径
testpaths = tests

# 测试文件模式
python_files = test_*.py
python_classes = Test*
python_functions = test_*

# 日志配置
addopts = 
    --result-log=logs/test_results.log
    --result-log-format=text
    --result-log-level=all
    -v
    --tb=short

# 标记
markers =
    slow: 慢速测试
    integration: 集成测试
    unit: 单元测试

requirements.txt

pytest>=7.0.0
pytest-result-log>=1.0.0

10.5 运行测试

基本运行

# 安装依赖
pip install -r requirements.txt

# 运行测试(会自动使用 pytest.ini 中的配置)
pytest

查看日志

# Windows
type logstest_results.log

# Linux/Mac
cat logs/test_results.log

生成的日志文件示例

2024-01-15 10:30:15,123 - INFO - ============================= test session starts ==============================
2024-01-15 10:30:15,124 - INFO - platform win32 -- Python 3.9.0, pytest-7.0.0
2024-01-15 10:30:15,125 - INFO - rootdir: C:UsersAdministratorDesktoppython自动化测试myproject
2024-01-15 10:30:15,126 - INFO - collected 6 items
2024-01-15 10:30:15,200 - PASS - tests/test_calculator.py::test_add - 0.001s
2024-01-15 10:30:15,201 - PASS - tests/test_calculator.py::test_subtract - 0.001s
2024-01-15 10:30:15,202 - PASS - tests/test_calculator.py::test_multiply - 0.001s
2024-01-15 10:30:15,203 - PASS - tests/test_calculator.py::test_divide - 0.001s
2024-01-15 10:30:15,204 - PASS - tests/test_calculator.py::test_divide_by_zero - 0.001s
2024-01-15 10:30:15,205 - PASS - tests/test_utils.py::test_format_number - 0.001s
2024-01-15 10:30:15,206 - PASS - tests/test_utils.py::test_validate_positive - 0.001s
2024-01-15 10:30:15,207 - PASS - tests/test_utils.py::test_validate_positive_fail - 0.001s
2024-01-15 10:30:15,208 - INFO - ============================= 8 passed in 0.05s ==============================

11. 总结

11.1 核心要点

  1. pytest-result-log 的作用:记录和管理 pytest 测试结果
  2. 主要功能:保存测试执行信息、支持多种格式、灵活配置
  3. 适用场景:CI/CD、历史追踪、结果分析、团队协作
  4. 最佳实践:合理命名、组织文件、定期清理、性能优化

11.2 常用命令速查

# 基本使用
pytest --result-log=results.log

# JSON 格式
pytest --result-log=results.json --result-log-format=json

# 只记录失败
pytest --result-log=results.log --result-log-level=failures

# 追加模式
pytest --result-log=results.log --result-log-append

# 完整配置
pytest 
  --result-log=results.log 
  --result-log-format=text 
  --result-log-level=all 
  -v

11.3 学习建议

  1. 从简单开始:先使用基本命令,熟悉后再使用高级功能
  2. 结合实际项目:在自己的项目中实践,加深理解
  3. 阅读文档:遇到问题时,查阅官方文档
  4. 参考示例:参考本文档中的示例代码
  5. 持续改进:根据项目需求,不断优化配置

11.4 扩展阅读


12. 附录

12.1 命令行参数完整列表

参数 说明 示例
--result-log 指定日志文件路径 --result-log=results.log
--result-log-format 指定日志格式 --result-log-format=json
--result-log-level 指定日志级别 --result-log-level=failures
--result-log-append 追加模式 --result-log-append
--result-log-encoding 指定编码 --result-log-encoding=utf-8

12.2 支持的日志格式

格式 说明 适用场景
text 文本格式(默认) 人类阅读
json JSON 格式 程序处理
xml XML 格式 CI/CD 集成

12.3 支持的日志级别

级别 说明 记录内容
all 记录所有信息 所有测试的详细信息
failures 只记录失败 只记录失败的测试
summary 只记录摘要 只记录测试摘要

12.4 常见错误码

错误码 说明 解决方法
PermissionError 权限错误 检查文件权限
FileNotFoundError 目录不存在 创建目录
UnicodeEncodeError 编码错误 指定 UTF-8 编码

文档结束

希望这份详细的文档能够帮助你更好地理解和使用 pytest-result-log 插件。如果你在使用过程中遇到任何问题,建议查阅官方文档或寻求社区帮助。

发表评论