• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
九五二七乌托邦
博客园    首页    新随笔    联系   管理    订阅  订阅

pytest

pytest是一个非常成熟的全功能的Python测试框架,主要特点有以下几点:

  简单灵活,容易上手,文档丰富;

  支持参数化,可以细粒度地控制要测试的测试用例;

  能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/ appnium等自动化测试、接口自动化测试( pytest+requests) ;

       pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-selenium (集成selenium) 、pytest-html (完美html测试报告生成)、pytest-rerunfailures (失败case重复执行)、pytest-xdist(多CPU分发)等;

  测试用例的skip和xfail处理;

  可以很好的和CI工具结合,例如Jenkins

安装pytest

  pip install pytest

pytest官网

  https://docs.pytest.org/ en/stable/

编写规则

  测试文件以test开头(以test结尾也可以)

  测试类以Test开头,并且不能带有init方法.

  测试函数以test开头

  断言使用基本的assert即可

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
class TestCase(object):
    def test01(self):
        print('我是test01')
    def test02(self):
        print('我是test02')
if __name__ == '__main__':
    # pytest.main(['-s','-v','test_01.py'])
    pytest.main(['-sv','test_01.py'])
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- D:\Python\python.exe
cachedir: .pytest_cache
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collecting ... collected 2 items

test_01.py::TestCase::test01 我是test01
PASSED
test_01.py::TestCase::test02 我是test02
PASSED

============================== 2 passed in 0.13s ==============================

Console参数介绍

  -v用于显示每个测试函数的执行结果

  -q只显示整体测试结果

  -s用于显示测试函数中print()函数输出

  -x, --exitfirst,在第一-个错误或测试失败时立即退出

  -h帮助

执行测试

  配置PyCharm执行: 

    Toals->Python Integrated tools-> Default test runner

  main方法

    pytest.main(['-s', '-v', '01 -pytest.py'])

  命令行

    pytest -s -v test.py

 

Pytest标记

  Pytest查找测试策略

    默认情况下,pytest 会递归查找当前目录下所有以test开始或结尾的Python脚本

    并执行文件内的所有以test开始或结束的函数和方法。

  标记测试函数

    由于某种原因(如test_ func2 的功能尚未开发完成),我们只想执行指定的测试函数。在pytest中有几种方式可以解决:

    第一-种,显式指定函数名,通过::标记

      test_ no_ mark.py::test func1

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
def test01():
    print('我是test01')
def test02():
    print('我是test02')
pytest test_01.py::test01
============================================================ test session starts ============================================================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collected 1 item                                                                                                                             

test_01.py .                                                                                                                           [100%]

============================================================= 1 passed in 0.24s =============================================================

 

    第二种,使用模糊匹配,使用-k选项标识。

      pytest -k func1 test_ no_ mark.py

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
def testaaa01():
    print('我是test01')
def testbbb02():
    print('我是test02')
def testaaabbb02():
    print('我是test02')

cmd执行:

pytest -k aaa test_01.py
============================================================ test session starts ============================================================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collected 3 items / 1 deselected / 2 selected                                                                                                

test_01.py ..                                                                                                                          [100%]

====================================================== 2 passed, 1 deselected in 0.15s ======================================================

 

    第三种,使用pytest.mark在函数.上进行标记

      给用例打标签

      注册标签名,通过.ini 配置文件,操作如下:

      新建pytest.ini 文件

      

 

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
@pytest.mark.do
def testaaa01():
    print('我是test01')
@pytest.mark.undo
def testbbb02():
    print('我是test02')

cmd执行

pytest -m do test_01.py
============================================================ test session starts ============================================================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1
rootdir: F:\pyqt_demo, inifile: pytest.ini
plugins: allure-pytest-2.8.40, Faker-4.1.0
collected 2 items / 1 deselected / 1 selected                                                                                                

test_01.py .                                                                                                                           [100%]

====================================================== 1 passed, 1 deselected in 0.15s ======================================================

 

 pytest参数化处理

  在pytest中,也可以使用参数化测试,即每组参数都独立执行一次测试。

  使用的工具就是pytest.mark.parametrize(argnames, argvalues)。

 

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
data1 = ['aaa','bbb']
@pytest.mark.parametrize('pwd',data1)
def test01(pwd):
    print(pwd)
