pytestⅠ

目录

Pytest介绍

pytest安装

pytest运行方式

1、pytest主函数运行方式

2、pytest命令行运行方式

pytest初始化和结束方法

常用断言方式

pytest配置文件

pytest测试报告插件

pytest控制测试方法执行顺序优先级插件

pytest失败重试插件

fixture工厂函数


Pytest介绍

pytest是python的⼀种单元测试框架,同⾃带的Unittest测试框架类似, 相⽐于Unittest框架使⽤起来更简洁,效率更⾼。

pytest特点:

  1. ⾮常容易上⼿,⼊⻔简单,⽂档丰富,⽂档中有很多实例可以参考
  2. ⽀持简单的单元测试和复杂的功能测试
  3. ⽀持参数化
  4. 执⾏测试过程中可以将某些测试跳过,或者对某些预期失败的Case标记成失败
  5. ⽀持重复执⾏失败的Case
  6. ⽀持运⾏由Nose , Unittest编写的测试Case
  7. 具有很多第三⽅插件,并且可以⾃定义扩展
  8. ⽅便的和持续集成⼯具集成

pytest安装

安装:

win:pip install pytest

安装指定版本:pip install pytest==版本号

安装后验证:

pytest --version

如果可以查看版本,那么说明pytest已经变为命令行命令

pytest运行方式

1、pytest主函数运行方式

pytest.main( [ " 需要执行文件名字 " ] )

★ 主函数运⾏仅限于debug代码调试使用

import pytest


def test_001():
    print('test_001')
    assert True  # 断言方式:assert 条件表达式,条件表达式结果true或false


def test_002():
    print('test_002')
    assert False


if __name__ == '__main__':
    pytest.main(['test_main.py'])

2、pytest命令行运行方式

pytest 路径 + 文件名

默认不输出打印内容,需添加-s参数才能输出打印内容

pytest可以不指定文件名字,默认搜索:

当前路径下所有以test_开头的文件

搜索文件中以Test开头的类

搜索类中以test开头的方法

★ 命令行运行方式是项目唯一方式,方便持续集成及项目管理

pytest初始化和结束方法

# 3、模块级别setup_module
#   1 只能作⽤于测试类外部
#   2 只会在整个⽂件中最开始运⾏⼀次
def setup_module():
    print('\n ---模块级别setup_module')


# 4、功能级别setup_function,仅限于类外面的测试方法
def setup_function():
    print('\n ---功能级别setup_function')


class Test:
    # 1、类级别setup_class和teardown_class
    def setup_class(self):
        print('\n ---类级别setup_class')

    def teardown_class(self):
        print('\n ---类级别teardown_class')

    # 2、方法级别setup 和 teardown
    def setup(self):
        print('\n ---方法级别setup')

    def teardown(self):
        print('\n ---方法级别teardown')

    def test_001(self):
        print('测试方法test_001')
        assert True

    def test_002(self):
        print('测试方法test_002')
        assert True


def test_001():
    print('测试类外的测试方法test_001')
    assert True


def test_002():
    print('测试类外的测试方法test_002')
    assert True

命令行运行结果:

(venv) D:\untitled\pytestDemo\scripts>pytest 2_pytest初始化和结束方法.py -s
=========================================================== test session starts ============================================================
platform win32 -- Python 3.5.4, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\untitled\pytestDemo\scripts
plugins: html-1.22.1, metadata-1.8.0, ordering-0.6, rerunfailures-8.0
collected 4 items

2_pytest初始化和结束方法.py
 ---模块级别setup_module

 ---类级别setup_class

 ---方法级别setup
测试方法test_001
.
 ---方法级别teardown

 ---方法级别setup
测试方法test_002
.
 ---方法级别teardown

 ---类级别teardown_class

 ---功能级别setup_function
测试类外的测试方法test_001
.
 ---功能级别setup_function
测试类外的测试方法test_002
.

============================================================ 4 passed in 0.06s =============================================================

(venv) D:\untitled\pytestDemo\scripts>

常用断言方式

1、捕获断言

2、列表断言

import time

from util import get_driver


