admin 管理员组

文章数量: 887021

Pytest

一、pytest封装测试类

文件命名:以test_开头

测试类命名:以Test_开头

测试用例命名:以test_开头

二、前置/后置方法

前置方法:

setup

放在类外只对类外方法有效,每条测试方法执行前都执行一次,在setup_function后执行。放在类内只对类内方法有效,每条测试用例执行前都执行一次,在setup_method后执行。

setup_function

放在类外对类外测试方法有效,每条测试方法执行前都执行一次,在setup前执行。放在类内无效。

setup_method

放在类内对类内测试方法生效,每条测试方法执行前都执行一次,在setup前执行。放在类外无效。

setup_class

放在类内对类内测试方法生效,类内所有测试方法执行前执行一次,在setup/setup_function/setup_method前执行。放在类外无效。

setup_model

放类中无效,放类外且类外有函数用例或类内有函数用例时才生效,整个.py模块开始前和结束后各调用一次。

后置方法:

teardown

teardown_function

teardown_method

teardown_class

teardown_model

三、测试用例执行顺序控制

import pytest-ordering

在测试用例前添加装饰@pytest.mark.run(order=XX)

import pytestclass TestOrder:@pytest.mark.run(order=3)def test_case1(self):print('test_case1')@pytest.mark.run(order=1)def test_case2(self):print('test_case2')@pytest.mark.run(order=2)def test_case3(self):print('test_case3')if __name__=="__main__":pytest.main(["-s","test_practice.py"])

四、测试用例跳过

1.直接跳过

测试方法前直接加:@pytest.mark.skip(“说明”)

2.条件跳过

设置条件:

例如: condition1 = '冒烟'

测试类前加条件,对该文件所有测试类生效。

测试类中加条件,仅对该测试类生效。

条件使用:

@pytest.mark.skipif(condition1 == '冒烟', reason='smoketest')

def test_case1(self):

print('test_case1')

说明:当condition1 == '冒烟'判定成功,test_case1跳过不执行。

3.样例代码

import pytestcondition1='class外部'
class TestSkip1:condition2='class内部'@pytest.mark.skip('直接跳过test_case1')def test_case1(self):print('test_case1')@pytest.mark.skipif(condition1 == 'class外部', reason='外部条件class1内部生效')def test_case2(self):print('test_case2')@pytest.mark.skipif(condition2 == 'class内部', reason='class1内部条件class1内部生效')def test_case3(self):print('test_case3')def test_case4(self):print('test_case4')
#内部标签整个模块内生效
class TestSkip2:@pytest.mark.skipif(condition1 == 'class外部', reason='外部条件class2内部生效')def test_case5(self):print('test_case5')
@pytest.mark.skipif(condition1 == 'class外部', reason='外部条件外部方法生效')
def test_case6(self):print('test_case6')
#内部标签外部不生效
# @pytest.mark.skipif(condition2 == 'class内部', reason='class1内部条件外部无法使用')
# def test_case7(self):
#     print('test_case7')
if __name__=="__main__":pytest.main(["-rs","test_practice.py"])

五、测试用例标签:

1.在pytest.ini文件中声明标签

[pytest] # 固定的section名

markers= # 固定的option名称

标签名1: 标签名的说明内容。

标签名2

标签名N

2.给测试用例加标签

方式一:@pytest.mark.已注册标签名

一个用例可以添加多个标签

方式二:pytestmark = [pytest.mark.已注册标签名]

可以加多个pytestmark = [pytest.mark.已注册标签名1,pytest.mark.已注册标签名2]

在类里面使用声明类内所有用例都会标记。

在模块(文件)里面标记,该文件所有用例都会打上该该标记。

3.使用标签过滤执行

pytest -m "Label" 路径或者文件

4.样例代码

import pytest@pytest.mark.classLabel('classLabel')
class TestLabel1:@pytest.mark.Label1('label1')def test_case1(self):print('test_case1')@pytest.mark.Label2('label2')def test_case2(self):print('test_case2')@pytest.mark.Label3('label3')def test_case3(self):print('test_case3')@pytest.mark.Label4('label4')
def test_case4():print('test_case4')if __name__=="__main__":# pytest.main(["-m", "Label1", "-s", "test_practice.py"])# pytest.main(["-m", "not Label1", "-s", "test_practice.py"])# pytest.main(["-m", "Label1 or Label3", "-s", "test_practice.py"])# pytest.main(["-m", "classLabel and Label3", "-s", "test_practice.py"])pytest.main(["-m", "classLabel and not Label3", "-s", "test_practice.py"])

六、传参

1.方式一:@pytest.fixture()

fixture是在测试函数运行前后,由pytest执行的外壳函数,执行顺序在测试用例代码之前,类似于unittest中的setup,在其中定义作为finalizer(终结器)使用的函数才会在测试用例执行后执行,类似于unittest中的teardown。