data2 = [[1,2,3],[6,7,8]]
@pytest.mark.parametrize('a,b,c',data1)
def test02(a,b,c):
    print(a,b,c)
============================================================ test session starts ============================================================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1
rootdir: F:\pyqt_demo, inifile: pytest.ini
plugins: allure-pytest-2.8.40, Faker-4.1.0
collected 4 items                                                                                                                            

test_01.py ....                                                                                                                        [100%]

============================================================= 4 passed in 0.17s =============================================================

Pytest fixture

  定义fixture跟定义普通函数差不多,唯-一区别就是在函数.上加个装饰器@pytest.fixture()

  fixture命名不要以test开头,跟用例区分开。fixture是有返回值得,没有返回值默认为None。

  用例调用fixture的返回值,直接就是把fixture的函数名称当做变量名称。

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
@pytest.fixture()
def init():
    print('我是init')
    return 1
def test01(init):
    print('我是test01',init)
def test02(init):
    print('我是test02',init)
if __name__ == '__main__':
    pytest.main(['-sv','test_01.py'])
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- D:\Python\python.exe
cachedir: .pytest_cache
rootdir: F:\pyqt_demo, inifile: pytest.ini
plugins: allure-pytest-2.8.40, Faker-4.1.0
collecting ... collected 2 items

test_01.py::test01 我是init
我是test01 1
PASSED
test_01.py::test02 我是init
我是test02 1
PASSED

============================== 2 passed in 0.25s ==============================

pytest setup和teardown简介

  模块级(setup_ module/teardown_ module )开始于模块始末,全局的

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
def setup_module():
    print('我是模块级别的setup_module')
def teardown_module():
    print('我是模块级别的teardown_module')
def test02():
    print('我是测试用例test02')
def test01():
    print('我是测试用例test01')
if __name__ == '__main__':
    pytest.main(['test_01.py','-sv'])
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- D:\Python\python.exe
cachedir: .pytest_cache
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collecting ... collected 2 items

test_01.py::test02 我是模块级别的setup_module
我是测试用例test02
PASSED
test_01.py::test01 我是测试用例test01
PASSED我是模块级别的teardown_module


============================== 2 passed in 0.12s ==============================

  函数级(setup_ function/teardown_ function )只对函数用例生效(不在类中)

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
def setup_function():
    print('我是函数级别的setup_function')
def teardown_function():
    print('我是函数级别的teardown_function')
def setup_module():
    print('我是模块级别的setup_module')
def teardown_module():
    print('我是模块级别的teardown_module')
def test02():
    print('我是测试用例test02')
def test01():
    print('我是测试用例test01')
if __name__ == '__main__':
    pytest.main(['test_01.py','-sv'])
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- D:\Python\python.exe
cachedir: .pytest_cache
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collecting ... collected 2 items

test_01.py::test02 我是模块级别的setup_module
我是函数级别的setup_function
我是测试用例test02
PASSED我是函数级别的teardown_function

test_01.py::test01 我是函数级别的setup_function
我是测试用例test01
PASSED我是函数级别的teardown_function
我是模块级别的teardown_module


============================== 2 passed in 0.14s ==============================

  类级(setup_ class/teardown_ class )只在类中前后运行一次(在类中)

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
class TestCase(object):
    @classmethod
    def setup_class(cls):
        print('我是类级别的setup_class')
  @classmethon
def teardown_class(cls): print('我是类级别的teardown_class') def test04(cls): print('我是测试用例test04') def test05(cls): print('我是测试用例test05') if __name__ == '__main__': pytest.main(['test_01.py','-sv'])
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- D:\Python\python.exe
cachedir: .pytest_cache
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collecting ... collected 2 items

test_01.py::TestCase::test04 我是类级别的setup_class
我是测试用例test04
PASSED
test_01.py::TestCase::test05 我是测试用例test05
PASSED我是类级别的teardown_class


============================== 2 passed in 0.15s ==============================

  方法级(setup_method/teardown_method)开始于方法始末(在类中)

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
class TestCase(object):
@classmethod
def setup_class(cls):
print('我是类级别的setup_class')
@classmethod
def teardown_class(cls):
print('我是类级别的teardown_class')
def setup_method(self):
print('我是方法级别的setup_method')
def teardown_method(self):
print('我是方法级别的teardown_method')
def test04(cls):
print('我是测试用例test04')
def test05(cls):
print('我是测试用例test05')
if __name__ == '__main__':
pytest.main(['test_01.py','-sv'])
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- D:\Python\python.exe
cachedir: .pytest_cache
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collecting ... collected 2 items