class TestSetLocation:

    def setup_class(self):
        # 声明我们的driver对象
        self.driver = get_driver()

    def teardown_class(self):
        """退出driver"""
        self.driver.quit()

    def test_loction(self):
        """测试方法"""
        # 存储
        save = self.driver.find_element_by_xpath("//*[contains(@text,'存储')]")
        # WLAN
        wlan = self.driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
        # 存储 -> WLAN
        self.driver.drag_and_drop(save, wlan)
        # 点击位置信息
        self.driver.find_element_by_xpath("//*[contains(@text,'位置信息')]").click()
        # 点击模式
        self.driver.find_element_by_xpath("//*[contains(@text,'模式')]").click()
        time.sleep(1)
        # 选择仅限设备
        self.driver.find_element_by_xpath("//*[contains(@text,'GPS确定位置')]").click()
        time.sleep(1)
        # 点击返回按钮
        self.driver.find_element_by_class_name("android.widget.ImageButton").click()
        # 断言结果
        """方式一:捕获断言"""
        # try:
        #     # 找预期结果
        #     self.driver.find_element_by_xpath("//*[contains(@text,'仅限设备')]")
        #     # 找到 断言成功
        #     assert True
        # except:
        #     # 没找到 断言失败
        #     assert False
        """方式二:列表断言"""
        # 获取所有文本信息
        results = self.driver.find_elements_by_id("android:id/summary")
        # 断言 仅限设备 在结果列表中
        assert "仅限设备" in [i.text for i in results]

util.py:

from appium import webdriver


def get_driver():
    desired_caps = {'platformName': 'android',
                    'platformVersionName': '5.1',
                    'deviceName': '192.168.192.101:5555',
                    'appPackage': 'com.android.settings',
                    'appActivity': '.Settings',
                    'unicodeKeyboard': True}
    return webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

pytest配置文件

配置放在项目根目录下,当命令行输入pytest时会自动加载配置文件中的内容

配置文件名字:

pytest.ini

tox.ini

setup.cfg

配置文件首行:[pytest]

★ 配置文件中不允许出现任何的中文字符

命令行参数:addopts = 参数                                    # 多个参数间空格隔开

搜索测试目录:testpaths = 某个目录                       # 相对路径

搜索测试文件:python_files = 文件名字/通配符       # 可采用通配符进行匹配,*:代表任意内容

搜索测试类:python_classes = 类名/类通配符 

搜索测试方法:python_functions = 方法名/方法通配符

★ 类和方法名可以自己指定,要求所有人遵循规则

配置文件搜索执行方法流程:

搜索目录 > 搜索文件 > 搜索测试类名 > 搜索类中方法名

pytest测试报告插件

插件:pytest-html

安装:pip install pytest-html

使用方式:

命令行:--html=路径/xx.html                   # 等号两侧不允许出现空格

命令行:--junitxml=路径/xx.xml               # 等号两侧不允许出现空格

[pytest]
# not use chinese

addopts = -s --html=./report/report.html  --junitxml=./report/report.xml

testpaths = ./scripts
python_files = test_fixture.py
python_classes = Test*
python_functions = test*

pytest控制测试方法执行顺序优先级插件

插件:pytest-ordering

安装:pip install pytest-ordering       

使用方式:

装饰器装饰测试方法

@pytest.mark.run(order=None)

import pytest


# ★pytest失败重试插件不建议和setup_class方法使用,如果一定要使用,要么测试方法之间没有前后依赖关系 pip install pytest-rerunsfailures
# pytest测试方法优先级插件:pip install pytest-ordering,使用方式:注解的形式
# pytest测试报告插件:pip install pytest-html(可生成xml/html报告)

class TestFile:

    # 全为正数或负数时越小优先级越高,有正有负时,正数优先级高于负数
    # 负数优先级低于未标记的测试方法,正数优先级高于未被标记的测试方法
    # 0最高
    @pytest.mark.run(order=2)
    def test_001(self):
        print('\n登录')
        assert True

    @pytest.mark.run(order=1)
    def test_002(self):
        print('\n查询订单')
        assert True


if __name__ == '__main__':
    pytest.main(['test_fixture.py'])

结果:

pytest失败重试插件

插件:pytest-rerunfailures

安装:pip install pytest-rerunfailures

使用方式:

命令⾏: --reruns n            # n:重试次数 建议最⼤为2

★ 重试插件不建议和setup_class⽅法⼀起使⽤,如果⼀定要使⽤,那么测试⽅法之间没 有前后依赖关系

import pytest


class TestFile:

    def setup_class(self):
        print("\n声明手机驱动对象")


    def test_001(self):
        print('\n登录')
        assert True

    def test_002(self):
        print('\n查询订单')
        assert False


pytest.ini:

[pytest]
# not use chinese