# test_fixture.pyimport pytest@pytest.fixture()
def fixtureFunc():return 'fixtureFunc'def test_fixture(fixtureFunc):print('我调用了{}'.format(fixtureFunc))if __name__=='__main__':pytest.main(['-v', 'test_fixture.py'])

参数说明:

name: 可以使用name参数给装饰器起别名,用别名传参,起了别名就不能用函数名传参。

scope :作用域参数,session(会话级,每次会话只需要运行一次),module(模块),class(测试类),function(测试用例)

autouse--自动执行:默认False,如果改为True,则不需要调用,在对应的作用域会自动执行。

params --实现参数化(数据驱动)

import pytestdata = [("username1", "password1"), ("username2", "password2")]@pytest.fixture(scope="function", params=data)
def get_data(request):print(request.param)return request.paramdef test_login(get_data):print("账号:%s" % get_data[0], "密码:%s" % get_data[1])if __name__ == '__main__':pytest.main(["-s", "test_practice.py"])# *****************输出信息**************
('username1', 'password1')
账号:username1 密码:password1
.('username2', 'password2')
账号:username2 密码:password2

2.方式二:@pytest.mark.parametrize()

@pytest.mark.parametrize(('param1','param2'),(['param1value1','param2value1'],['param1value2','param2value2']))

@pytest.mark.parametrize('num',test_case01) #test_case01只能是普通方法,不能加fixture标签,该方法有返回值(一般直接处理成list类型)

说明:装饰器定义了几个参数,测试方法就要传入几个参数,每组测试数据数量和参数的个数要相等,执行测试方法时每一组测试数据自动执行一次该测试方法,类似循环,第一次传入第一组测试数据['name1','password1'],第二次传入['name2','password2']。

import pytest@pytest.mark.parametrize(('name','password'),(['name1','password1'],['name2','password2']))
def test_case1(name,password):print(name)print(password)# for date in name:#     print(date)
if __name__=="__main__":pytest.main(["-vs","test_practice.py"])
==============================控制台输出==============================
test_practice.py::test_case1[name1-password1] name1
password1
PASSED
test_practice.py::test_case1[name2-password2] name2
password2
PASSED

七、常用钩子函数:

1.在html测试报告中添加测试人信息

2.在html测试报告中添加测试执行时间

3.在测试报告中添加错误截图

样例代码如下:

import pytest
import time
from py.xml import html
import pytest
from selenium import webdriver# 在html测试报告中添加测试人信息
@pytest.mark.optionalhook
def pytest_html_results_summary(prefix, summary, postfix):prefix.extend([html.p("测试人: zg")])#在html测试报告中添加测试执行时间
@pytest.mark.optionalhook
def pytest_html_results_table_row(report, cells):cells.insert(3, html.td(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), class_='col-time'))cells.pop()# 设置一个browser固件
@pytest.fixture(scope='session', autouse=True)
def browser():global driverif driver is None:driver = webdriver.Chrome()return driver#定义截屏方法
def _capture_screenshot():return driver.get_screenshot_as_base64()#定义添加错误图片的钩子函数
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):"""当测试失败的时候,自动截图,展示到html报告中:param item:"""pytest_html = item.config.pluginmanager.getplugin('html')outcome = yieldreport = outcome.get_result()extra = getattr(report, 'extra', [])if report.when == 'call' or report.when == "setup":xfail = hasattr(report, 'wasxfail')if (report.skipped and xfail) or (report.failed and not xfail):file_name = report.nodeid.replace("::", "_") + ".png"screen_img = _capture_screenshot()if file_name:html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \'onclick="window.open(this.src)" align="right"/></div>' % screen_imgextra.append(pytest_html.extras.html(html))report.extra = extra

八、测试报告

1.HTML报告

(1)安装类库 pytest-html

(2)conftest中定制报告内容,即添加钩子函数到conftest.py

(3)生成默认html报告

执行测试脚本命令中添加参数“--html=测试报告.html”。

例如:

terminal中执行:pytest --html=测试报告.html。

main函数中执行:pytest.main(['test_login.py','--html=测试报告.html'])

2.allure测试报告

(1)环境准备

1)依赖java,需安装JDK

2)allure安装包

下载最新版本的allure-commandline-2.*.*.zip ,解压到任意不含中文的路径下,并配置其中bin文件夹的环境变量到path。

下载链接:/

3)安装类库allure-pytest

(2)生成allure测试报告

1)pytest 测试脚本.py --alluredir= ./tmp/my_allure_results

在指定路径下生成测试报告所需文件及数据。

2)allure serve ./tmp/my_allure_results

生成即时观看的allure测试报告(未生成具体的测试报告文件)

3)allure generate ./tmp/my_allure_results

