CK28-Pytest测试框架-王毛毛

L1

pytest测试用例编写规则

模块命名

以test_开头或_test结尾

类命名

以Test开头,类名驼峰命名

方法命名

以test_开头

* 注意测试类中不能添加__init__方法

pytest测试用例结构

测试用例名称,测试步骤,断言

def test_method():
    #测试步骤
    assert 1==1

pytest测试用例断言

什么是断言

断言是一个在程序中的一阶逻辑(如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发着的预期结果,–当程序执行到断言的位置时,对应的断言应该为真,若断言不为真时,程序会中止执行,并给出错误信息

断言表达式

assert Expression1
assert Expression1,Expression2
Expression1总为布尔值,Expression2为是断言失败时,抛出的断言错误信息字符

断言示例

def test_001():
   assert 2==2,'结果相等'
def test_a():
   assert False
def test_c():
   assert 'abc' in 'abcd'

pytest测试框架结构

setup和teardown框架结构

pytest setup和pytest teardown介绍

1、setup_module和teardown_module;全局模块级
2、setup_class和teardown_class ;类级,只在类中前后运行一次
3、setup_function和teardown_function;函数级,在类外
4、setup_method和teardown_method;方法级,类中的每个方法执行前后
5、setup和teardown 在类中,运行在调用方法的前后(重点)


# 模块级 在类外
def setup_module(self):
    print('模块级前置')
def teardown_module(self):
    print('模块级后置')
class TestSetUpModule:
    def test_aa01(self):
        print('test_aa01')
    def test_aa02(self):
        print('test_aa02')

# 方法级,在类中
class TestSetUp_method:
    def setup_method(self):
        print('方法级前置')
    def teardown_method(self):
        print('方法级后置')
    def test_aa03(self):
        print('test_aa03')
    def test_aa04(self):
        print('test_aa04')

# 函数级在类外
def setup_function(self):
    print('函数级前置')
def teardown_function(self):
    print('函数级后置')
def test_aa05():
    print('test_aa05')
def test_aa06():
    print('test_aa06')

# 方法级,在类内
class TestSetUp_class:
    def setup_class(self):
        print('类级别前置')

    def teardown_class(self):
        print('类级别后置')

    def test_aa07(self):
        print('test_aa07')
    def test_aa08(self):
        print('test_aa08')


# 每个方法前后,类内
class TestSetUp:
    def setup(self):
        print('调用方法前置')

    def teardown(self):
        print('调用方法后置')

    def test_aa09(self):
        print('test_aa09')
    def test_aa10(self):
        print('test_aa10')

*调用先后顺序:模块级(setup_module/teardown_module)在类外>函数级(setup_function/teardown_function)类外>类级(setup_class/teardown_function)类中>方法级(set_method/teardown_method)每个方法前后执行

L2

pytest参数化用例

参数化:

1、通过参数的方式传递数据,从而实现数据和脚本分离
2、并且可以实现用例的重复生成与执行

参数化应用场景

1、测试登录场景
测试登录成功,登录失败

参数化实现方案

pytest参数化实现方法

装饰器:@pytest.mark.parametrize

@pytest.mark.parametrize("username,password",[['admin','123456']])
def test_login(username,password):
   print(f'姓名为:{username},密码为:{password}')

参数化测试函数使用

单参数

单参数:可以将数据放在列表中

search_list=['appium','selenium','hook']
@pytest.mark.parametrize('key_word',['appium','selenium','sale'])
def test_search(key_word):
   assert key_word in search_list

多参数

将数据放在列表嵌套元组中

@pytest.mark.parametrize('a,b,c',[(1,1,2),(2,2,5)])
def test_search(a,b,c):
   assert a+b==c

将数据放在列表嵌套列表中

@pytest.mark.parametrize('a,b,c',[[1,1,2],[2,2,5]])
def test_search(a,b,c):
   assert a+b==c

参数化-用例重命名-添加ids参数

通过ids参数,将别名放在列表中

@pytest.mark.parametrize('a,b,c',[[1,1,2],[2,2,5]],ids=('one','two'))
def test_search(a,b,c):
   assert a+b==c

参数化:用例重命名-添加ids参数(中文)

笛卡尔积

将每个参数穷举组合

@pytest.mark.parametrize('a',['appium','selenium','doc','hah'])
@pytest.mark.parametrize('b',[1,2,3])
def test_001(a,b):
   print(f'笛卡尔积{a}--{b}')

pytest标记测试用例

mark: 跳过(skip)及预期失败(xfail)

这是pytest的内置标签,可以处理一些特殊的测试用例,不能成功的测试用例
1、跳过标记:@pytest.mark.skip(始终跳过该测试用例)
2、有条件跳过:@pytest.mark.skipif(遇到特定情况跳过该测试用例)
3、预期失败:@pytest.mark.xfail(标记的用例错误,产生一个“期望失败”输出)

skip使用场景

  • 调试时不想运行这个用例
  • 标记无法在某些平台上运行的测试功能
  • 在某些版本中执行,其他版本中跳过
  • 比如:当前的外部资源不可用时跳过
    如果测试数据是从数据库中取到的,
    连接数据库的功能如果结果返回失败就跳过,因为执行也都报错
  • 解决1: 添加装饰器
    @pytest.mark.skip
    @pytest.mark.skipif
  • 解决2:代码中添加跳过代码
    @pytest.skip(reason)
import pytest

class TestSkip:
    @pytest.mark.skip
    def test_skip(self):
        print('跳过用例')
    a=2
    @pytest.mark.skipif(a>1,reason='测试')
    def test_skipif(self):
        print('跳过用例')

xfail使用场景

  • 与skip类似,预期结果为fail,标记用例为xfail
  • 用法:添加装饰器@pytest.mark.xfail
class Testxfail:
    @pytest.mark.xfail
    def test_xfail(self):
        assert 1==2

pytest运行用例

运行多条用例

运行某个/多个用例包

1、运行包下的所有用例 pytest

运行某个/多个用例模块

1、运行单个 pytest 文件名

运行某个/多个 用例类

1、pytest 文件名::类名

运行某个/多个用例方法

1、pytest 文件名::类名::方法名

pytest测试用例调度与运行

命令行参数-使用缓存状态

  • –lf(–last-failed) 只重新运行故障
  • –ff(–failed-first) 先运行故障然后再运行其他的测试用例

pytest命令行常用参数

命令行参数-常用命令行参数

  • -x 用例一旦失败(fail/error),就立刻停止执行
  • -m 被@pytest.mark.标记的用例 pytest -m ‘smoke1 and smoke2’
  • –maxfail=num 指定最大失败次数,达到最大失败次数以后停止运行
  • -k 执行包含关键字的测试用例
  • -v 打印详细日志
  • -s 打印输出日志 一般-vs一块儿使用
  • -collect-only 测试平台,pytest自动导入功能

python执行pytest

python-代码执行pytest-main函数

if __name__ == '__main__':
    # 1、运行当前目录下所有符合规则的用例,包括子目录(test_*.py 和 *_test.py)
    pytest.main()
    # 2、运行test_mark1.py::test_dkej模块中的某一条用例
    pytest.main(['./testcase/test_loginnew/test_user.py','-vs'])
    pytest.main(['test_mark1.py::test_dkej','-vs'])
    # 3、运行某个 标签
    pytest.main(['test_mark1.py','-vs','-m','dkej'])

运行方式
  `python test_*.py `

pytest异常处理


L3

#数据驱动

什么是数据驱动

数据驱动就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变,简单来说,就是参数化的应用,数据量小的测试用例可以使用代码的参数化来实现数据驱动,数据量大的情况下建议大家使用一张结构化的文件(yaml,json)来对数据进行存储,然后在测试用例中读取这些数据

应用

  • APP、Web、接口自动化测试
  • 测试步骤的数据驱动
  • 测试数据的数据驱动
  • 配置的数据驱动

pytest结合数据驱动-yaml

yaml文件介绍

对象:键值对的集合,用冒号“:”表示
数组:一组按次序排列的值
纯量:单个的,不可再分的值

  • 字符串
  • 布尔值
  • 整数
  • 浮点数
  • Null
  • 时间
  • 日期
eg
# 编程语言
languages:
  - PHP
  - Java
  - Python
book:
  Python入门: # 书籍名称
    price: 55.5
    author: Lily
    available: True
    repertory: 20
    date: 2018-02-17
  Java入门:
    price: 60
    author: Lily
    available: False
    repertory: Null
    date: 2018-05-11

yaml文件使用

读写yaml文件

  • 安装 pip3 install pyyaml
  • 读 ymal.safe_load(f)
  • 写 yam.safe_dump(data,f)
读
    with open('test.yaml',encoding='utf-8') as f:
        yaml.safe_load(f)
写
    with open('test.yaml',mode='a',encoding='utf-8') as f:
        yaml.safe_dump('bb',f)
        # a=yaml.safe_load(f)

pytest结合数据驱动-execl

pytest结合数据驱动-csv

pytest结合数据驱动-json

pytest测试用例生命周期管理(一)

fixture特点及优势

1、命令灵活:可以不起setup,teardown两个名字
2、数据共享:在conftest.py 配置里写方法可以实现数据共享,不需要import导入,可以跨文件导入
3、scope的层次及神奇的yield相当于各种setup和teardown
4、实现参数化

fixture在自动化中的应用-基本用法

场景:测试用例执行时,有的用例需要登录才能执行,有些用例不需要登录,setup和teardown无法慢煮,fixture可以,默认scope为function
步骤:
1、在conftest写好登录函数加上装饰器@pytest.fixture()
2、在要使用的测试方法中传入(登录函数名称),就会前置先登录
3、测试方法不传入的就不会调用登录

conftest.py
-----
@pytest.fixture()
@pytest.mark.parametrize("username,password,code,uuid",readdata()['Userdata'])
def get_login_token(username,password,code,uuid):
    json={"username":username,"password":password, "code":code,"uuid":uuid}
    res = requestClient.sendrequest(url="http://localhost:8013/auth/login", json=json, method='post')
    print(res.json())
def test(get_login_token):
      pass

pytest测试用例生命周期管理(二)

fixture用法,fixture在自动化中的应用-作用域

取值 范围 说明
funtion 函数级 每一个函数和方法都会调用
class 类级别 每一个测试类只运行一次
module 模块级 每一个.py 文件调用一次
package 包级 每一个 python 包只调用一次(暂不支持)
session 会话级 每次会话只需要运行一次,会话内所有方法及类,模块都共享这个方法

pytest测试用例生命周期管理(三)

Fixture在自动化中的应用-yield关键字

场景:测试方法前的依赖解决了,测试方法后销毁清除数据的要如何进行
解决:在fixture函数中加入yield关键字,yield是调用第一次返回结果,第二次执行它下面的语句
步骤:

@pytest.fixture()
def get_login_token(username,password):
    print('登录成功')
    yield 
    print('退出登录')

pytest测试用例生命周期管理(四)

fixture在自动化中的应用-数据共享

场景:你与其他测试⼯程师合作⼀起开发时,公共的模块要在不同⽂件中,要在⼤家都访问到的地⽅。
解决:使用conftest.py这个文件进行共享,可放在不同位置起着不同的范围共享
执行: 1、 系统执⾏到参数 login 时先从本模块中查找是否有这个名字的变量什么的,
2 、之后在 conftest.py 中找是否有。
步骤:将登陆模块带@pytest.fixture 写在 conftest.py

pytest测试用例生命周期管理-自动生效

fixture在自动化中的应用-自动应用

在函数上@pytest.fixture(@autouse=Ture)

pytestfixture实现参数化

fixture在自动化中的应用-参数化

场景:
测试离不开数据,为了数据灵活,一般数据都是通过参数传的
解决:
fixture通过固定参数request传递
步骤:
在fixture中增加@pytest.fixture(params=[{‘admin’:‘123456’},{‘wyl’:‘12346’}])
在方法参数写request,方法体里面使用request.param接收参数

@pytest.fixture(params=[{'admin':'123456'},{'wyl':'12346'}])
def login(request):
    return request.param
#

def test_001(login):
   for i ,v in login.items():
       print(i,v)

Fixture的用法总结


L4

pytest配置文件【进阶】

pytest.ini是什么

1、pytest.ini 是pytest的配置文件
2、可以修改pytest的默认行为
3、不能使用任何中文符号,包含汉字、空格、引号、冒号等

pytest.ini

1、修改用例的命名规则

;执行check开头和test开头的所有的文件,后面一定要加*
python_files = check_* test_*
;执行所有以Test和Check的类
python_classes = Test* Check*
;执行所有以test_和check_开头的方法
python_functions = test_* check_*

2、配置日志格式,比代码配置更方便
3、添加标签,防止运行过程报警错误
4、指定执行目录
;设置执行的路径
testpaths = 文件路径
;忽略某些文件夹/目录
norecursedirs = 文件夹/目录
5、添加默认参数
addopts = -v -s --alluredir=./results

pytest插件【进阶】

pytest插件分类

1、内置插件:代码内部的_pytest目录加载
2、外部插件:pip install 安装的插件
pip install 安装的插件
pip install pytest-ordering 控制用例的执行顺序
pip install pytest-rerunfailures 失败重跑
pip install pytest-assume 多种校验
pip install pytest-repeat 重复执行用例
pip install pytest-html 测试结果生成html报告插件
3、本地插件

pytest测试用例执行顺序自定义pytest-ordering【进阶】

场景:
对于集成测试,经常会有上下文依赖关系的测试用例
用例默认是自上而下执行
解决:
可以通过setup、teardown和fixture来解决,也可以使用对应的插件
1、安装 pip install pytest-ordering
2、用法@pytest.mark.run(order=1)

import pytest
class TestOrder:
    def test_01(self):
        print('test')

    @pytest.mark.run(order=1)
    def test_02(self):
        print('最开始运行')

pytest测试用例并行运行和分布式运行【进阶】

场景1:
测试用例1000条,一个用例执行1分钟,一个测试人员执行需要1000分钟
通常我们会用人力成本换取时间成本,加几个人一起执行,时间就会缩短
如果10人一起执行只需要100分钟,这就是一种分布式场景
场景2:
假设有个报名系统,对报名总数统计,数据同时进行修改操作的时候有可能出现问题
需要模拟这个场景,需要多用户并发请求数据
用例较多时,效果明显
解决:
使用分布式并发执行测试用例,分布式插件:pytest-xdist
安装:pip install pytest-xdist
运行:
1、pytest -s -n auto
参数 -n auto 自动检测到系统的cpu数量,此时所有的cpu都来执行测试用例
2、pytest -s -n 2,指定执行cpu数

分布式执行测试用例原则

1、用例之间是独立的,不要有依赖关系
2、用例执行没有顺序,随机顺序都能正常秩序
3、每个用例都能重复运行,运行结果不回影响其他用例

pytest内置插件hook体系【进阶】

pytest插件开发【进阶】