addopts = -s --html=./report/report.html  --junitxml=./report/report.xml  --reruns 2

testpaths = ./scripts
python_files = test_fixture.py
python_classes = Test*
python_functions = test*

执行结果:

(venv) D:\untitled\pytestDemo\scripts>pytest 4_pytest测试方法优先级插件.py
======================================================================== test session starts ========================================================================
platform win32 -- Python 3.5.4, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\untitled\pytestDemo, inifile: pytest.ini
plugins: html-1.22.1, metadata-1.8.0, ordering-0.6, rerunfailures-8.0
collected 2 items

4_pytest测试方法优先级插件.py
声明手机驱动对象

登录
.
查询订单
R
声明手机驱动对象

查询订单
R
声明手机驱动对象

查询订单
F

============================================================================= FAILURES ==============================================================================
_________________________________________________________________________ TestFile.test_002 _________________________________________________________________________

self = <4_pytest测试方法优先级插件.TestFile object at 0x00000263696C1320>

    def test_002(self):
        print('\n查询订单')
>       assert False
E       assert False

4_pytest测试方法优先级插件.py:24: AssertionError
========================================================================= warnings summary ==========================================================================
d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436
  d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436: PytestDeprecationWarning: The 'junit_family' default value will change to 'xunit2' in pytest 6.
0.
  Add 'junit_family=legacy' to your pytest.ini file to silence this warning and make your suite compatible.
    _issue_warning_captured(deprecated.JUNIT_XML_DEFAULT_FAMILY, config.hook, 2)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
----------------------------------------------- generated xml file: D:\untitled\pytestDemo\scripts\report\report.xml ------------------------------------------------
------------------------------------------- generated html file: file://D:\untitled\pytestDemo\scripts\report\report.html -------------------------------------------
========================================================== 1 failed, 1 passed, 1 warning, 2 rerun in 0.15s ==========================================================

(venv) D:\untitled\pytestDemo\scripts>

fixture工厂函数

fixture装饰器来标记固定的⼯⼚函数,在其他函数,模块,类或整个⼯程调⽤它时会被激活 并优先执⾏,通常会被⽤于完成预置处理和重复操作

Api:

fixture(scope="function", params=None, autouse=False, name=None)
       常⽤参数:
             # scope:被标记⽅法的作⽤域
                           # "function" (default):作⽤于每个测试⽅法,每个test都运⾏⼀次
                           # "class":作⽤于整个类,每个class的所有test只运⾏⼀次
                           # "module":作⽤于整个模块,每个module的所有test只运⾏⼀次
                           # "session":作⽤于整个session(慎⽤),每个session只运⾏⼀次
             # params:(list类型)提供基础参数数据,供调⽤标记⽅法使⽤
             # autouse:是否⾃动运⾏,默认为False不运⾏,设置为True⾃动运⾏
             # name: ⼯⼚函数名字
使⽤⽅式:
            @pytest.fixture()
            ⽅法()

工厂函数-参数引用

import pytest


@pytest.fixture(name='outx')
def before_out():
    print('\nbefore_out')


class TestFixture:

    @pytest.fixture(name='inx')  # 工厂函数被调用时是优先运行的,默认方法名作为工厂函数名字,当指定name残烛时,那么函数名不再作为工厂函数的名字
    def before_in(self):
        print('\nbefore_in')

    # 方式一:参数引用
    def test_query_all(self, outx, inx):  # 工厂函数按参数先后顺序执行
        print('\n查询员工')
        assert True

运行结果:

(venv) D:\untitled\pytestDemo\scripts>pytest 5_工厂函数fixture.py
======================================================================== test session starts ========================================================================
platform win32 -- Python 3.5.4, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\untitled\pytestDemo, inifile: pytest.ini
plugins: html-1.22.1, metadata-1.8.0, ordering-0.6, rerunfailures-8.0
collected 1 item

5_工厂函数fixture.py
before_out

before_in

查询员工
.

========================================================================= warnings summary ==========================================================================
d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436
  d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436: PytestDeprecationWarning: The 'junit_family' default value will change to 'xunit2' in pytest 6.
0.
  Add 'junit_family=legacy' to your pytest.ini file to silence this warning and make your suite compatible.
    _issue_warning_captured(deprecated.JUNIT_XML_DEFAULT_FAMILY, config.hook, 2)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
----------------------------------------------- generated xml file: D:\untitled\pytestDemo\scripts\report\report.xml ------------------------------------------------
------------------------------------------- generated html file: file://D:\untitled\pytestDemo\scripts\report\report.html -------------------------------------------
=================================================================== 1 passed, 1 warning in 0.11s ====================================================================