创建生成allure的html格式的报告

九、日志

1.pytest自带日志模块

(1)import logging

(2)记录器Logger

1)创建Logger

logger = logging.getLogger(name=None)

创建具有层次结构的记录器是,使用 '.' 点号分割,如'a'、'a.b'等,a是b的父记录器,b继承a的配置。未指定name时,返回Logger根实例,名称是root。

2)设置Logger

<1>设置日志级别(记录日志时不会记录比设置级别低的日志)

Logger.setLevel(level = logging.DEBUG)

包含如下级别:

CRITICAL

ERROR

WARNING

INFO

DEBUG

NOTSET # 作为 logger 的默认值。

<2>添加Handler

logger.addHandler(filehandler)

<3>添加Filter

logger.addFilter()

<4>记录日志

logger.debug("logger debug log")

logger.info("logger info log")

logger.warning("logger warning log")

logger.error("logger error log")

logger.critical("logger critical log")

(3)处理器Handler

处理器,将(记录器产生的)日志记录发送至合适的目的地,可以是控制台、文件。

1)创建Handler

<1>流处理器StreamHandler

console =logging.StreamHandler(stream=None)

stream是流的意思,默认值为系统输出流:sys.stderr,可以输出内容到Terminal。也输出内容到自定义文件。

import loggingf=open('test1.log','a') # 打开一个文件
logger = logging.getLogger('test') #实例化一个记录器
logger.setLevel(level = logging.DEBUG) #设置记录器的错误级别
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 设置一个输出格式
console = logging.StreamHandler(f) #实例化一个流处理器
console.setFormatter(formatter) #设置流处理器格式
logger.addHandler(console) #将流处理器添加到记录器
# 在记录器中记录日志
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

<2>文件处理器FileHandler

filehandler = logging.FileHandler(filename, mode='a', encoding=None)

将对应记录器的日志输出到filename对应文件中,同时也会输出到终端。FileHandler()会自动打开filename文件,打开方式有mode决定,默认值为'a'。

2)设置Handler

<1>设置日志级别

filehandler.setLevel()

<2>设置格式

filehandler.setFormatter()

<3>添加Filter

logger.addFilter()

(4)日志格式Formatter

格式化器,指明了最终输出中日志记录的布局。

1)创建Formatter

formatter=logging.Formatter()

常用参数如下:

%(levelno)s 打印日志级别的数值

%(levelname)s 打印日志级别名称

%(pathname)s 打印当前执行程序的路径

%(filename)s 打印当前执行程序名称

%(funcName)s 打印日志的当前函数

%(lineno)d 打印日志的当前行号

%(asctime)s 打印日志的时间

%(thread)d 打印线程id

%(threadName)s 打印线程名称

%(process)d 打印进程ID

%(message)s 打印日志信息

例如:formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

2.logging.basicConfig()函数

使用basicConfig()函数直接进行直接在测试脚本中配置日志

(1)参数说明

filename:指定日志输出文件,及其位置

filemode:执行日志输出到文件的方式,默认为'a',追加写入

format:指定输出日志的格式

datefmt:指定输出日志中的日期格式

level:指定输出那个级别及其以上级别日志

(2)样例代码

import logginglogging.basicConfig(level=logging.INFO,format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='[%Y-%m_%d %H:%M:%S]',filename='./my.log',filemode='a')logging.critical('logging critical message.')
logging.error('logging error message')
logging.warning('logging warning message')
logging.info('logging info message')
logging.debug('logging debug message')

3.日志模块公共配置文件

(1)import logging.config

(2)创建*.conf文件

文件后缀固定,名称自定。例如:logger.conf

1)创建Logger

在[loggers]下通过关键字keys配置需要创建的Logger,创建多个时用英文逗号隔开

[loggers]
keys=root,file
2)为每个Logger配置相关属性

[logger_root]是说明为哪个Logger配置属性,例如[logger_root],就是为root记录器配置属性,即步骤一中keys后面配置的值。关键字level设置日志级别,关键字handlers为记录器添加处理器。

[logger_root]
level=DEBUG
handlers=hand01
3)创建handler

在[handlers]下通过关键字keys配置需要创建的handler,创建多个时用英文逗号隔开

[handlers]
keys=hand01,hand01
4)为每个handler配置相关属性

[handler_hand01]是说明为哪个handler配置属性,例如[handler_hand01],就是为hand01处理器配置属性,即步骤三中keys后面配置的值。

[handler_hand01] #[handler_hand01]是说明为哪个handler配置属性,例如[handler_hand01],就是为hand01处理器配置属性,即步骤三中keys后面配置的值。
class=StreamHandler #关键字class说明处理器的类型
level=WARNING # 关键字level设置日志级别,记录日志级别大于等于处理设置日志级别才能输出,小于不处理
formatter=form01 #关键字formatter设置为该处理器添加日志输出格式
args=(sys.stderr,) 该处理器的参数列表,StreamHandler流处理器(sys.stderr,),FileHandler文件处理器args=('python.log', 'a')
5)创建formatter

