pytest框架

pytest测试框架

单元测试框架定义:针对软件开发最小的单元(函数,方法)进行正确性位置测试

单元测试框架:java(junit,testing)python(unittest,pytest)

单元测试框架的作用

  1. 测试发现:从多个文件中找到测试用例
  2. 测试执行:按照一定的顺序和规则去执行并且生成结果
  3. 测试判断:通过断言判断预期结果和实际结果的差异
  4. 测试报告:统计测试的结果,耗时,通过率等生成美观的测试报告

自动化框架的内容:单元测试框架,pom设计模式,数据驱动,关键字驱动,全局配置文件的封装,日志监控,selenium和request二次封装,断言,报告邮件,更多...

pytest简介和安装

  1. pytest比unittest更灵活,容易上手
  2. pytest可以和selenium,reques,appnium结合实现web自动化,接口自动化,app自动化
  3. pytest可以实现测试用例的跳过和reruns失败用例重试
  4. pytest可以和allure生成测试报告
  5. pytest可以和jenkins持续集成
  6. pytest提供强大的插件

如下:建立requirements.txt 内容如下

复制代码
pytest 本身
pytest-html 生成html格式化报告
pytest-xdist 测试用例分布式进行,多CPU分发
pytest-ordering 用于改变测试用例的执行顺序
pytest-returnfailures 用例失败后重跑
allure-pytest 用于生成美观的测试报告

终端上输入安装:

复制代码
 pip install -r requirements.txt

检验:

复制代码
pytest --version

模块命名

  1. 以test_开头或者 _test结尾
  2. 测试类必须以Test开头并且不再有ini方法
  3. 测试方法必须以test开头

测试用例的运行方式

1.主函数运行方式

(1)运行所有:pytest.main()

复制代码
class  TestLogin:
    def test_01_login(self):
        print("测试登录")

if __name__ == '__main__':
    pytest.main()

(2)运行指定模块:

复制代码
pytest.main(['-vs','test_login.py'])

(3)指定目录:

复制代码
pytest.main(['-vs','./test_other.py'])

(4)通过nodeid指定用例运行:nodeid由模块名,分隔符,类名,函数名组成

复制代码
pytest.main(['-vs','./interface_testcase/test_interface.py::test_04_func'])

2.命令行模式

(1)运行所有:

复制代码
pytest

(2)指定模块:

复制代码
pytest -vs test_login.py

(3)指定目录:

复制代码
pytest -vs ./test_other.py

(4)指定目录:

复制代码
pytest -vs ./interface_testcase/test_interface.py::test_04_func

3.参数详解

-v 输出更详细的信息

-s 输出调试信息

-vs:这两个参数一起用

-n:支持多线程或者分布式运行测试用例

如:pytest -vs ./test_login.py -n 2

--returns NUM:失败用例重跑

-x:表示只要要一个用例报错,那么测试就停止

--maxfall=2 出现两个用例失败就停止

-k:根据测试用例的部分字符串指定测试用例

如:pytest -vs/testcase -k"ao"

复制代码
class  TestLogin:
    def test_01_login(self):
        print("测试登录")

if __name__ == '__main__':
    pytest.main(['-s'])

案例:创建一个all.py文件运行指定的test用例和控制输出的信息

复制代码
import  pytest

if __name__ == '__main__':
    pytest.main(['-vs','test_login.py'])

相当于终端输入

复制代码
pytest -vs test_login.py

3.结合pytest.ini全局配置文件执行

pytest为整个pytest的核心配置文件

  1. 位置:一般放在项目的根目录

  2. 编码:必须是ANSI编码格式,可以使用notepad++修改编码格式

  3. 作用:改变pytest默认行为

  4. 运行规则:不管是主函数的模式运行,命令行模式运行,都会去读取ini文件

    复制代码
    1 [pytest]
    2 #命令行参数
    3 #常见:--html=./reports/report.html --reruns 2
    4 addopts = -vs -m "user_manager or smoke"
    5 #配置执行的用例位置
    6 testpaths = ./testcases
    7 #配置修改默认的模块规则
    8 python_files = test_*.py
    9 #配置修改默认的类规则
    10 python_classes = Test*
    11 #配置修改默认的用例规则
    12 python_functions = test_*
    13 #配置基础路径
    14 base_url = http://www.baidu.com
    15 #标记
    16 markers =
    17 smoke:冒烟测试用例
    18 product_manage:商品管理
    19 user_manager:用户管理

pytest 测试用例的顺序

区分

unittest:ascII的大小来绝对的执行顺序

pytest:默认从上到下

改变默认的执行顺序,使用mark标记

添加注释,其中1是执行的顺序

复制代码
@pytest.mark.run(order=1)

