pytest使用总结

一、测试用例识别与运行

1.测试用例的识别规则

1)pytest框架的默认识别规则

  1. 测试文件:必须为test_*.py或*_test.py
  2. 测试类:必须为Test*
  3. 测试方法或函数:必须为test_*

2)修改pytest的默认执行规则

在测试框架的根目录下新建pytest.ini文件,加入新的用例识别规则。假如我们要定义新的pytest用例识别的规则如下:

  1. 测试文件:必须为test_*.py或weeds_*.py
  2. 测试类:必须为Test*或Weeds*
  3. 测试方法或函数:必须为test_*或weeds_*

pytest.ini配置如下:

[pytest]
python_files = weeds_* test_*
python_classes = Weeds* Test*
python_functions = weeds_* test_*

 

2.测试用例的执行顺序

根据测试用例的编写顺序从上到下依次执行。

二、pytest命令行常用参数

  • pytest -v打印详细的日志信息
  • pytest -s将用例中print方法打印的信息输出到控制台
  • pytest 文件名 执行该文件中所有的测试用例
  • pytest 文件名:类名 单独执行文件中的类
  • pytest 文件名:类名:方法名 单独执行类中的方法
  • pytest -k "类名 and not 方法名"跳过某个方法执行测试用例
  • pytest -m [标记名] 执行被@pytest.mark.[标记名]装饰的测试用例
  • pytest -x 文件名 一旦用例执行,就停止执行其他用例
  • pytest --maxfail=[num] 当错误用例数达到num时,停止执行用例

三、pytest用例参数化

1.参数化的使用

使用@pytest.mark.parametrize(argnames,argvalues)对用例进行参数化。

  • argnames:要参数化的变量,string,list,tuple
  • argvalues:参数化的值,list,list[tuple]

2.参数化实战

参数化变量使用string。

# 要参数的变量使用string,参数化的值使用list[touple]
@pytest.mark.parametrize("a,b", [(1, 2), (2, 3)])
def test_add(a, b):
    print(a + b)

参数化变量使用tuple。

# 要参数化的变量使用list,参数化的值使用tuople(touple)
@pytest.mark.parametrize(["a", "b"], ((1, 2), (3, 4)))
def test_add01(a, b):
    print(a + b)

参数化变量使用list类似tuple,就不再举例。

使用多个parametrize对参数进行组合。

# 对两组测试数据进行组合
@pytest.mark.parametrize("a", [1, 2, 3])
@pytest.mark.parametrize("b", ["a", "b", "c"])
def test_div(a, b):
    print(f"{a} is {b}")

使用yaml文件存储要参数化的值,对测试用例进行数据驱动。

import yaml
# 使用yaml进行参数化
data = yaml.safe_load(open("data.yaml"))


@pytest.mark.parametrize("a,b", data)
def test_add02(a, b):
    print(a+b)

四、pytest fixture使用

1.fixture的作用

说到pytest fixture要先提一下pytest的setup,teardown方法,用于测试前准备或测试后清理工作。

  • 模块级(setup_module/teardown_module)模块始末执行
  • 函数级(setup_function/teardown_function)只对函数用例生效(不在类中的方法称为函数)
  • 类级(setup_class/teardown_class)在类中前后运行一次
  • 方法级(setup_method/teardown_method)开始于方法始末,同setup/teardown

fixture是pytest用于将测试前后准备,清理工作的代码分离出核心测试逻辑的一种机制,从功能上看,fixture与setup/teardown相似。

2.fixture的作用域

fixture中有一个参数scope,通过该参数确定fixture的作用域,scope包含以下参数:

  • function 函数或方法级别被调用
  • class 类级别调用一次
  • module 模块级别调用一次
  • session 多个文件调用一次。比如一个目录下包含多个模块,模块中都使用了该fixture方法,使用pytest 目录名,运行该目录下所有模块,该fixture只调用一次

3.fixture的使用

场景一:同一模块下,有些用例需要登录,有些无需登录

import pytest


@pytest.fixture()
def login():
    print("这是一个登陆方法")


class TestFixture001:
    def test_001(self, login):
        print("需要登陆")

    def test_002(self):
        print("无需登陆")

场景二:用例运行前打开浏览器,运行后关闭浏览器

yield之前的代码在用例执行前运行,yield之后的代码在用例执行后运行

import pytest


# 默认scope等于function,作用域为函数级别,类似于setup、teardown.
# scope="module",作用域为module,类似于setup_module,teardown_module
# scope参数有四种选择:function(测试函数级别),class(测试类级别),
# module(测试模块“.py”级别),session(多个文件级别)。默认是function级别。
@pytest.fixture(scope="module")
def open():
    print("打开浏览器")
    yield
    print("关闭浏览器")


class TestFixture001:
    def test_001(self, open):
        print("登陆系统1")

    def test_002(self, open):
        print("登陆系统2")

场景三:自动导入conftest.py中的函数。conftest.py用于存放所有被fixture装饰的前置函数

conftest所在目录的上一级目录的文件无法访问到conftest中定义的函数,同级或者下级目录的文件可以访问到conftest中定义的函数

 

conftest.py存放装饰的前置函数,代码如下:

#!usr/bin/env python
#-*- coding:utf-8 -*-
"""
备注:
        conftest.py这个文件名是固定的,不可以更改。
        conftest.py与运行用例在同一个包下,并且该包中有__init__.py文件
        使用的时候不需要导入conftest.py,会自动寻找。
"""
import pytest
#默认scope等于function,作用域为函数级别,类似于setup、teardown.  scope="module",作用域为module,类似于setup_module,teardown_module
@pytest.fixture(scope="module")
def open():
    print("打开浏览器")
    yield
    print("关闭浏览器")