在[formatters]下通过关键字keys配置需要创建的formatter,创建多个时用英文逗号隔开

[formatters]
keys=form01,form02
6)为每个formatter配置相关属性

[formatter_form01]是说明为哪个formatter配置属性,例如[formatter_form01],就是为form01格式化器配置属性,即步骤三中keys后面配置的值。

[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
7)样例代码
###############################################
[loggers]
keys=root
[logger_root]
level=DEBUG
handlers=hand01
###############################################
[handlers]
keys=hand01
[handler_hand01]
class=StreamHandler
level=WARNING
formatter=form01
args=(sys.stderr,)
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form02
args=('python.log', 'w')
###############################################
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form02]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S

(3)测试脚本中引用配置文件

1)导入类库

import logging

import logging.config

2)测试脚本中添加该配置文件

logging.config.fileConfig('logger.conf')

3)实例化配置文件中某个记录器

root_logger = logging.getLogger('root')

4)记录器中添加日志,运行测试脚本

5)样例代码
import logging
import logging.configlogging.config.fileConfig('logger.conf') # 测试脚本中添加该配置文件
root_logger = logging.getLogger('root') # 实例化配置文件中root记录器
root_logger.debug('test root logger...') # 记录器中添加日志

十、FAQ

1.脚本的并发

安装类库pytest -xdist

执行脚本添加参数"-n=4"

2.配置文件pytest.ini

pytest.ini 可以改变 pytest 的默认行为,不管是主函数模式运行,命令行模式运行,都会去先读取这个全局配置文件。如pytest.ini有该参数值,在执行的时候,先读取配置文件中的参数,如没有,则取其他地方的(主函数/命令行中)。

(1)文件首行写[pytest]

(2)定义标签,指定测试内容

第五章

(3)配置测试执行参数

脚本运行参数设置,多个参数用英文空格隔开。在这里设置后,通过命令行或者mian方法运行脚本时不用在添加参数。例:addopts =-v -s

获取帮助 :pytest --help

-s:在Termail中执行测试用例py文件,默认不执行print语句,想要执行需加上-s参数

-rs:输出跳过测试方法原因,同时也会输出print语句

-v:更详细的运行信息

-m:通过指定标签来过滤运行哪些用例

-k :通过指定关键来过滤运行哪些用例

--lf:只执行上次失败的用例

--tb:

--tb=no:不展示用例失败的错误详情。

--tb=line:展示用例失败的代码具体行数

-x:遇到错误停止

–maxfail=num:错误用例达到次数停止

--duration=N:表示把最耗时间的用例展示出来,N表示最慢的N个

(4)指定执行测试用例范围

配置多个参数取交集

1)testpaths:指定路径

testpaths=./test_script/authority ./test_script/user

通过路径来挑选测试方法,执行配置路径下所有测试脚本中所有方法。可以配置多个路径,用英文空格隔开。未配置的路径,即使配置对应的脚本或者类或者方法也不执行

2)python_files:指定文件

python_files=完整测试脚本名

python_files=测试脚本名前缀*

配置文件会执行文件所有测试用例,配置多个文件用英文空格隔开。用通配符时,仅支持后面加通配符,前后都加执行时会卡在挑选测试方法步骤执行不下去。

3)python_classes:指定测试类

python_classes=完整测试类名

python_classes=测试类名前缀*

配置测试类会执行类中所有测试方法,配置多个测试类用英文空格隔开。不是类方法的配置了对应文件,未配置对应测试方法也执行。

4)python_functions:指定测试用例

python_functions=完整方法名

python_functions=测试方法名前缀*

配置多个用英文空格隔开。用通配符时,仅支持后面加通配符,前后都加执行时会卡在挑选测试方法步骤执行不下去。

5)样例

[pytest]
markers=Labelname1:labelvalue1Labelname2:labelvalue3...addopts=-v -rs -s
testpaths=./test_script
python_files=test_pytest_workflow1.py test_practice.py
python_classes=TestLabel2 TestClass1

3.配置文件contest.py

(1)contest.py说明

conftest.py 文件名称是固定的,不能改动。

conftest.py不需导入,pytest执行自动调用。

一个项目中可以有多个conftest.py。一个conftest.py只对同一路径下的其它文件以及其子文件生效。

(2)使用场景

1)设置全局变量
2)测试数据处理

大批量的脚本需要使用相同的测试数据,可以放在conftest.py中进行数据处理。

3)添加钩子函数

使用pytest-html生成测试报告添加钩子函数,定义报告样式

本文标签: Pytest