赞
踩
1、搭建自动化测试框架的思路和流程,任意测试手段流程都是一致的:手工测试、自动化测试、工具测试
2、测试框架(是一种技术栈):是一个现成的框架,区别于自动化测试框架(需要借助测试框架+基于项目实现搭建的针对项目的框架),介绍一下使用最多的2个框架:
- 安装: pip install pytest
- 导包:import pytest
3. unittest和pytest都是单元测试框架,都可以用来编写测试用例,运行用例,生成报告,实现测试前置和后置等
1、 使用函数格式编写时,函数名字以test_开头才会被识别为pytest测试用例的方法,不然就是普通的函数
2、 测试类的形式 编写测试用例,类名Test开头,类里面方法 test_开头,才会被识别为pytest测试用例的方法
注意:当pytest识别出这个是测试用例后,这个函数前方会有一个小绿三角,点击小绿三角也可以执行用例,如下图所示:
如果没有小三角,可以这样做:File - setting - Tools - Python Integrated Tools - testing配置pytest
1、运行单个模块用例,右键运行,点击三角符号运行
2、完整项目框架里每个模块单独调用一个py文件管理,需要收集所有模块的用例,一起执行可以在项目的跟目录下创建一个main.py,会运行这个项目底下所有的用例,其原理是:不同模块,不同目录,主要符合命名规则的都会拿过来执行(范围:rootdir)
在项目最外层创建main.py文件,文件内容如下,它会自动在这个文件所在目录收集符合命名规则的文件,符合规则如下:
注意:pytest用例执行搜索名字时,跟项目文件夹的名字无关
- '''
- main.py
- '''
-
- import pytest
- pytest.main() # 收集所有符合pytest语法命名的测试用例
使用装饰器标记如下:
- 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"])
目录如下:
文件内容:
- '''
- 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。
allure是一个开源的、独立的展示测试报告的构建工具,可以支持各种语言框架执行测试用例,使用allure会先生成allure可解析的结果文件,从而再去生成具备可读性的html文件。
安装包下载地址:https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/
备注:安装时,尽量不要放在有中文的磁盘、也不要放在层级目录太多的磁盘,需要配置环境变量,到bin目录即可。
1、windows和mac下载的都是zip包
2、下载后,解压
3、配置环境变量,例如:D:\allure\allure-2.23.1\bin
4、在命令行中,输入: allure --version 查看版本号,如果出现版本号那就安装成功了~、
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去做~
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可以运行完所有的测试用例,但是过于繁琐,如果有成百上千条用例,那么需要写这么多的函数,实在不是最优解
使用数据驱动实现一个方法,执行多条测试数据,使用不同的用例,得到不同的测试结果
- @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"]
结果:
- - 如果数据驱动写在方法上面, 那么只有这个方法可以使用这个数据驱动,另外的方法就不可以用;
- - 如果数据驱动写在类上面,那么类下面的所有的方法都可以用。
- @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"]
结果:
总结:使用数据驱动很完美的解决了这个问题
在实际项目中,接口或者UI操作肯定不是独立存在的,接口依赖/接口关联其他数据;UI中,点击按钮输入内容之前打开网页等等,因此在自动化中经常要注意这几点:
1、前置条件
1、接口测试中购物车操作之前必须要登录,登录操作是前提
2、UI测试:点击按钮输入内容之前,需要打开浏览器页面,这也是前提
2、后置操作
1、接口测试完成后,需要数据清理,登出
2、UI测试完毕后,需要关闭浏览器
而这些前置和后置操作,在pytest中使用非常灵活,前置和后置统一称为 夹具,前置会在用例执行前执行,后置在用例执行后运行
备注:夹具可以理解为一个函数
- import pytest
-
- # 定义夹具
- @pytest.fixture()
- def setup_teardown():
- print("我是前置代码")
- print("我是后置代码")
调用夹具时,在用例方法中调用,直接在方法的括号中,填写夹具函数的名称即可调用,如下:
- # 调用夹具
-
- def test_01(setup_teardown):
- print("这是测试用例代码......")
查看结果:
使用yeild可以区分前置和后置,yield之前的代码是前置,之后的代码是后置,如果没有yield区分,那么代码都会认为是前置代码,使用yeild之后的夹具定义如下:
- @pytest.fixture()
- def setup_teardown():
- print("我是前置代码")
- yield
- print("我是后置代码")
测试夹具及其结果如下:
- def test_case01(setup_teardown):
- print("这是测试代码")
测试结果如下,测试通过:
如果前置中有返回值需要传递给测试用例,那这些返回值可以添加在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)
查看结果:
以上便是夹具的基本用法
前言:夹具默认的作用域是方法,可以通过给夹具添加参数 scope=class,修改作用域为类范围的作用域,如下修改作用域:
- @pytest.fixture(scope="class") #声明这个函数是一个夹具函数,是class级别的夹具
- def setup_teardown():
- print("我是前置代码")
- yield"123459","username","是否成功"
- print("我是后置代码")
夹具定义:
- @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)
夹具定义:
- @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)
备注:如果是类夹具,在方法中使用时,依然写在方法的括号中
夹具定义:
- @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代码..")
夹具定义:
- '''
- 定义类作用域的夹具
- '''
- @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):
如果测试用例和夹具不在同一个文件里,不可以直接调用的;但是可以导包使用,但是比较麻烦。因此引入夹具的共享: 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")
查看结果:
1、用例执行顺序是什么?
- 文件: 名字排序,ASCII顺序,0-9a-zA-Z
- 文件内部: 代码从上到下顺序执行
如果调整用例执行的顺序 按照规则调整即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。