调用fixture的用例模块代码如下:

import pytest


class TestFixture001:
    def test_001(self, open):
        print("登陆系统1")

    # 使用@pytest.mark.usefixtures(open)将open函数作为test_002的前置函数
    @pytest.mark.usefixtures("open")
    def test_002(self):
        print("登陆系统2")

场景四:pytestmark = pytest.mark.usefixtures("open_everyone")

定义模块下所有用例的前置函数都为open_everyone

coftest.py代码如下:

import pytest
@pytest.fixture()
def open_everyone():
    print("打开浏览器")
    yield
    print("关闭浏览器")

用例模块中的代码如下:

import pytest

pytestmark = pytest.mark.usefixtures("open_everyone")


class TestFixture001:
    def test_001(self):
        print("登陆系统1")

    def test_002(self):
        print("登陆系统2")

场景五:@pytest.fixture(autouse="true")

让模块中的每个用例自动添加login为前置函数,无需将login作为参数传递给每个用例方法

import pytest


@pytest.fixture(autouse="true")
def login():
    print("这是一个登陆方法")


class TestFixture001:
    def test_001(self):
        print("需要登陆1")

    def test_002(self):
        print("需要登陆2")

场景六:两个用例,第一个用例需要登录admin验证,第二个需要登录guest验证

import pytest


@pytest.fixture(params=[["admin","dlh12378612873"],["guest","dlh12378612873"]])
def login(request):
    print("登录用户名{},登录密码{}".format(request.param[0],request.param[1]))


class TestFixture001:
    def test_001(self,login):
        print("需要登陆admin")
    def test_002(self,login):
        print("需要登陆guest")
    

四、pytest实用插件

插件的安装方式都为pip install [插件名],后面安装方式就不再赘述。

1.失败重跑:pytest-rerunfailures

用例失败时,重新运行5遍,每遍间隔时间为2秒。命令行执行:pytest test_rerunfailures.py --reruns 5 --reruns-delay 2 -vs

代码中实现如下:

import pytest


@pytest.mark.parametrize("a,b", [(1, 0)])
# 在方法中添加该装饰器,使用功能:用例失败时,重新运行5遍,用例执行时间间隔两秒。
# 如果方法中添加了该装饰器,命令行执行时,重新运行次数及间隔时间与装饰器配置不符,以装饰器的为准
@pytest.mark.flaky(reruns=5, reruns_delay=2)
def test_div(a, b):
    print(a / b)

2.多重校验:pytest-assume

pytest自带的校验工具assert,如果用例中包含多个断言,第一个断言失败时,其他断言则不会在执行。在实际工作中,我们希望用例的每个断言都得到执行,此时就需要多重校验插件pytest-assume。

示例:第二个断言失败,第三个仍然执行,代码如下:

import pytest


# 第二条断言失败时,第三条依旧会校验
def test_assume():
    pytest.assume(1 == 1)
    pytest.assume(False)
    pytest.assume(True)

3.指定用例执行顺序:pytest-ordering

在实际的工作过程中,我们会遇到需要按照一定顺序执行的用例,此时可以使用pytest-ordering来实现。如同一类中,有的用例指定了顺序,有的没有执行顺序,则先运行指定顺序的用例,之后再根据默认顺序执行剩余用例。

示例:指定用例按照一定顺序运行

import pytest


class TestOrder:
    # 在该脚本中,test_a第三个执行,因为其余三条用例都指定了执行顺序
    def test_a(self):
        print("a")

    # 该用例第二个执行
    @pytest.mark.run(order=2)
    def test_002(self):
        print("002")

    # 该用例第一个执行
    @pytest.mark.run(order=1)
    def test_001(self):
        print("001")

    # 该用例最后执行
    @pytest.mark.run(order=-1)
    def test_fianal(self):
        print("fianal")

4.指定前后依赖运行用例:pytest-dependency

在实际工作共经常会遇到用例执行有依赖关系,比如订单系统,查看订单前必须要创建订单。

示例:依赖关系为先创建订单,再查看订单,然后删除订单。具体代码如下:

import pytest


class TestDepend:
    """
    业务场景为新增/查看列表/删除
    查看列表用例依赖新增用例
    删除用例依赖查看列表用例
    """

    # 定义用例的别名
    @pytest.mark.dependency(name="create")
    def test_create(self):
        print("create")

    # 定义用例的别名为list,需要依赖的用例create,
    # 如果create执行失败,则list用例不会再执行
    # 如果list执行失败,delete则不会再执行
    @pytest.mark.dependency(name="list", depends=["create"])
    def test_list(self):
        print("list")
        assert False

    # 定义用例的别名为delete,需要依赖的用例为"create", "list"
    @pytest.mark.dependency(name="delete", depends=["create", "list"])
    def test_delete(self):
        print("delete")

5.分布式执行用例:pytest-xdist

分布式执行用例的前提:所有的用例都可以单独执行,用例间无依赖关系。当我们有大量的用例需要执行时,就可以采用分布式执行用例,来降低执行用例的总时长。

命令行执行使用命令:pytest [目录或文件名] -n [线程数]

示例:使用5个线程执行test_xdist.py文件。在命令行输入命令 pytest test_xdist.py -n 5

from time import sleep

import pytest


class TestXdist:
    @pytest.mark.parametrize("a", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    def test_001(self, a):
        sleep(1)
        print(a)

6.显示用例执行的进度条:pytest-sugar

安装成功后,在命令行运行用例时,显示进度条,具体效果如下图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值