Files
PlexPlaylistSync/TESTING.md
T
2025-11-25 20:55:09 +09:00

5.6 KiB

测试框架使用指南

概述

项目已配置 pytest 测试框架,相比你之前的手动测试方法,有以下优势:

改进点

旧方法 (Jupyter Notebook) 新方法 (pytest)
手动运行每个测试用例 自动发现和运行所有测试
手动读取和对比文件 自动化断言和差异报告
难以定位失败原因 清晰的错误堆栈和 diff
无法批量运行 支持参数化和并行测试
无覆盖率统计 自动生成覆盖率报告

快速开始

1. 运行所有测试

python -m pytest

2. 运行特定测试文件

python -m pytest tests/test_playlist_merge.py -v

3. 在 VS Code 中运行

  • Ctrl+Shift+P
  • 输入 "Run Task"
  • 选择 "运行所有测试" 或其他测试任务

测试文件说明

已创建的测试文件

tests/
├── __init__.py              # 包初始化
├── conftest.py              # pytest fixtures 和配置
├── test_playlist_merge.py   # 播放列表合并测试
└── README.md                # 详细测试指南

测试用例覆盖

test_playlist_merge.py 包含:

  1. 基础功能测试 (TestPlaylistMergeBasics)

    • test_load_paths_removes_comments - 测试路径加载
    • test_save_paths_adds_header - 测试文件保存
    • test_empty_playlist_merge - 测试空播放列表合并
  2. 本地优先测试 (TestPlaylistMergeLocalPriority)

    • 参数化测试 case1-4 的本地优先合并
  3. 远程优先测试 (TestPlaylistMergeRemotePriority)

    • 参数化测试 case1-4 的远程优先合并
  4. 冲突检测测试 (TestConflictDetection)

    • 测试冲突识别和解决

常用命令

基础运行

# 运行所有测试
pytest

# 详细输出
pytest -v

# 显示 print 输出
pytest -s

# 只运行特定测试
pytest tests/test_playlist_merge.py::TestPlaylistMergeBasics::test_load_paths_removes_comments

测试筛选

# 运行特定 case
pytest -k "case_num-1"

# 运行包含特定关键字的测试
pytest -k "local_priority"

# 运行标记的测试
pytest -m unit

调试和重试

# 失败时进入调试器
pytest --pdb

# 只运行上次失败的测试
pytest --lf

# 先运行失败的测试
pytest --ff

# 遇到第一个失败就停止
pytest -x

覆盖率报告

# 生成覆盖率报告
pytest --cov=app --cov-report=html

# 在浏览器中查看
# 打开 htmlcov/index.html

# 终端中显示覆盖率
pytest --cov=app --cov-report=term

编写新测试

1. 创建测试文件

tests/ 目录下创建 test_<feature>.py

2. 编写测试类和函数

import pytest

class TestMyFeature:
    """测试我的功能"""
    
    def test_basic_case(self):
        """测试基本用例"""
        result = my_function()
        assert result == expected_value
    
    @pytest.mark.parametrize("input,expected", [
        (1, 2),
        (2, 4),
        (3, 6),
    ])
    def test_multiple_cases(self, input, expected):
        """参数化测试多个用例"""
        assert my_function(input) == expected

3. 使用 fixtures

@pytest.fixture
def sample_data():
    """提供测试数据"""
    return {"key": "value"}

def test_with_fixture(sample_data):
    assert sample_data["key"] == "value"

与旧测试方法的对比

旧方法 (test.ipynb)

# 手动循环和对比
for i in range(len(test_playlists[1])):
    with open(test_playlists[1][i], 'r', encoding='utf-8') as f1, \
         open(target_playlists[i], 'r', encoding='utf-8') as f2:
        content1 = f1.read()
        content2 = f2.read()
        # 手动处理和对比...
        if content1 == content2:
            print(f"Test case {i+1} passed.")
        else:
            print(f"Test case {i+1} failed.")

新方法 (pytest)

@pytest.mark.parametrize("case_num", [1, 2, 3, 4])
def test_merge_local_priority(case_num, tmp_path):
    """自动运行所有用例,失败时显示详细 diff"""
    # 加载输入
    base_text = load_file(f"case{case_num}.m3u")
    # ... 执行测试
    result = merge_playlists(base_text, local_text, remote_text)
    # 自动断言和差异报告
    assert_playlists_equal(actual_file, expected_file)

持续集成

可以将测试集成到 CI/CD 流程:

# .github/workflows/test.yml (示例)
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
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests
        run: pytest --cov=app

最佳实践

  1. 测试命名 - 使用清晰描述性的名称
  2. 独立性 - 每个测试应该独立运行
  3. 快速 - 单元测试应该快速执行
  4. 明确 - 一个测试只测一个功能点
  5. 维护 - 定期更新测试用例

故障排除

pytest 找不到

python -m pip install pytest pytest-cov

导入错误

确保项目根目录在 Python 路径中:

import sys
sys.path.insert(0, str(Path(__file__).parent.parent))

测试发现失败

检查 pytest.ini 配置和文件命名是否符合规范。

下一步

  • 为其他模块添加测试 (如 plex_client.py, local_playlist.py)
  • 添加集成测试
  • 配置 CI/CD 自动运行测试
  • 设置测试覆盖率目标 (如 >80%)

参考资料