(venv) D:\untitled\pytestDemo\scripts>

工厂函数-函数引用

import pytest


@pytest.fixture(name='outx')
def before_out():
    print('\nbefore_out')


class TestFixture:

    @pytest.fixture(name='inx')  # 工厂函数被调用时是优先运行的,默认方法名作为工厂函数名字,当指定name残烛时,那么函数名不再作为工厂函数的名字
    def before_in(self):
        print('\nbefore_in')

   

    # 方式二:函数引用,类可以使用函数引用方法引用工厂函数,函数引用和类引用同时存在时,最先调用的时函数引用方法
    @pytest.mark.usefixtures('outx', 'inx')  # 按参数先后顺序执行
    def test_query(self):
        print('查询员工')
        assert True

运行结果:

(venv) D:\untitled\pytestDemo\scripts>pytest 5_工厂函数fixture.py
======================================================================== test session starts ========================================================================
platform win32 -- Python 3.5.4, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\untitled\pytestDemo, inifile: pytest.ini
plugins: html-1.22.1, metadata-1.8.0, ordering-0.6, rerunfailures-8.0
collected 1 item

5_工厂函数fixture.py
before_out

before_in
查询员工
.

========================================================================= warnings summary ==========================================================================
d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436
  d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436: PytestDeprecationWarning: The 'junit_family' default value will change to 'xunit2' in pytest 6.
0.
  Add 'junit_family=legacy' to your pytest.ini file to silence this warning and make your suite compatible.
    _issue_warning_captured(deprecated.JUNIT_XML_DEFAULT_FAMILY, config.hook, 2)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
----------------------------------------------- generated xml file: D:\untitled\pytestDemo\scripts\report\report.xml ------------------------------------------------
------------------------------------------- generated html file: file://D:\untitled\pytestDemo\scripts\report\report.html -------------------------------------------
=================================================================== 1 passed, 1 warning in 0.78s ====================================================================

(venv) D:\untitled\pytestDemo\scripts>

类可以使⽤函数引⽤⽅法引⽤⼯⼚函数作用于类中每一个方法:

★ 函数引⽤和类引⽤⼯⼚函数同时存在时,函数引⽤优先级⾼于类引⽤优先级

import pytest


@pytest.fixture(name='outx')
def before_out():
    print('\nbefore_out')


@pytest.mark.usefixtures('outx')  # 类引用可以引用类内部和类外部的工厂函数,作用于类中的每一个测试方法
class TestFixture:

    @pytest.fixture(name='inx')  # 工厂函数被调用时是优先运行的,默认方法名作为工厂函数名字,当指定name残烛时,那么函数名不再作为工厂函数的名字
    def before_in(self):
        print('\nbefore_in')


    # 函数引⽤和类引⽤⼯⼚函数同时存在时,函数引⽤优先级⾼于类引⽤优先级
    # ★ 前提是函数引用和类引用采用的都是方式二,若测试方法采用的是方式一参数引用先执行的是类引用而并非函数引用!!!
    @pytest.mark.usefixtures('inx')
    def test_query_all(self):
        print('\n查询员工')
        assert True

执行结果:

(venv) D:\untitled\pytestDemo\scripts>pytest 5_工厂函数fixture.py
======================================================================== test session starts ========================================================================
platform win32 -- Python 3.5.4, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\untitled\pytestDemo, inifile: pytest.ini
plugins: html-1.22.1, metadata-1.8.0, ordering-0.6, rerunfailures-8.0
collected 1 item

5_工厂函数fixture.py
before_in

before_out

查询员工
.

========================================================================= warnings summary ==========================================================================
d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436
  d:\program files\python35\lib\site-packages\_pytest\junitxml.py:436: PytestDeprecationWarning: The 'junit_family' default value will change to 'xunit2' in pytest 6.
0.
  Add 'junit_family=legacy' to your pytest.ini file to silence this warning and make your suite compatible.
    _issue_warning_captured(deprecated.JUNIT_XML_DEFAULT_FAMILY, config.hook, 2)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
----------------------------------------------- generated xml file: D:\untitled\pytestDemo\scripts\report\report.xml ------------------------------------------------
------------------------------------------- generated html file: file://D:\untitled\pytestDemo\scripts\report\report.html -------------------------------------------
=================================================================== 1 passed, 1 warning in 0.10s ====================================================================

