Pytest 用例发现规则详解
1. 什么是用例发现
1.1 基本概念
用例发现(Test Discovery) 是 pytest 自动查找和识别测试用例的过程。当你运行 pytest 命令时,pytest 会自动扫描指定的目录,找到所有符合规则的测试文件,然后在这些文件中查找所有符合规则的测试函数和测试类。
1.2 为什么需要用例发现
- 自动化:不需要手动指定每个测试文件
- 高效:一次命令运行所有测试
- 规范:统一的命名规则让测试更易管理
- 灵活:可以运行整个项目或指定目录的测试
1.3 发现流程
运行 pytest 命令
↓
扫描指定目录(默认当前目录)
↓
查找符合命名规则的文件
↓
在文件中查找符合命名规则的类和函数
↓
收集所有测试用例
↓
执行测试用例
2. Pytest 发现机制原理
2.1 默认发现规则
Pytest 使用以下默认规则来发现测试用例:
- 文件规则:文件名必须以
test_开头或以_test结尾 - 函数规则:函数名必须以
test_开头 - 类规则:类名必须以
Test开头(不能有__init__方法) - 目录规则:目录名不能是 Python 保留字,不能以
.开头
2.2 发现顺序
Pytest 按照以下顺序发现测试:
- 首先扫描当前目录(或指定目录)
- 递归扫描子目录
- 按照文件名的字母顺序处理文件
- 在文件中按照代码顺序处理类和函数
2.3 示例:基本发现过程
假设你有以下目录结构:
project/
├── test_login.py
├── test_user.py
└── api/
└── test_api.py
运行 pytest 时:
- 发现
test_login.py✓ - 发现
test_user.py✓ - 发现
api/test_api.py✓ - 在每个文件中查找
test_开头的函数 - 收集所有测试用例并执行
3. 文件命名规则
3.1 规则说明
Pytest 会识别以下两种命名格式的测试文件:
规则 1:以 test_ 开头
- ✅
test_login.py - ✅
test_user_management.py - ✅
test_api_v1.py - ❌
login_test.py(不符合此规则) - ❌
test.py(不符合,必须是test_开头)
规则 2:以 _test 结尾
- ✅
login_test.py - ✅
user_management_test.py - ✅
api_v1_test.py - ❌
testlogin.py(不符合此规则) - ❌
_test.py(不符合,必须有前缀)
3.2 详细示例
示例 1:正确的文件命名
# 文件名:test_login.py
def test_user_login():
assert 1 + 1 == 2
def test_admin_login():
assert 2 + 2 == 4
运行结果:
$ pytest test_login.py
# 会找到 2 个测试用例
示例 2:另一种正确的命名方式
# 文件名:login_test.py
def test_user_login():
assert 1 + 1 == 2
运行结果:
$ pytest login_test.py
# 会找到 1 个测试用例
示例 3:错误的文件命名
# 文件名:login.py(不符合规则)
def test_user_login():
assert 1 + 1 == 2
运行结果:
$ pytest login.py
# 不会发现任何测试用例,除非使用 pytest login.py::test_user_login 明确指定
3.3 文件命名最佳实践
推荐方式:使用 test_ 前缀
# ✅ 推荐
test_login.py
test_user_management.py
test_api_v1.py
test_integration.py
# ⚠️ 可用但不推荐
login_test.py
user_management_test.py
原因:
test_前缀更直观,一眼就能看出是测试文件- 在文件浏览器中,测试文件会聚集在一起(按字母排序)
- 符合大多数 Python 项目的约定
4. 测试函数命名规则
4.1 基本规则
函数名必须以 test_ 开头
这是 pytest 识别测试函数的核心规则。
4.2 正确示例
# test_example.py
# ✅ 正确:以 test_ 开头
def test_login():
assert True
def test_user_registration():
assert True
def test_api_get_user():
assert True
def test_计算器加法(): # 支持中文
assert 1 + 1 == 2
def test_login_with_valid_credentials():
assert True
def test_123(): # 数字也可以
assert True
4.3 错误示例
# test_example.py
# ❌ 错误:不以 test_ 开头
def login():
assert True # pytest 不会发现这个函数
def check_user():
assert True # pytest 不会发现这个函数
# ⚠️ 注意:testlogin() 在某些 pytest 版本中可能被发现
# 但这不是标准规则,不推荐使用
def testlogin(): # 缺少下划线,不符合标准规则
assert True # 某些版本的 pytest 可能会发现,但不推荐
# ❌ 错误:在类中但类名不符合规则
class UserTest: # 类名应该是 TestUser
def test_login(self):
assert True # 不会被执行
重要说明:关于 testlogin() 的发现规则
虽然 pytest 的官方标准规则要求函数名必须以 test_ 开头(带下划线),但在实际使用中:
-
某些 pytest 版本或配置可能会发现
test开头的函数(不带下划线)- 这可能是因为 pytest 使用了更宽松的匹配规则
- 或者项目中有自定义的
pytest.ini配置
-
不推荐使用
testlogin()这种命名方式,因为:- 不符合官方规范(官方要求
test_开头) - 可能在不同版本或配置下行为不一致
- 可读性较差,容易与
test_login()混淆 - 可能导致团队协作时的困惑
- 不符合官方规范(官方要求
-
如何验证你的环境:
# 查看 pytest 的配置 pytest --collect-only # 查看 pytest 版本 pytest --version # 查看 pytest.ini 配置(如果有) cat pytest.ini
推荐做法:始终使用 test_ 开头(带下划线),这是最可靠和标准的做法。
实际测试示例:
# test_demo.py
def test_user_login(): # ✅ 标准写法,肯定会被发现
assert 1 + 1 == 2
def testlogin(): # ⚠️ 非标准写法,可能被发现,但不推荐
assert 2 + 2 == 4
运行 pytest 后,如果两个函数都被发现,说明你的环境支持更宽松的规则,但为了代码的可移植性和规范性,建议统一使用 test_ 开头的命名方式。
4.4 函数命名最佳实践
实践 1:描述性命名
# ✅ 好的命名:清晰描述测试内容
def test_user_login_with_valid_credentials():
"""测试用户使用有效凭据登录"""
pass
def test_user_login_with_invalid_password():
"""测试用户使用无效密码登录"""
pass
def test_user_login_with_empty_username():
"""测试用户名为空时的登录"""
pass
# ❌ 不好的命名:不够描述性
def test_login1():
pass
def test_login2():
pass
def test_1():
pass
实践 2:使用下划线分隔
# ✅ 推荐:使用下划线
def test_user_can_login():
pass
def test_admin_can_delete_user():
pass
# ⚠️ 可用但不推荐:驼峰命名
def testUserCanLogin():
pass # 虽然能运行,但不符合 Python 命名规范
实践 3:组织相关测试
# test_user.py
def test_user_login_success():
"""测试登录成功"""
pass
def test_user_login_failure():
"""测试登录失败"""
pass
def test_user_logout():
"""测试登出"""
pass
def test_user_profile_update():
"""测试更新用户资料"""
pass
4.5 参数化测试函数
参数化测试函数也需要以 test_ 开头:
import pytest
# ✅ 正确:参数化函数也要以 test_ 开头
@pytest.mark.parametrize("username,password", [
("admin", "123456"),
("user", "password"),
])
def test_login_with_different_users(username, password):
assert username is not None
assert password is not None
5. 测试类命名规则
5.1 基本规则
类名必须以 Test 开头,且类中不能有 __init__ 方法
重要说明:
- 如果测试类中有
__init__方法,pytest 无法收集该测试类 - 运行 pytest 时会显示
PytestCollectionWarning警告 - 最终会收集到 0 个测试项
- 需要使用
setup_method、setup_class或fixture来替代__init__进行初始化
5.2 正确示例
# test_example.py
# ✅ 正确:类名以 Test 开头
class TestLogin:
def test_user_login(self):
assert True
def test_admin_login(self):
assert True
# ✅ 正确:Test 后面可以有其他字符
class TestUserManagement:
def test_create_user(self):
assert True
def test_delete_user(self):
assert True
# ✅ 正确:可以有多个测试类
class TestAPI:
def test_get_request(self):
assert True
class TestDatabase:
def test_connection(self):
assert True
5.3 错误示例
# test_example.py
# ❌ 错误:类名不以 Test 开头
class LoginTest: # 应该是 TestLogin
def test_user_login(self):
assert True # pytest 不会发现这个测试
# ❌ 错误:类名是 test(小写)
class testLogin: # 应该是 TestLogin
def test_user_login(self):
assert True # pytest 不会发现这个测试
# ❌ 错误:类中有 __init__ 方法
class TestLogin:
def __init__(self): # 不能有 __init__
self.username = "admin"
def test_user_login(self):
assert True # pytest 不会发现这个测试
# 运行 pytest 时会显示警告:
# PytestCollectionWarning: cannot collect test class 'TestLogin'
# because it has a __init__ constructor
# collected 0 items
# ❌ 错误:类名不以 Test 开头,即使函数名正确
class UserTest:
def test_login(self):
assert True # pytest 不会发现这个测试
5.4 类中方法的命名规则
重要:类中的测试方法也必须以 test_ 开头!
class TestLogin:
# ✅ 正确:方法名以 test_ 开头
def test_user_login(self):
assert True
# ✅ 正确:可以有多个测试方法
def test_admin_login(self):
assert True
# ❌ 错误:方法名不以 test_ 开头
def user_login(self):
assert True # pytest 不会发现这个方法
5.5 类的继承
测试类可以继承其他类,但必须遵循命名规则:
# ✅ 正确:继承的基类可以是普通类
class BaseTest:
def setup(self):
pass
class TestLogin(BaseTest):
def test_user_login(self):
assert True
# ✅ 正确:可以继承其他测试类
class TestBase:
def setup(self):
pass
class TestLogin(TestBase):
def test_user_login(self):
assert True
5.6 类命名最佳实践
实践 1:类名应该描述测试范围
# ✅ 好的命名
class TestUserLogin:
"""测试用户登录相关功能"""
def test_login_success(self):
pass
def test_login_failure(self):
pass
class TestUserRegistration:
"""测试用户注册相关功能"""
def test_register_success(self):
pass
# ❌ 不好的命名
class Test1:
def test_login(self):
pass
class TestA:
def test_register(self):
pass
实践 2:一个类组织相关的测试
# test_user.py
class TestUserLogin:
"""所有登录相关的测试放在一个类中"""
def test_login_with_valid_credentials(self):
pass
def test_login_with_invalid_password(self):
pass
def test_login_with_empty_username(self):
pass
class TestUserProfile:
"""所有用户资料相关的测试放在一个类中"""
def test_update_profile(self):
pass
def test_get_profile(self):
pass
6. 目录结构规则
6.1 基本规则
- 目录名不能是 Python 保留字
- 目录名不能以
.开头(隐藏目录) - 目录中的文件仍需遵循文件命名规则
6.2 目录结构示例
示例 1:扁平结构
project/
├── test_login.py
├── test_user.py
├── test_api.py
└── conftest.py
示例 2:按模块组织
project/
├── tests/
│ ├── test_login.py
│ ├── test_user.py
│ └── api/
│ ├── test_user_api.py
│ └── test_order_api.py
└── conftest.py
示例 3:按功能组织
project/
├── tests/
│ ├── unit/
│ │ ├── test_user_model.py
│ │ └── test_order_model.py
│ ├── integration/
│ │ ├── test_user_flow.py
│ │ └── test_order_flow.py
│ └── api/
│ ├── test_user_api.py
│ └── test_order_api.py
└── conftest.py
6.3 目录中的文件命名
重要:无论文件在哪个目录,都必须遵循文件命名规则!
# ✅ 正确:tests/api/test_user_api.py
def test_get_user():
assert True
# ✅ 正确:tests/unit/test_user_model.py
def test_user_creation():
assert True
# ❌ 错误:tests/api/user_api.py(不符合命名规则)
def test_get_user():
assert True # pytest 不会发现这个文件
6.4 conftest.py 文件
conftest.py 是一个特殊的文件,用于存放 pytest 的配置和共享的 fixture。
规则:
- 文件名必须是
conftest.py(固定名称) - 可以放在任何目录中
- 该目录及其子目录的测试都可以使用其中的 fixture
示例:
project/
├── conftest.py # 项目级别的 fixture
├── tests/
│ ├── conftest.py # tests 目录级别的 fixture
│ ├── test_login.py
│ └── api/
│ ├── conftest.py # api 目录级别的 fixture
│ └── test_api.py
6.5 运行指定目录的测试
# 运行 tests 目录下的所有测试
pytest tests/
# 运行 tests/api 目录下的所有测试
pytest tests/api/
# 运行 tests/unit 目录下的所有测试
pytest tests/unit/
7. 配置文件 pytest.ini
7.1 什么是 pytest.ini
pytest.ini 是 pytest 的配置文件,可以自定义测试发现规则和其他配置。
重要格式要求:
pytest.ini文件必须以[pytest]节头开始- 所有配置项都必须在
[pytest]节下 - 如果没有
[pytest]节头,会报错:ERROR: no section header defined
7.2 基本配置示例
✅ 正确格式:
[pytest]
# 指定测试文件匹配模式
python_files = test_*.py *_test.py
# 指定测试类匹配模式
python_classes = Test*
# 指定测试函数匹配模式
python_functions = test_*
# 指定测试目录
testpaths = tests
# 其他配置
addopts = -v --tb=short
7.3 自定义文件命名规则
示例 1:只接受 test_ 开头的文件
[pytest]
python_files = test_*.py
效果:
- ✅
test_login.py– 会被发现 - ❌
login_test.py– 不会被发现
示例 2:自定义文件命名模式
[pytest]
python_files = test_*.py check_*.py
效果:
- ✅
test_login.py– 会被发现 - ✅
check_login.py– 会被发现 - ❌
login_test.py– 不会被发现
7.4 自定义类命名规则
[pytest]
python_classes = Test* Check*
效果:
# ✅ 会被发现
class TestLogin:
def test_login(self):
pass
class CheckUser:
def test_user(self):
pass
# ❌ 不会被发现
class LoginTest:
def test_login(self):
pass
7.5 自定义函数命名规则
⚠️ 重要提醒:配置项必须在 [pytest] 节头下,否则会报错!
错误示例(会导致报错):
# ❌ 错误:缺少 [pytest] 节头
python_functions = test_* login_*
# 运行 pytest 时会报错:
# ERROR: pytest.ini:1: no section header defined
正确示例:
# ✅ 正确:必须有 [pytest] 节头
[pytest]
python_functions = test_* login_*
效果:
# ✅ 会被发现
def test_login():
pass
def login_user():
pass
# ❌ 不会被发现
def check_user():
pass
更多示例:
[pytest]
# 允许 test_ 和 check_ 开头的函数
python_functions = test_* check_*
效果:
# ✅ 会被发现
def test_login():
pass
def check_user():
pass
# ❌ 不会被发现
def login_test():
pass
7.6 指定测试目录
[pytest]
testpaths = tests unit_tests
效果:pytest 会优先在这些目录中查找测试文件。
7.7 完整配置示例
[pytest]
# 测试文件匹配模式
python_files = test_*.py
# 测试类匹配模式
python_classes = Test*
# 测试函数匹配模式
python_functions = test_*
# 测试目录
testpaths = tests
# 命令行选项
addopts =
-v
--tb=short
--strict-markers
--disable-warnings
# 标记定义
markers =
slow: 标记为慢速测试
integration: 标记为集成测试
smoke: 标记为冒烟测试
8. 实际示例演示
8.1 示例 1:基本测试文件
创建文件 test_basic.py:
# test_basic.py
def test_addition():
"""测试加法"""
assert 1 + 1 == 2
def test_subtraction():
"""测试减法"""
assert 5 - 3 == 2
def test_multiplication():
"""测试乘法"""
assert 2 * 3 == 6
运行:
$ pytest test_basic.py
输出:
test_basic.py::test_addition PASSED
test_basic.py::test_subtraction PASSED
test_basic.py::test_multiplication PASSED
发现结果:pytest 发现了 3 个测试用例。
8.2 示例 2:测试类
创建文件 test_user.py:
# test_user.py
class TestUser:
def test_create_user(self):
"""测试创建用户"""
assert True
def test_delete_user(self):
"""测试删除用户"""
assert True
class TestUserLogin:
def test_login_success(self):
"""测试登录成功"""
assert True
def test_login_failure(self):
"""测试登录失败"""
assert True
运行:
$ pytest test_user.py -v
输出:
test_user.py::TestUser::test_create_user PASSED
test_user.py::TestUser::test_delete_user PASSED
test_user.py::TestUserLogin::test_login_success PASSED
test_user.py::TestUserLogin::test_login_failure PASSED
发现结果:pytest 发现了 2 个测试类,共 4 个测试用例。
8.3 示例 3:混合情况
创建文件 test_mixed.py:
# test_mixed.py
# 独立的测试函数
def test_function1():
assert True
def test_function2():
assert True
# 测试类
class TestClass1:
def test_method1(self):
assert True
def test_method2(self):
assert True
# 普通函数(不会被发现)
def helper_function():
return "helper"
# 普通类(不会被发现)
class HelperClass:
def helper_method(self):
return "helper"
# 另一个测试类
class TestClass2:
def test_method3(self):
assert True
运行:
$ pytest test_mixed.py -v
输出:
test_mixed.py::test_function1 PASSED
test_mixed.py::test_function2 PASSED
test_mixed.py::TestClass1::test_method1 PASSED
test_mixed.py::TestClass1::test_method2 PASSED
test_mixed.py::TestClass2::test_method3 PASSED
发现结果:
- ✅ 发现了 2 个独立测试函数
- ✅ 发现了 2 个测试类中的 3 个测试方法
- ❌ 忽略了
helper_function和HelperClass(不符合命名规则)
8.4 示例 4:目录结构
创建以下目录结构:
project/
├── test_root.py
├── tests/
│ ├── test_module1.py
│ ├── test_module2.py
│ └── subdirectory/
│ └── test_sub.py
文件内容:
# test_root.py
def test_root():
assert True
# tests/test_module1.py
def test_module1():
assert True
# tests/test_module2.py
def test_module2():
assert True
# tests/subdirectory/test_sub.py
def test_sub():
assert True
运行:
$ pytest
输出:
test_root.py::test_root PASSED
tests/test_module1.py::test_module1 PASSED
tests/test_module2.py::test_module2 PASSED
tests/subdirectory/test_sub.py::test_sub PASSED
发现结果:pytest 递归扫描了所有目录,发现了 4 个测试用例。
8.5 示例 5:不符合规则的示例
创建文件 wrong_naming.py:
# wrong_naming.py(文件名不符合规则)
def test_function():
"""这个函数符合规则,但文件不符合"""
assert True
class TestClass:
def test_method(self):
"""这个方法符合规则,但文件不符合"""
assert True
def login_test():
"""函数名不符合规则"""
assert True
class LoginTest:
"""类名不符合规则"""
def test_method(self):
assert True
运行:
$ pytest wrong_naming.py
输出:
# 不会发现任何测试用例,因为文件名不符合规则
解决方法 1:重命名文件
$ mv wrong_naming.py test_wrong_naming.py
$ pytest test_wrong_naming.py
解决方法 2:明确指定测试
$ pytest wrong_naming.py::test_function
$ pytest wrong_naming.py::TestClass::test_method
8.6 示例 6:使用 pytest.ini 自定义规则
创建 pytest.ini:
[pytest]
python_files = test_*.py check_*.py
python_classes = Test* Check*
python_functions = test_* check_*
创建文件 check_custom.py:
# check_custom.py(使用自定义规则)
def check_function():
"""使用自定义函数命名规则"""
assert True
class CheckClass:
def check_method(self):
"""使用自定义类和方法命名规则"""
assert True
运行:
$ pytest check_custom.py -v
输出:
check_custom.py::check_function PASSED
check_custom.py::CheckClass::check_method PASSED
发现结果:由于配置了自定义规则,pytest 发现了这些测试。
9. 常见问题与解决方案
9.1 问题 1:pytest 找不到我的测试用例
症状:
$ pytest
# 输出:no tests ran
可能原因和解决方案:
原因 1:文件名不符合规则
# ❌ 错误:login.py
def test_login():
assert True
解决:重命名文件
# ✅ 正确:test_login.py
def test_login():
assert True
原因 2:函数名不符合规则
# test_login.py
# ❌ 错误
def login():
assert True
解决:重命名函数
# ✅ 正确
def test_login():
assert True
原因 3:类名不符合规则
# test_login.py
# ❌ 错误
class LoginTest:
def test_login(self):
assert True
解决:重命名类
# ✅ 正确
class TestLogin:
def test_login(self):
assert True
9.2 问题 2:类中的测试方法没有被发现
症状:
class TestLogin:
def login(self): # 方法名不以 test_ 开头
assert True
解决:
class TestLogin:
def test_login(self): # 方法名必须以 test_ 开头
assert True
9.3 问题 3:类中有 init 方法导致测试不被发现
症状:
class TestLogin:
def __init__(self):
self.username = "admin"
def test_login(self):
assert True # 这个测试不会被发现
运行 pytest 时的表现:
$ pytest
collected 0 items
=== 1 warning in 0.01s ===
PytestCollectionWarning: cannot collect test class 'TestLogin' because it has a __init__ constructor
原因说明:
- pytest 无法收集带有
__init__方法的测试类 - 会显示
PytestCollectionWarning警告 - 最终收集到 0 个测试项
解决:使用 fixture 或 setup_method
方法 1:使用 fixture(推荐)
import pytest
class TestLogin:
@pytest.fixture(autouse=True)
def setup(self):
"""自动执行的 fixture,用于初始化"""
self.username = "admin"
yield # 可选:清理代码可以放在 yield 之后
# 清理代码(如果有)
def test_login(self):
assert self.username == "admin"
方法 2:使用 setup_method(简单场景)
class TestLogin:
def setup_method(self):
"""pytest 会在每个测试方法执行前自动调用这个方法"""
self.username = "admin"
def test_login(self):
assert self.username == "admin"
方法 3:使用 setup_class(类级别初始化)
class TestLogin:
@classmethod
def setup_class(cls):
"""pytest 会在类中所有测试执行前调用一次"""
cls.shared_data = "admin"
def test_login(self):
assert self.shared_data == "admin"
对比说明:
__init__:❌ pytest 无法收集测试类setup_method:✅ 每个测试方法执行前调用setup_class:✅ 整个类执行前调用一次fixture:✅ 最灵活,可以控制作用域和自动执行
9.4 问题 4:子目录中的测试没有被发现
症状:
project/
├── tests/
│ └── api/
│ └── test_api.py # 这个文件没有被发现
可能原因:目录中有 __init__.py 文件,但 pytest 配置不正确。
解决:确保 pytest 能扫描到子目录
# 明确指定目录
pytest tests/
# 或者配置 pytest.ini
[pytest]
testpaths = tests
9.5 问题 5:如何查看 pytest 发现了哪些测试
方法 1:使用 --collect-only 参数
$ pytest --collect-only
输出示例:
<Module test_login.py>
<Function test_user_login>
<Function test_admin_login>
<Module test_user.py>
<Class TestUser>
<Function test_create_user>
<Function test_delete_user>
方法 2:使用 -v 参数查看详细信息
$ pytest -v
9.6 问题 6:如何只运行特定的测试
运行单个文件:
$ pytest test_login.py
运行单个测试函数:
$ pytest test_login.py::test_user_login
运行单个测试类:
$ pytest test_user.py::TestUser
运行类中的单个方法:
$ pytest test_user.py::TestUser::test_create_user
使用模式匹配:
# 运行所有包含 "login" 的测试
$ pytest -k login
# 运行所有包含 "user" 但不包含 "admin" 的测试
$ pytest -k "user and not admin"
9.7 问题 7:pytest.ini 配置不生效或报错
错误 1:缺少 [pytest] 节头
症状:
$ pytest
ERROR: D:pythonpytest.ini:1: no section header defined
原因:pytest.ini 文件缺少必需的 [pytest] 节头。
错误示例:
# ❌ 错误:缺少 [pytest] 节头
python_functions = test_* login_*
解决方法:
# ✅ 正确:必须有 [pytest] 节头
[pytest]
python_functions = test_* login_*
错误 2:配置不生效
可能原因:
- 文件位置不对(应该在项目根目录)
- 文件格式错误(缺少
[pytest]节头) - 配置项拼写错误
- 配置项缩进错误(不应该有缩进)
检查方法:
# 查看 pytest 的配置
$ pytest --collect-only -v
# 查看 pytest 版本
$ pytest --version
正确的位置和格式:
project/
├── pytest.ini # 应该在项目根目录
├── tests/
│ └── test_example.py
正确的格式:
[pytest] # 必须有这个节头
python_files = test_*.py
python_classes = Test*
python_functions = test_*
常见格式错误:
# ❌ 错误1:缺少节头
python_functions = test_*
# ❌ 错误2:节头拼写错误
[pytest] # 正确
[pyest] # 错误
# ❌ 错误3:配置项有缩进
[pytest]
python_functions = test_* # 错误:不应该有缩进
# ✅ 正确:配置项没有缩进
[pytest]
python_functions = test_* # 正确
10. 最佳实践建议
10.1 文件命名建议
-
统一使用
test_前缀# ✅ 推荐 test_login.py test_user_management.py test_api_v1.py -
文件名应该描述测试内容
# ✅ 好的命名 test_user_login.py test_order_creation.py test_api_authentication.py # ❌ 不好的命名 test1.py test_a.py test.py -
使用下划线分隔单词
# ✅ 推荐 test_user_login.py test_api_v1.py # ⚠️ 不推荐 testUserLogin.py test-api-v1.py
10.2 函数命名建议
-
使用描述性的名称
# ✅ 好的命名 def test_user_login_with_valid_credentials(): pass def test_user_login_with_invalid_password(): pass # ❌ 不好的命名 def test_login1(): pass def test_login2(): pass -
遵循测试命名模式:test_
# 模式:test_<对象>_<场景>_<结果> def test_user_login_with_valid_credentials_should_succeed(): pass def test_user_login_with_invalid_password_should_fail(): pass -
使用下划线,不要使用驼峰命名
# ✅ 推荐 def test_user_can_login(): pass # ⚠️ 不推荐 def testUserCanLogin(): pass
10.3 类命名建议
-
类名应该描述测试范围
# ✅ 好的命名 class TestUserLogin: pass class TestOrderManagement: pass # ❌ 不好的命名 class Test1: pass class TestA: pass -
一个类组织相关的测试
class TestUserLogin: """所有登录相关的测试""" def test_login_success(self): pass def test_login_failure(self): pass def test_login_with_empty_username(self): pass
10.4 目录结构建议
-
按功能模块组织
tests/ ├── test_user.py ├── test_order.py ├── test_payment.py └── api/ ├── test_user_api.py └── test_order_api.py -
按测试类型组织
tests/ ├── unit/ │ ├── test_user_model.py │ └── test_order_model.py ├── integration/ │ ├── test_user_flow.py │ └── test_order_flow.py └── api/ ├── test_user_api.py └── test_order_api.py -
使用 conftest.py 共享 fixture
project/ ├── conftest.py # 项目级别的 fixture ├── tests/ │ ├── conftest.py # tests 目录的 fixture │ └── api/ │ ├── conftest.py # api 目录的 fixture │ └── test_api.py
10.5 配置文件建议
-
创建 pytest.ini 统一配置
[pytest] python_files = test_*.py python_classes = Test* python_functions = test_* testpaths = tests addopts = -v --tb=short -
定义测试标记
[pytest] markers = slow: 标记为慢速测试 integration: 标记为集成测试 smoke: 标记为冒烟测试 api: 标记为 API 测试
10.6 验证测试发现
定期运行以下命令验证测试发现是否正常:
# 查看所有发现的测试
pytest --collect-only
# 查看详细信息
pytest --collect-only -v
# 运行所有测试
pytest
# 运行并显示详细信息
pytest -v
10.7 总结检查清单
在创建测试时,检查以下事项:
- [ ] 文件名以
test_开头或以_test结尾 - [ ] 测试函数名以
test_开头 - [ ] 测试类名以
Test开头 - [ ] 测试类中没有
__init__方法 - [ ] 类中的测试方法名以
test_开头 - [ ] 文件、函数、类名使用描述性名称
- [ ] 使用下划线分隔单词
- [ ] 目录结构清晰合理
- [ ] 有 pytest.ini 配置文件(如需要)
附录:快速参考表
文件命名规则
| 格式 | 示例 | 是否会被发现 |
|---|---|---|
test_*.py |
test_login.py |
✅ 是 |
*_test.py |
login_test.py |
✅ 是 |
*.py |
login.py |
❌ 否 |
函数命名规则
| 格式 | 示例 | 是否会被发现 | 说明 |
|---|---|---|---|
test_* |
test_login() |
✅ 是 | 标准规则,推荐使用 |
* |
login() |
❌ 否 | 不符合规则 |
test* |
testlogin() |
⚠️ 可能 | 某些版本可能发现,但不推荐,应使用 test_* |
类命名规则
| 格式 | 示例 | 是否会被发现 |
|---|---|---|
Test* |
TestLogin |
✅ 是 |
*Test |
LoginTest |
❌ 否 |
test* |
testLogin |
❌ 否 |
类方法命名规则
| 格式 | 示例 | 是否会被发现 |
|---|---|---|
test_* |
test_login() |
✅ 是 |
* |
login() |
❌ 否 |
特殊文件
| 文件名 | 作用 | 是否会被发现 |
|---|---|---|
conftest.py |
存放 fixture | ❌ 否(特殊文件) |
pytest.ini |
配置文件 | ❌ 否(配置文件) |
结语
通过本文档,你应该已经全面了解了 pytest 的用例发现规则。记住核心规则:
- 文件:
test_*.py或*_test.py - 函数:
test_* - 类:
Test*(不能有__init__) - 类方法:
test_*
遵循这些规则,pytest 就能自动发现并运行你的测试用例。如果遇到问题,使用 pytest --collect-only 命令查看 pytest 发现了哪些测试,这将帮助你快速定位问题。