如何分组执行(冒烟、分模块执行、分接口和web执行)

smoke:冒烟用例,分布在各个模块内

添加冒烟标记在.py文件

复制代码
@pytest.mark.smoke

注意要在pytest.ini文件打一下标记

复制代码
 #标记
 markers =
smoke:冒烟测试用例
product_manage:商品管理
user_manager:用户管理

在终端输入

复制代码
pytest -vs -m "smoke"

想执行其他模块

复制代码
pytest -vs -m "smoke and 其他模块的名称"

跳过执行的用例

  1. 无条件跳过

    复制代码
    @pytest.mark,skipif(reason='跳过原因')
  2. 有条件跳过

    复制代码
    @pytest.mark,skipif(条件,reason='跳过原因')

例如

复制代码
@pytest.mark.skipif(age>=18,reason='已成年')

pytest框架的一些前后置(固件,夹具)处理,常用的三种

前后置:setup_class、setup、teardown、teardown_class

python 复制代码
class TestPosition:
    def setUp_class(self):
        print('\n 在每个类执行前的准备工作,例如:创建日志对象、数据库链接、接口请求对象')
    def setup(self):
        print('\n 执行用例之前的开始动作,比如打开浏览器')



    def test_01(self):
        print('\n 测试用例')

    def teardown(self):
        print('\n 执行用例之前的扫尾动作,比如关闭浏览器')
    def teardown_class(self):
        print('\n 在每个类执行前的准备工作,例如:销毁日志对象、数据库链接、接口请求对象')

使用@pytest.fixture()装饰器实现部分用例的前后置

装饰器:

