1、编写测试用例

在Pycharm中新建test_sample.py文件如下:其中包含被测函数func和测试用例test_answer,使用assert断言测试预期结果和实际结果。

# content of test_sample.py
def func(x):
    return x + 1


def test_answer():
    assert func(3) == 5

测试用例编写规范:

  • 测试文件名必须以“test_”开头或者以”_test”结尾
  • 测试类命名以Test开头
  • 测试方法必须以“test_”开头
  • 测试用例包pakege必须要有__init__.py文件
  • 使用assert断言

2、运行测试用例

在Pycharm中使用pytest运行测试用例有两种方式,分别是命令行模式和主函数模式。

1、Terminal终端(命令行模式):

选中test_sample.py文件,鼠标右键Open in | Open in Terminal,打开在Terminal终端窗口,输入命令:pytest

pytest中用例级别 pytest指定用例_测试用例

pytest中用例级别 pytest指定用例_测试用例_02

运行结果如下: 

C:\Users\057776\PycharmProjects\pytest-demo\testcases>pytest
======================================================================= test session starts ========================================================================
platform win32 -- Python 3.8.8, pytest-7.3.1, pluggy-1.0.0 -- E:\Programs\Python\Python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '7.3.1', 'py': '1.10.0', 'pluggy': '1.0.0'}, 'Plugins': {'html': '3.1.1
', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'allure-pytest': '2.10.0', 'assume': '2.4.3', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jd
k1.8.0_271', 'foo': 'bar'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo
configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, allure-pytest-2.10.0, assume-2.4.3, requests-mock-1.7.0
collected 1 item                                                                                                                                                    

test_sample.py::test_answer FAILED

============================================================================= FAILURES =============================================================================
___________________________________________________________________________ test_answer ____________________________________________________________________________

    def test_answer():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_sample.py:18: AssertionError
===================================================================== short test summary info ======================================================================
FAILED test_sample.py::test_answer - assert 4 == 5
======================================================================== 1 failed in 0.56s =========================================================================

C:\Users\057776\PycharmProjects\pytest-demo\testcases>

观察可知,pytest运行测试用例输出的信息包括以下内容:

  • Platform(平台):显示正在运行测试的操作系统和Python版本信息。
  • Cachedir(缓存目录):显示pytest的缓存目录路径,pytest使用缓存来提高后续运行的效率。
  • Metadata(元数据):显示有关项目和环境的元数据信息,例如项目名称、作者、版本等。
  • Rootdir(根目录):显示pytest运行的根目录路径,即包含测试用例的目录。
  • Configfile(配置文件):显示pytest的配置文件路径,pytest可以使用配置文件来自定义测试行为。
  • Plugins(插件):显示已加载的pytest插件列表,插件可以扩展pytest的功能和行为。

 2、pytest.main方法(主函数模式):

pytest.main()方法可以作为我们自动化测试项目的测试用例执行入口。

修改上面的test_sample.py文件如下:

import pytest


# content of test_sample.py
def func(x):
    return x + 1

def test_answer():
    assert func(3) == 5

if __name__ == '__main__':
    pytest.main()

命令行窗口输出命令:python test_sample.py

C:\Users\057776\PycharmProjects\pytest-demo\testcases>python test_sample.py
======================================================================= test session starts ========================================================================
platform win32 -- Python 3.8.8, pytest-7.3.1, pluggy-1.0.0 -- E:\Programs\Python\Python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '7.3.1', 'py': '1.10.0', 'pluggy': '1.0.0'}, 'Plugins': {'html': '3.1.1
', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'allure-pytest': '2.10.0', 'assume': '2.4.3', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jd
k1.8.0_271', 'foo': 'bar'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo
configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, allure-pytest-2.10.0, assume-2.4.3, requests-mock-1.7.0
collected 1 item                                                                                                                                                    

test_sample.py::test_answer FAILED

============================================================================= FAILURES =============================================================================
___________________________________________________________________________ test_answer ____________________________________________________________________________

    def test_answer():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_sample.py:19: AssertionError
===================================================================== short test summary info ======================================================================
FAILED test_sample.py::test_answer - assert 4 == 5
======================================================================== 1 failed in 0.49s =========================================================================

C:\Users\057776\PycharmProjects\pytest-demo\testcases>

参数说明:

  • pytest.main() :无任何参数,会收集当前目录下所有的测试用例,相当于命令行模式下输入命令pytest。
  • pytest.main(['test_sample.py']) :一个参数,执行指定文件test_sample.py的所有测试用例。
  • pytest.main(['-q','test_sample.py']) :二个参数,参数-q表示安静模式, 不输出环境信息。

pytest.main()源码如下:

def main(
    args: Optional[Union[List[str], py.path.local]] = None,
    plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
) -> Union[int, ExitCode]:
    """Perform an in-process test run.

    :param args: List of command line arguments.
    :param plugins: List of plugin objects to be auto-registered during initialization.

    :returns: An exit code.
    """

pytest.main() 函数接受两个参数:

  •  args :命令行参数列表,和命令行模式运行时的参数相同,在列表 List 里以字符串 str 的形式,多参数以 “,” 隔开,也可以传入测试case的路径。
  •  plugins :为插件参数,初始化期间要自动注册的插件对象列表。

3、鼠标右键Run运行(测试运行器):

本地调试代码的时候,我们也可以通过配置默认的测试运行器,选择要运行的.py文件,鼠标右键Run运行。

File | Settings | Tools | Python Integrated Tools

pytest中用例级别 pytest指定用例_pytest运行方式_03

pytest中用例级别 pytest指定用例_pytest_04

pytest中用例级别 pytest指定用例_pytest_05

说明:

这种方式可以直接使用pytest来运行指定的文件,方便调试。

 3、常见的命令行参数

  • -h : 获取帮助信息
  • -v : 输出详细信息
  • -q : 输出简要信息
  • -s : 控制台输出错误信息
  • [-k EXPRESSION] : 仅运行与给定子字符串表达式匹配的测试,匹配不区分大小写。
  • [-m MARKEXPR] : 执行 mark 标记的测试用例
  • [-x, --exitfirst] : 遇到第一个 error 或 failed 的测试用例立即退出
  • [--lf, --last-failed] : 重跑上次失败的 tests,如果没有失败就重跑全部

详见:pytest合集(3)— 命令行参数

 4、测试用例收集规则

默认情况下,pytest会收集当前目录及其子目录下所有以test_*.py或*_test.py文件中,以"test_"开头的测试用例。

Python测试发现的约定是指测试框架(如pytest)遵循的规则和指南,用于自动发现和执行Python项目中的测试用例。这些约定有助于简化测试执行过程,并使组织和有效运行测试更加容易。

在pytest的上下文中,有特定的测试发现约定:

1. 测试函数命名:测试模块中的测试函数应以`test_`为前缀,以被识别为测试用例。pytest根据命名约定识别这些函数,并将它们作为测试套件的一部分执行。

2. 测试文件命名:pytest将与模式`test_*.py`或`*_test.py`匹配的文件视为测试模块。遵循这种命名约定,pytest可以自动发现并执行这些模块中定义的测试用例。

3. 测试类命名:如果你喜欢使用类来组织测试,pytest将以`Test*`或`*Test`的约定命名的类识别为测试类。这些类中的测试方法也应遵循上述测试函数命名约定。

4. 目录结构:pytest从当前目录及其子目录开始搜索测试模块。它递归地探索目录结构,发现测试文件并执行其中的测试用例。因此,将测试文件组织在一个良好定义的目录结构中可以帮助pytest更高效地定位和执行测试。

通过遵循这些约定,你可以利用pytest的测试发现功能,自动找到并执行测试用例,而无需显式指定每个测试用例。这在处理较大的代码库和测试套件时,可以实现更简化和高效的测试过程。

5、测试运行结果标记符

  • 点号,表示用例通过
  • F 表示失败 Failure
  • E 表示用例中存在异常 Error
  • S 表示用例被跳过 Skip
  • x 小写的 x 表示预期失败 xfail
  • X 大写的 X 表示预期失败,但是通过了

6、.pytest_cache缓存目录

pytest运行测试用例后,会在当前执行脚本路径下生成.pytest_cache文件夹,即pytest缓存目录如下:

pytest中用例级别 pytest指定用例_测试用例_06

目录文件说明:

  • lastfailed :上一次运行失败的测试用例。
  • nodeids :上一次运行的所有测试用例(无论测试用例的执行结果通过还是失败)。
  • stepwise :测试用例的路径。
  • .gitignore :pytest测试框架 .pytest_cache的自带的 .gitignore 文件。
  • CACHEDIR.TAG :pytest创建的缓存目录标签。
  • README.md : .pytest_cache 文件夹介绍。

说明:将文件加入到.gitignore中之后,在pycharm中,该文件或文件夹的颜色变为黄色。

7、使用mark标记测试用例

1、使用skip跳过测试用例

使用mark标记可以实现跳过测试用例的功能。通过为测试用例添加`skip`标记,可以将某些测试用例标记为跳过状态,从而在运行测试时跳过这些测试用例。

import pytest

@pytest.mark.skip
def test_example():
    # 测试用例的代码
    assert 1 + 1 == 2

在上述示例中,`@pytest.mark.skip`装饰器将`test_example()`函数标记为跳过状态。当运行pytest时,这个测试用例将被跳过,不会被执行。

2、使用skipif条件为真时跳过测试用例

除了直接跳过整个测试用例,还可以使用`skipif`标记来根据条件动态地跳过测试用例。`skipif`标记可以接受一个条件表达式和一个跳过的原因,只有当条件表达式为真时,才会跳过对应的测试用例。

import pytest

@pytest.mark.skipif(condition, reason="跳过原因")
def test_example():
    # 测试用例的代码
    assert 1 + 1 == 2

在上述示例中,`condition`是一个条件表达式,当它为真时,对应的测试用例将被跳过,并且会提供一个跳过的原因。

详见:pytest合集(7)— Mark标记

8、运行不同层级的测试用例

pytest可以运行不同层级的测试用例,从整个文件夹到具体的函数级别。

1. 运行测试文件夹中的所有用例:

pytest <folder_path>

使用上面的命令,pytest将会运行指定文件夹路径下的所有测试用例文件。

2. 运行py文件的所有用例:

pytest <file_path>

使用上面的命令,pytest将会运行指定的.py文件中的所有测试用例。

3. 运行类中的所有用例:

pytest <file_path>::<class_name>

使用上面的命令,pytest将会运行指定.py文件中指定类名的所有测试用例。

4. 运行指定函数的测试用例:

pytest <file_path>::<class_name>::<function_name>

使用上面的命令,pytest将会运行指定.py文件中指定类中指定函数的测试用例。

9、管理测试用例执行顺序

pytest运行测试用例默认执行顺序是,文件之间按ASCLL码排序,文件里的测试用例从上往下执行。

1、使用钩子函数管理测试用例执行顺序:

# 管理收集到的测试用例执行顺序
def pytest_collection_modifyitems(session, config,items):
    """
    called after collection is completed.
    you can modify the ``items`` list
    :param _pytest.main.Session session: the pytest session object
    :param _pytest.config.Config config: pytest config object
    :param List[_pytest.nodes.Item] items: list of item objects

    """
    print("收集到的测试用例:%s"%items)
    print("收集到的测试用例items:",type(items))
    items.sort(key=lambda x: x.name)
    print('调整后的测试用例执行顺序',items)

2、使用 pytest-ordering 插件管理测试用例执行顺序:

注意:这个pytest插件已经不再维护了,只适用于python 3.6之前的版本。

官方文档:pytest-ordering: run your tests in order — pytest-ordering 0.4 documentation

3、使用 pytest-order插件管理测试用例执行顺序:

注意:

pytest-order 是 pytest-ordering 的升级版本。

适用于 Python 3.6 - 3.10;

对于除 Python 3.10 之外的所有版本,pytest 版本 >= 5.0.0;

对于 Python 3.10,pytest >= 6.2.4。

官网文档:pytest-order - a plugin to order test execution — pytest-order 1.0.1 documentation