test_01.py::TestCase::test04 我是类级别的setup_class
我是方法级别的setup_method
我是测试用例test04
PASSED我是方法级别的teardown_method

test_01.py::TestCase::test05 我是方法级别的setup_method
我是测试用例test05
PASSED我是方法级别的teardown_method
我是类级别的teardown_class


============================== 2 passed in 0.16s ==============================

 

  类里面的(setup/teardown) 运行在调用方法的前后

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
class TestCase(object):
    def setup(self):
        print('我是类里面的steup')
    def teardown(self):
        print('我是类里面的teardown')
    def test04(cls):
        print('我是测试用例test04')
    def test05(cls):
        print('我是测试用例test05')
if __name__ == '__main__':
    pytest.main(['test_01.py','-sv'])
============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- D:\Python\python.exe
cachedir: .pytest_cache
rootdir: F:\pyqt_demo
plugins: allure-pytest-2.8.40, Faker-4.1.0
collecting ... collected 2 items

test_01.py::TestCase::test04 我是类里面的steup
我是测试用例test04
PASSED我是类里面的teardown

test_01.py::TestCase::test05 我是类里面的steup
我是测试用例test05
PASSED我是类里面的teardown


============================== 2 passed in 0.13s ==============================

pytest allure生成测试报告

安装

  pip install allure-pytest

官方文档

  https://docs.qameta.io/

下载地址

  https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/

下载之后配置环境变量

allure用例描述

序号 使用方法 参数值 参数说明
1 @allure.epic() epic描述  一级菜单
2 @allure.feature() 模块名称 二级菜单
3 @allure.story() 用户故事 三级菜单
4 @allure.title() 用例的标题 标题
5 @allure.step() 步骤 步骤
6 @allure.severity()
用例级别
BLOCKER CRITICAL NORMAL MINOR
       
       

 

# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
import allure
@pytest.fixture(scope="session")
def login():
    print("首先是登录的测试用例")
@allure.step("步骤1:点击XXX")
def step_01():
    print("我是第一个步骤")
@allure.step("我是第二个步骤")
def step_02():
    print("我是第二个步骤")

@allure.feature("编辑界面")
class TestCase():
    @allure.story("我是第一个测试用例")
    def test01(self,login):
        step_01()
        step_02()
        print("我是第一个测试用例的代码位置")

    @allure.story("我是第二个测试用例")
    def test02(self,login):
        print("我是第二个测试用例执行的位置")

if __name__ == '__main__':
    pytest.main(["--alluredir","./repports",'test_01.py'])

执行完成之后会生成一个repports目录,再次运行

allure serve ./repports test_01.py

 前置后置:

'''conftest.py文件是作用域的管理,文件名称是固定写法,
可以利用他的性质,来给测试用例做前置后置处理的操作。--前置后置处理器'''
import pytest
#session整个回话只调用一次 package每个项目一次 module每个模块一次 class每个类一次 function每个函数一个
#注意 autouse=True 的话 ,不用每个函数都写前置函数名称 都可以调用。如果不写 就要调用,不调用不执行
# @pytest.fixture(scope='function',autouse=True)
代码:
import pytest
@pytest.fixture(scope='session', autouse=True)
def realize_session():
print('session之前')
yield
print('session之后')

调用代码:
def test_add_cart(self):
print('test_add_cart')
#测试自定义调用fixture函数方式1
@pytest.mark.usefixtures('aa')
def test_learn(self):
print('装饰器方式调用fixtures函数')
#测试自定义调用fixture函数方式1
def test_learn2(self,aa):
print('传参方式调用fixtures函数')

参数化:
# *_*coding:utf-8 *_*
# @Author : zyb
import pytest
@pytest.mark.parametrize('uesrname',['aa','bb','cc'])
@pytest.mark.parametrize('password',['aaa','bbb','ccc'])
def test_can_01(uesrname,password):
    print(uesrname,password)

@pytest.mark.parametrize(['uesrname','password'],[['aa','aaa'],['bb','bbb'],['cc','ccc']])
def test_can_02(uesrname,password):
    print(uesrname, password)

 

posted @ 2022-03-12 12:25  九五二七乌托邦  阅读(117)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3