@pytest.fixtrue(scope="作用范围",autouse="自动执行",params="参数化",ids="参数别

名",name="固件别名")

案例:

复制代码
@pytest.fixture(scope="",params="", autouse="",ids="",name="")
def my_fixture():
    print('这是前置的方法,可以实现部分以及全部用例的前置')
    yield
    print('这是后置方法,可以实现部分以及全部用例的后置')

def test_02(self,my_fixture):
    print('\n 执行fixture')

用法:

  1. scope表示的是被@pytest.fixture标记的方法的作用域。function(默认),class,moudle,package/session

  2. params:参数化(支持,列表[],元组(),字典列表[{},{},{}],字典元组({},{},{})

    import pytest

    @pytest.fixture(scope='function',params=['参数1','参数2','参数3'])
    def my_fixture(request):
    print('前置')
    yield request.param
    # 注意yield和return都表示返回的以上,但是return后面不能跟代码,yield可以
    print('后置')

    def test_02(self,my_fixture):
    print('\n 执行fixture')

注意:params和yield不能一起用但是可以和前置一起用。

  1. autouse=True:自动化使用,默认False

  2. ids:当使用params参数化时,给每一个值设置一个变量名,意义不大

  3. name:给表示的是被@pytest.fixture标记的方法取一个别名

    当取了别名之后,那么原来的名就用不了了

通过conftest.py和@pytest.fixture()结合使用实现全局的前置应用(如:项目的全局登录,模块的全局处理)

  1. conftest.py文件是单独存放的一个夹具的配置文件,名称不能更改

  2. 用处可以在不同的py文件中使用同一个fixture函数

  3. 原则上conftest.py需要和运行的用例放在统一层,并且不需要做任何的import导入的操作

    案例

    全局拥有全局前置,各个子模块下面有一个前置,对于用例编写为

    复制代码
    def test_01_testconftest(self,all_fixtuture,user_fixture)
    
    print("test用例")
    
    print(user_fixture)
    
    print(all_fixture)

    这样全局前置包含了子模块的前置

setup/teardown,setup_class/teardown_class 它是作用于所有用例或者所有的类

@pytest.fixture() 它的作用是局部的也是全局的

conftest.py和@pytest_fixture()结合使用,作用于全局的前后置

断言

assert

pytest结合allure+pytest插件生成allure测试报告

以前是使用的是pytest_html生成,现在可以使用allure+pytest结合生成美观的报告

1.下载、解压、配置path路径

https://github.com/allure-framework/allure2/releases

配置环境变量如:

D:\allure-2.30.0\bin

验证

allure --version

2.加入命令到pytest.ini文件,生成json格式的临时报告

复制代码
addopts = -vs --alluredir ./temp

3.生成allure报告

allure generate 命令,固定的

./temp 临时的json格式的报告

-o 输出output

./report 生成allure报告路径

.clear 清空./report路径原来的报告

复制代码
if __name__ == '__main__':
    time.sleep(3)
    # pytest.main(['-vs','test_login.py'])
    pytest.main()
    # allure generate 命令,固定的;./temp 临时的json格式的报告 -o 输出output ./report 生成allure报告路径 .clear 清空./report路径原来的报告
    os.system('allure generate ./temp -o ./report --clear')

yaml接口自动化实战

1,断言的封装

2,allure报告的定制

3,关键字驱动和数据驱动结合实现接口自动化测试

4,python的反射

正常:先初始化对象,再调方法

反射:通过对象得到类对象,然后通过类对象调用方法

5,jenkins的持续集成和allure报告的集成,并且根据项目的自动化的报告错误率发送电子邮件

pytest接口自动化

接口测试

发展背景:

目前市面上主流的接口测试工具:(适用于中小型项目)

  1. postman+newman+git+jenkins
  2. jmeter+ant+git+jenkins

目前主流的接口自动化测试技术:(适用于大小型的项目)

requests库

安装

requests用来发送http请求以及接受http响应的python第三方库,主要用于接口自动化测试

复制代码
pip install requests

requests库常用的方法

requests.get() url是接口的地址,param用于传参

requests.post() url是接口地址,data用于传参,json也适用传参

​ data和json传参的区别:主要是通过请求头Content-Type来区分

​ Content-Type:作用是服务器要求传入的报文的内容类型

​ 请求:请求方式,请求路径,请求头,请求正文

requests.pull()

requests.delete()

requests.request() 可以发送所有类型的请求:get,post,put,delete

拓展:postman的四种传参的方式对应的Content-Type的值如下:

from-data:Content-Type:multipart/form-data、boundary=

x-www-from-urlencoded:Content-Type:applic.ation/x-www-form-urlencoded

raw:

​ text:Content-Type:text/plain

​ javascript:Content-Type:application/javascript

​ json:Content-Type:application/json

​ html:Content-Type:application/xml

binary:Content-Type:application/binary

data和json传参以及Content-Type的关系如下:

1.data传参:

​ data传参:报文时dict类型,那么默认Content-Type application/x-www-form-urlencoded

​ data传参:报文是str类型,那么默认Content-Type text/plain

2.json传参:

​ 报文可以是dict类型,那么默认Content-Type application/json

注意:

  1. json是字典类型的字符串显示。

  2. json.loads() 把json字符串转化为dict格式;json.dumps() 把dict格式转换为json字符串

  3. 不管是get,post,put,delete,都是调用的requests.request方法。而requests.reques方法调用的是session.reques方法

    method 请求方式

    url 请求路径

    params=None get方式传参

    data=None post方式传参

    json=None post方式传参

    headers=None 请求头

    cookie=None 请求cookie

    files=None 文件上传

requests模块返回的response对象详解

  1. res.json() 获取返回的字典格式的数据
  2. res.text 获得返回的字符串格式的数据
  3. res.content 获得返回的bytes字节类型的数据
  4. res_status_code 返回状态码
  5. res.reason 返回状态信息
  6. res.cookies 返回cookie信息
  7. res.headers 返回响应头
  8. res.request.xxx 返回需求的数据,如:需求头,请求参数

请求必须带有请求头的接口,以及需要cookie鉴权和session鉴权的接口

复制代码
<input type="hidden" name="csrf_token" value="e7202bf558a7eb34"/>

90%以上的基于web的接口都有cookie鉴权

两种解决方式:

  1. 使用cookie关联

    案例注释:接口关联变量,在第一个接口取值,在第二个接口传值,第三个接口文件上传,访问首页的接口,解决鉴权

    复制代码
    import requests
    
    
    class TestRequest:
        #全局变量,类变量,通过类名调用
        access_token=""
        csrf_token=""
        php_cookie=""
    
        def test_get_token(self):
            url:"https://api.weixin.qq.com/cgi-bin/token"
            data:{
                "grant_type":"client_credential",
                "appid":"wx74a8627810cfa300",
                "secret":"e40a02f9cf9d79df497e6aaf93ab80"
            }
            res=requests.request(method="get",url=url,params=data)
            print(res.json())
            TestRequest access_token =  res.json()['access_token']
    
        # post请求:编辑标签接口
        def test_edit_flag(self):
            url:"https://api.weixin.qq.com/cgi-bin/tags/updat?access token="+TestRequest.access_token
            data=[
                "tag":
                   {
                       "id":134,
                        "name":"广东人"
                    }
            ]
            str_data=json.dumps(data)
            res=requests.request(method="post",url=url,data=data)
            print(res.json())
    
    
    #         文件上传
    def test_file_upload(self):
        url:"https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token="+TestRequest.access_token"+TestRequest.access_token
        data={
            "media":open(r"E:\shu.png","rb")
        }
        res=requests.request(method="post",url=url,files=data)
        print(res.json())
    
    
    # 访问首页的接口
    def test_start(self):
        url:"https://47.107.116.139/phpwind"
        res=requests.request(method="get",url=url)
        print(res.text)
    # 正则提取token
    obj=re.search('name="csrf_token" value="(.*?)"',res.text)
    # print(obj.group(1))
    TestRequest.csrf_token=obj.group(1)
    # 解决鉴权的方式一:提取cookie
    TestRequest.php_cookie=res.cookies
    
    #     登录接口
    def test_login(self):
        url:"https://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
        data={
            "username":"msxy",
            "password":"msxy",
            "csrf_token":TestRequest.csrf_token,
            "backurl":"http://47.107.116.139/phpwind/",
            "invite":""
        }
        # 请求头
        headers={
            "Accept":"application/json, text/javascript, /; q=0.01",
            "X-Requested-with":"XMLHttpRequest"
        }
        res=requests.request(method="post",url=url,data=data,headers=headers,cookies=TestRequest.php_cookie)
        print(res.json())
    # 解决鉴权的方式一:提取cookie
    TestRequest.php_cookie=res.cookies
    
    
    if __name__ == '__main__':
        TestRequest().test_get_token()
        TestRequest().test_edit_flag()
        TestRequest().test_file_upload()
        TestRequest().test_start()
        TestRequest().test_login()
  2. 使用session关联

    复制代码
    import requests
    
    
    class TestRequest:
        #全局变量,类变量,通过类名调用
        access_token=""
        csrf_token=""
        # php_cookie=""
        sess=requests.session()
    
        def test_get_token(self):
            url:"https://api.weixin.qq.com/cgi-bin/token"
            data:{
                "grant_type":"client_credential",
                "appid":"wx74a8627810cfa300",
                "secret":"e40a02f9cf9d79df497e6aaf93ab80"
            }
            res=requests.request(method="get",url=url,params=data)
            print(res.json())
            TestRequest access_token =  res.json()['access_token']
    
        # post请求:编辑标签接口
        def test_edit_flag(self):
            url:"https://api.weixin.qq.com/cgi-bin/tags/updat?access token="+TestRequest.access_token
            data=[
                "tag":
                   {
                       "id":134,
                        "name":"广东人"
                    }
            ]
            str_data=json.dumps(data)
            res=requests.request(method="post",url=url,data=data)
            print(res.json())
    
    
    #         文件上传
    def test_file_upload(self):
        url:"https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token="+TestRequest.access_token"+TestRequest.access_token
        data={
            "media":open(r"E:\shu.png","rb")
        }
        res=requests.request(method="post",url=url,files=data)
        print(res.json())
    
    
    # 访问首页的接口
    def test_start(self):
        url:"https://47.107.116.139/phpwind"
        res=TestRequest.sess.request(method="get",url=url)
    
        # print(res.text)
    # 正则提取token
    obj=re.search('name="csrf_token" value="(.*?)"',res.text)
    # print(obj.group(1))
    TestRequest.csrf_token=obj.group(1)
    
    #     登录接口
    def test_login(self):
        url:"https://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
        data={
            "username":"msxy",
            "password":"msxy",
            "csrf_token":TestRequest.csrf_token,
            "backurl":"http://47.107.116.139/phpwind/",
            "invite":""
        }
        # 请求头
        headers={
            "Accept":"application/json, text/javascript, /; q=0.01",
            "X-Requested-with":"XMLHttpRequest"
        }
        res=TestRequest.sess.request(method="post",url=url,data=data,headers=headers)
        print(res.json())
    
    
    
    if __name__ == '__main__':
        TestRequest().test_get_token()
        TestRequest().test_edit_flag()
        TestRequest().test_file_upload()
        TestRequest().test_start()
        TestRequest().test_login()
相关推荐
阿阳微客22 分钟前
Steam 搬砖项目深度拆解:从抵触到真香的转型之路
前端·笔记·学习·游戏
Chef_Chen5 小时前
从0开始学习R语言--Day18--分类变量关联性检验
学习
键盘敲没电5 小时前
【IOS】GCD学习
学习·ios·objective-c·xcode
海的诗篇_6 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
AgilityBaby6 小时前
UE5 2D角色PaperZD插件动画状态机学习笔记
笔记·学习·ue5
AgilityBaby6 小时前
UE5 创建2D角色帧动画学习笔记
笔记·学习·ue5
武昌库里写JAVA8 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
一弓虽9 小时前
git 学习
git·学习
Moonnnn.11 小时前
【单片机期末】串行口循环缓冲区发送
笔记·单片机·嵌入式硬件·学习
viperrrrrrrrrr711 小时前
大数据学习(131)-Hive数据分析函数总结
大数据·hive·学习