1、认识pytest框架
1、搭建自动化框架的思路与流程
1、搭建自动化测试框架的思路和流程,任意测试手段流程都是一致的:手工测试、自动化测试、工具测试
- 手工测试:熟悉业务 —— 写用例 —— 执行用例并记录结果 —— 生成测试报告
- 自动化测试:熟悉业务 —— 写自动化用例(来自于手工测试用例,格式转化为代码) —— 代码表达用例 —— 代码收集测试用例 —— 执行测试用例 —— 代码生成测试报告
2、测试框架(是一种技术栈):是一个现成的框架,区别于自动化测试框架(需要借助测试框架+基于项目实现搭建的针对项目的框架),介绍一下使用最多的2个框架:
- unittest:内置库,不需要安装,不能自动发现测试用例,手动收集用例
- pytest:第三方库,需要安装导入使用:智能自动收集所有用例,使用更广泛
- 安装: pip install pytest
- 导包:import pytest
3. unittest和pytest都是单元测试框架,都可以用来编写测试用例,运行用例,生成报告,实现测试前置和后置等
2、pytest语法
1、编写用例的2种规则(为了确保可以自动识别测试用例)
1、 使用函数格式编写时,函数名字以test_开头才会被识别为pytest测试用例的方法,不然就是普通的函数
2、 测试类的形式 编写测试用例,类名Test开头,类里面方法 test_开头,才会被识别为pytest测试用例的方法
注意:当pytest识别出这个是测试用例后,这个函数前方会有一个小绿三角,点击小绿三角也可以执行用例,如下图所示:
如果没有小三角,可以这样做:File - setting - Tools - Python Integrated Tools - testing配置pytest
2、运行pytest用例
1、运行单个模块用例,右键运行,点击三角符号运行
2、完整项目框架里每个模块单独调用一个py文件管理,需要收集所有模块的用例,一起执行可以在项目的跟目录下创建一个main.py,会运行这个项目底下所有的用例,其原理是:不同模块,不同目录,主要符合命名规则的都会拿过来执行(范围:rootdir)
3、执行自动化测试用例
1、使用 pytest.main()执行所以用例
在项目最外层创建main.py文件,文件内容如下,它会自动在这个文件所在目录收集符合命名规则的文件,符合规则如下:
- 文件名字,以 test_开头、_test开头
- 用例名字:测试用例名字以 test开头,或者类以 Test开头 +test_开头的方法函数名字
注意:pytest用例执行搜索名字时,跟项目文件夹的名字无关
'''
main.py
'''
import pytest
pytest.main() # 收集所有符合pytest语法命名的测试用例
2、执行部分用例的方式(3种方法)
- 修改文件和用例方法的名称
- 指定目录和文件执行,加参数控制,例如:pytest.main([r"testcase\test_01_demo.py"])表示仅执行test_01_demo.py文件
- 加标签【类比手工测试用例的优先级: P1 P2 P3 P4 (important critical major) high medium low】, 加参数过滤用例
- 用例定义的加一个标签,pytest自带: 用装饰器形式:@pytest.mark.p2
- 执行的时候 加参数 -m 标签,如下举例:
使用装饰器标记如下:
class Testdemo:
@pytest.mark.p1 # 添加标记
def test_case02(self):
assert 1 == 10
@pytest.mark.p2 # 添加标记
def test_case03(self):
assert 10 > gen_ran()
def test_case04(self):
assert 20 < gen_ran()
执行时使用-m参数如下:
import pytest
pytest.main(["-m p1 or p2"])
3、pytest用例实战
目录如下:
文件内容:
'''
test_01_demo
'''
import random
import pytest
def test_case_01():
# 在这里写测试用例
excepted = 2
actual = 1
assert actual == excepted
def test_case_02():
return random.randint(1,10)
def gen_ran():
return random.randint(1,100)
ans = gen_ran()
print(ans)
def add():
return 1+3
class Testdemo:
@pytest.mark.p1
def test_case02(self):
assert 1 == 10
@pytest.mark.p2
def test_case03(self):
assert 10 > gen_ran()
def test_case04(self):
assert 20 < gen_ran()
'''
test_02_demo
'''
import random
# 使用方法写测试用例
def test_case_01():
# 在这里写测试用例
excepted = 2
actual = 1
assert actual == excepted
def test_case_02():
a = 10
b = random.randint(20,100)
assert a < b
def gen_ran():
return random.randint(1,100)
ans = gen_ran()
print(ans)
def add():
return 1+3
# 使用类来写测试用例
class Testdemo1:
def test_case02(self):
assert 1 == 10
def test_case03(self):
assert 10 < gen_ran()
def test_case04(self):
assert 20 < gen_ran()
'''
main.py 文件
'''
import pytest
pytest.main()
执行main文件后,查看执行结果:
在单个文件中运行后,可以准确查看用例执行效果,如下:
注意点:有时候运行文件后,不会出现执行结果的小窗口,解决方法如下:
删除之后,再运行就会出现小窗口了,还有问题,那么考虑重新启动pycharm。
2、使用allure生成测试报告
1、概述
allure是一个开源的、独立的展示测试报告的构建工具,可以支持各种语言框架执行测试用例,使用allure会先生成allure可解析的结果文件,从而再去生成具备可读性的html文件。
官网:Allure Framework
安装包下载地址:https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/
备注:安装时,尽量不要放在有中文的磁盘、也不要放在层级目录太多的磁盘,需要配置环境变量,到bin目录即可。
2、安装使用步骤
1、安装
1、windows和mac下载的都是zip包
2、下载后,解压
3、配置环境变量,例如:D:\allure\allure-2.23.1\bin
4、在命令行中,输入: allure --version 查看版本号,如果出现版本号那就安装成功了~、
2、使用
1、allure结合pytest生成测试报告,还需要再按转包给一个第三方库:pip install allure-pytest | pycharm
2、在main文件中添加加参数:"--alluredir=outputs/allure_reports",其中“--alluredir=”部门可以当做固定语法,而“outputs/allure_reports”部分是存放allure生成的文件,是给allure看的哦,我们看不懂。这个文件每次执行都会生成文件,因此,可以添加"--clean-alluredir"参数,清楚以前的文件
3、在这个测试用例文件的rootdir(一般是文件夹目录下)下执行:allure serve .\outputs\allure_reports\,即可生成测试报告(你最近一次执行用例的测试报告),如下演示:
看一下结果展示,查看结果时,不要退出这个进程,即不要输出 Ctrl + C:
点击后,可以看到详细信息~
测试报告只能看到当前的用例执行结果,如果要看连贯的,需要结合Jenkins去做~
3、数据驱动ddt
1、重复的数据用例如何执行
pytest的ddt(数据驱动)解决以下的问题:
1、多条用例被执行的 但是在pytest结果里显示多条用例结果
2、如果第一条用例数据执行失败了 后续的用例依然会再执行的。
举例:有一个列表数据,列表中有3个字典需要跑登录场景的测试用例:
datas = [{"name":"admin1","pwd":"123456","expect":"登录成功"}, {"name":"lemon","pwd":"123456","expect":"用户名错误"}, {"name":"admin","pwd":"1234","expect":"密码错误"}]
那么该如何运行它呢?
有2种方法,第1种是写一个方法,然后遍历这个列表取出每条数据,第2种是写3个方法,分别执行第1、2、3条数据,具体写法如下:
方式1写法:
'''
方式一:写一个方法,然后遍历这个字典
'''
def test_login(datas):
for data in datas:
result = login(data["name"],data["pwd"])
assert result == data["expect"]
方式1结果:
方式二写法:
def test_login01():
result = login(datas[0]["name"], datas[0]["pwd"])
assert result == datas[0]["expect"]
def test_login02():
result = login(datas[1]["name"],datas[1]["pwd"])
assert result == datas[1]["expect"]
def test_login02():
result = login(datas[2]["name"],datas[2]["pwd"])
assert result == datas[2]["expect"]
方式二结果:
总结:方法2可以运行完所有的测试用例,但是过于繁琐,如果有成百上千条用例,那么需要写这么多的函数,实在不是最优解
2、高效执行重复数据的方法
使用数据驱动实现一个方法,执行多条测试数据,使用不同的用例,得到不同的测试结果
1、数据驱动方式一
@pytest.mark.parametrize("变量",测试用例数据) # 用例的方法上面
def test_login_02(变量): # 参数要跟上面的变量名字一致
result = login(变量["name"],变量["pwd"])
assert result == 变量["expect"]
- 依次取到测试用例数据里的每一个元素,赋值给变量
- 变量作为用例方法的参数,依次执行每一条测试用例
- 直到所有用例都执行完了。
- 注意: 变量引号括起来
实战:
@pytest.mark.parametrize("data",datas)
def test_login01(data):
result = login(data["name"], data["pwd"])
assert result == data["expect"]
结果:
2、数据驱动方式二
- 如果数据驱动写在方法上面, 那么只有这个方法可以使用这个数据驱动,另外的方法就不可以用;
- 如果数据驱动写在类上面,那么类下面的所有的方法都可以用。
@pytest.mark.parametrize("变量", 测试用例数据)
class TestLogin:
def test_login_01(self,data):
result = login(data["name"], data["pwd"])
assert result == data["expect"]
def test_login_02(self,data):
result = login(data["name"], data["pwd"])
assert result == data["expect"]
实战:
@pytest.mark.parametrize("data",datas)
class Testlogin():
def test_login02(self,data):
result = login(data["name"],data["pwd"])
assert result == data["expect"]
def test_login03(self,data):
result = login(data["name"],data["pwd"])
assert result == data["expect"]
结果:
总结:使用数据驱动很完美的解决了这个问题
4、夹具(fixture、conftest)
1、为什么引入夹具
在实际项目中,接口或者UI操作肯定不是独立存在的,接口依赖/接口关联其他数据;UI中,点击按钮输入内容之前打开网页等等,因此在自动化中经常要注意这几点:
1、前置条件
1、接口测试中购物车操作之前必须要登录,登录操作是前提
2、UI测试:点击按钮输入内容之前,需要打开浏览器页面,这也是前提
2、后置操作
1、接口测试完成后,需要数据清理,登出
2、UI测试完毕后,需要关闭浏览器
而这些前置和后置操作,在pytest中使用非常灵活,前置和后置统一称为 夹具,前置会在用例执行前执行,后置在用例执行后运行
2、认识pytest中的夹具
备注:夹具可以理解为一个函数
1、定义夹具
pytest定义夹具:在函数名上方添加 “@pytest.fixture()” 即是一个夹具,如:
import pytest
# 定义夹具
@pytest.fixture()
def setup_teardown():
print("我是前置代码")
print("我是后置代码")
2、使用夹具
调用夹具时,在用例方法中调用,直接在方法的括号中,填写夹具函数的名称即可调用,如下:
# 调用夹具
def test_01(setup_teardown):
print("这是测试用例代码......")
查看结果:
3、区分前置和后置关键字 yield
使用yeild可以区分前置和后置,yield之前的代码是前置,之后的代码是后置,如果没有yield区分,那么代码都会认为是前置代码,使用yeild之后的夹具定义如下:
@pytest.fixture()
def setup_teardown():
print("我是前置代码")
yield
print("我是后置代码")
测试夹具及其结果如下:
def test_case01(setup_teardown):
print("这是测试代码")
测试结果如下,测试通过:
4、夹具中的返回值,写在yield后面
如果前置中有返回值需要传递给测试用例,那这些返回值可以添加在yield的后面,如果有多个返回值则用逗号隔开,如下定义:
@pytest.fixture()
def setup_teardown():
print("我是前置代码")
yield
yield"123459","username","是否成功"
print("我是后置代码")
将yeild后的参数进行接收和使用,举例如下:
def test_case01(setup_teardown):
print("这是测试代码")
data = setup_teardown
print("输出yield的返回值类型:",data) # 查看多个参数时的数据类型是什么,最终确定为元组
pwd,name,expect = setup_teardown # 接收这些参数
print(pwd,"=========",name,"========",expect)
查看结果:
以上便是夹具的基本用法
3、在方法和类中使用夹具
1、夹具的默认属性function和扩展属性class
前言:夹具默认的作用域是方法,可以通过给夹具添加参数 scope=class,修改作用域为类范围的作用域,如下修改作用域:
@pytest.fixture(scope="class") #声明这个函数是一个夹具函数,是class级别的夹具
def setup_teardown():
print("我是前置代码")
yield"123459","username","是否成功"
print("我是后置代码")
1、在方法中的使用
1、夹具的作用域为方法
夹具定义:
@pytest.fixture()
def setup_teardown():
print("我是前置代码")
yield"123459","username","是否成功"
print("我是后置代码")
在方法中的使用:
def test_case01(setup_teardown):
print("这是测试代码")
data = setup_teardown
print("输出yield的返回值类型:",data)
pwd,name,expect = setup_teardown
print(pwd,"=========",name,"========",expect)
2、夹具的作用域为类
夹具定义:
@pytest.fixture(scope="class") #声明这个函数是一个夹具函数,是class级别的夹具
def setup_teardown():
print("我是前置代码")
yield"123459","username","是否成功"
print("我是后置代码")
在方法中的使用:
@pytest.mark.usefixtures("setup_teardown") #类级别的夹具,在函数中调用时,可以写在括号中使用,这一行可有可无
def test_case01(setup_teardown):
print("这是测试代码")
pwd,name,expect = setup_teardown
print(pwd,"=========",name,"========",expect)
备注:如果是类夹具,在方法中使用时,依然写在方法的括号中
2、在类中的使用
1、夹具的作用域为方法
夹具定义:
@pytest.fixture()
def setup_teardown():
print("我是前置代码")
yield"123459","username","是否成功"
print("我是后置代码")
在类中使用:
# pytest用例类的形式
class TestDemo:
"""
类的形式调用夹具 直接写在方法括号 ,有其他的参数 用逗号隔开就可以了
如果类下面有多个方法,只有调用的这个夹具的方法才会执行前置和后置。== 默认的情况下
"""
def test_01(self,setup_teardown): #这个方法调用了夹具,夹具只在这个方法中生效
print("这是测试用例1代码..")
def test_02(self):
print("这是测试用例2代码..")
2、夹具的作用域为类
夹具定义:
'''
定义类作用域的夹具
'''
@pytest.fixture(scope="class") #声明这个函数是一个夹具函数,是class级别的夹具
def setup_teardown():
print("我是前置代码")
yield "123459","username","是否成功"
print("我是后置代码")
在类中使用:
'''
有2种方式
方式一:类中的函数不使用夹具中的参数,那么直接在类上方添加@pytest.mark.usefixtures("setup_teardown") 即可
方式二:类中的函数需要使用夹具中的参数,那么,在第一个函数的括号中加夹具的名称即可
'''
'''
方式一举例
'''
@pytest.mark.usefixtures("setup_teardown") # 这样也不行,放在这里方法里取不到返回值
class TestDemo:
"""
类的形式调用夹具 直接写在方法括号 ,有其他的参数 用逗号隔开就可以了
如果类下面有多个方法,只有调用的这个夹具的方法才会执行前置和后置。== 默认的情况下
"""
def test_case01(self):
print("这是第一个类的函数: ",1+2)
def test_case02(self):
print("这是第二个类的函数:",2+2)
'''
方式二举例
'''
# @pytest.mark.usefixtures("setup_teardown") # 可以舍弃,放在这里方法里取不到返回值
class TestDemo:
"""
类的形式调用夹具 直接写在方法括号 ,有其他的参数 用逗号隔开就可以了
如果类下面有多个方法,只有调用的这个夹具的方法才会执行前置和后置。== 默认的情况下
"""
def test_case01(self,setup_teardown):
print("这是第一个类的函数: ",1+2)
pwd, name, expect = setup_teardown
print(pwd, "=========", name, "========", expect)
def test_case02(self,setup_teardown):
print("这是第二个类的函数:",2+2)
pwd, name, expect = setup_teardown
print(pwd, "=========", name, "========", expect)
def test_case03(self,setup_teardown):
print("这是第二个类的函数:",3+2)
pwd, name, expect = setup_teardown
print(pwd, "=========", name, "========", expect)
总结:
夹具的作用域: 默认是函数级别的作用域,是在函数方法前后执行的。
"function"(default): 默认配置,所以夹具默认函数前后执行的。
- - 前置就会在方法用例执行之前执行前置
- 在类里面的方法执行完成之后,再执行后置
- 前置 用例方法 后置
"class": 定义为类级别的夹具
- 前置就会在整个类里面的用例执行之前执行前置
- 在类里面的所有方法都执行完成之后,再执行后置
- 前置 类里所有方法 后置
其他的级别我们不用: "module","package"`` or ``"session"。
扩展了解:--夹具另外一种调用的方式:可以不掌握,重点掌握在方法的括号里调用返回值。
@pytest.mark.usefixtures("setup_teardown") # 调用夹具,但是这种不能直接用返回值
class TestDemo:
@pytest.mark.usefixtures("setup_teardown")
def test_case(setup_teardown):
4、共享夹具
如果测试用例和夹具不在同一个文件里,不可以直接调用的;但是可以导包使用,但是比较麻烦。因此引入夹具的共享: conftest文件管理夹具。这个夹具定义在conftest文件里 ,其他的模块可以自动发现并直接使用不需要导入。文件有2个要求:
1、文件名字必须是conftest 不能改
2、作用范围是 conftest文件所在目录下的所有用例可以自动发现,超过这个文件夹的范围不行了
(不管是否有子文件夹 都可以发现)
如果有多个夹具,使用原则就是就近原则:
1、名字不一样,就按照名字区分
2、名字一样 就近原则选择:
- 第一步:优先用例所在文件里找fixture,找到就用
- 第二步: 第一步里没有找到,就去当前所在目录下conftest.py找。找到了就用
- 第三步: 第二步里没有找到,就去【当前文件所在目录的上一级目录】 下conftest里找,找到了就用
- 一直找到rootdir(根目录)截止,没有报错。
如下举例说明:
1、目录如下,那么在testcase目录下的文件都可以使用conftest中的夹具:
conftest文件内容:
import pytest
# 定义夹具
@pytest.fixture(scope="class") #声明这个函数是一个夹具函数,是class级别的夹具
def setup_teardown():
print("我是前置代码")
yield"123459","username","是否成功"
print("我是后置代码")
新建一个文件夹调用conftest中的夹具:
# from d12_pytest的夹具和pymysql链接.testcase.test_07_conftest import setup_teardown
'''
如果需要夹具共享,那么导包也可以使用
更简单的还是简历conftest文件
'''
def test_01(setup_teardown):
print("这是文件9")
查看结果:
文章来源:https://www.toymoban.com/news/detail-854591.html
扩展
1、用例执行顺序是什么?
- 文件: 名字排序,ASCII顺序,0-9a-zA-Z
- 文件内部: 代码从上到下顺序执行
如果调整用例执行的顺序 按照规则调整即可。文章来源地址https://www.toymoban.com/news/detail-854591.html
到了这里,关于Python —— pytest框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!