(venv) D:\untitled\pytestDemo\scripts>

工厂函数和初始化函数比较:

"""
    工厂函数优先级低于setup
"""
import pytest


class Test:

    def setup(self):
        print("\n函数初始化方法setup")

    def setup_class(self):
        print("\n----类初始化方法setup_class")

    # @pytest.mark.usefixtures("inx")
    def test_001(self, inx):
        print("\n---测试方法")
        assert True

    @pytest.fixture(name="inx")
    def before(self):
        print("\n-----工厂方法before")


if __name__ == '__main__':
    pytest.main(['test_fixture.py'])

执行结果:

fixture(默认设置为运⾏)

"""工厂函数之autouse"""
import pytest


@pytest.fixture(autouse=True)  # autouse=True时,login工厂函数不需要调用,会在每个测试方法执行前自动执行工厂函数
def login():
    print("\n 登录")


class Test_001:

    def test_001(self):
        print("\n test001")

    def test_002(self):
        print("\n test002")

fixture(scope类级别)

"""工厂函数之scope类级别"""
import pytest

# scope默认function,值可以是function class module session(慎用,每个session只运行一次),
# 后两个用于编写插件使用,我们主要使用前两个
@pytest.fixture(scope='class', autouse=True)
def login():
    print("\n 登录")


class Test_002:

    def test_001(self):
        print("\n 进入个人中心")

    def test_002(self):
        print("\n 查看订单")
import pytest

# 注意:类级别的工厂函数  如果在类内部,运行结果为整个类的setup_class之后优先运行;
#                       如果在类外部,运行结果为在整个类开始运行工厂函数
@pytest.fixture(scope='class', autouse=True)
def login():
    print("\n 登录")


class Test_003:

    def setup_class(self):
        print('\n setup_class')

    def test_001(self):
        print("\n 进入个人中心")

    def test_002(self):
        print("\n 查看订单")

fixture参数化(list类型基础数据)

import pytest


@pytest.fixture(params=[1, 2, 3, 4]) # 可以为元组,但不推荐
def data(request):  # 固定参数:request 等价于这个列表
    return request.param  # 固定写法,返回列表中的某个值


class Test_004:

    def test_004(self, data): # 只有参数引用才可以使用工厂函数的返回值
        assert data != 2

pytest跳过测试方法

import pytest


class Test5:
    @pytest.mark.skipif(True, reason='跳过方法') # 跳过
    def test_01(self):
        print('\n test01')

    @pytest.mark.skipif(False, reason='跳过方法')   # 不满足条件,不跳过
    def test_02(self):
        print('\n test02')

    @pytest.mark.skipif(reason='跳过方法')  # 跳过
    def test_03(self):
        print('\n test03')

    @pytest.mark.skipif()  # 跳过
    def test_04(self):
        print('\n test04')

    @pytest.mark.skipif(True)  # 报错
    def test_05(self):
        print('\n test05')

    @pytest.mark.skipif(True)  # 报错
    def test_06(self):
        print('\n test06')

将某些方法标记为预期失败

import pytest


class Test6:
    # 标记预期失败,但还会执行一次测试方法
    @pytest.mark.xfail(True, reason='预期失败')    # xpassed
    def test_01(self):
        print('\n test01')
        assert True    # 断言通过,结果为xpass; 断言失败,结果为xfail,这种情况更合理

    @pytest.mark.xfail(True, reason='预期失败')   # xfailed
    def test_02(self):
        print('\n test02')
        assert False

    @pytest.mark.xfail(reason='预期失败')  # xfailed
    def test_03(self):
        print('\n test03')
        assert False

    @pytest.mark.xfail()     # xfailed
    def test_04(self):
        print('\n test04')
        assert False

    @pytest.mark.xfail(True)   # errors
    def test_05(self):
        print('\n test05')
        assert False

    @pytest.mark.xfail(False)  # errors
    def test_06(self):
        print('\n test06')
        assert False

pytest参数化

import pytest


class Test_ParamOne:

    @pytest.mark.parametrize('num', [1, 2, 3, 4])  # 上面申明,下面可以不使用 但不能不传递
    def test_001(self, num):  # 参数名必须要和申明的参数一致
        assert num != 2


def data():
    return [(1, 2, 3), (4, 4, 8)]


class Test_ParamMore:

    @pytest.mark.parametrize('a, b, c', data())
    def test_001(self, a, b, c):
        assert a + b == c

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值