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-rerunfailures 用例失败后重跑
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格式,也可以传str格式(如果是嵌套字典那么就需要使用json.dumps()把嵌套字典转化为json字符串)

    json.dumps() 把dict格式转换为json字符串(包括嵌套的dict)

    不管是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=str_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=str_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()

pytest接口自动化-进阶篇

目标:python+requests+pytest+allure+jenkins

接口自动化测试框架关于接口关联的封装

策略:去掉全局变量,用yaml文件代替保存下载pyyaml的包(搜搜)

复制代码
import os
import yaml



# 读取
def read_yaml():
    with open(os.getcwd()+'/extract.yaml',mode='r',encoding='utf-8') as f:
        value = yaml.load(stream=f, Loader=yaml.FullLoader)
        return value


# 写入
def write_yaml(data):
    with open(os.getcwd()+'/extract.yaml', mode='a',encoding='utf-8') as f:
        yaml.dump(data, stream=f,allow_unicode=True)

# 清空
def clear_yaml(data):
    with open(os.getcwd()+'/extract.yaml', mode='w',encoding='utf-8') as f:
        f.truncate()

关于接口自动化框架之统一请求封装

如果两个测试用例要使用同一个session怎么办

  1. 通过同一个session发送请求

  2. 方便统计用例。日志封装

    import requests

    关于接口自动化框架之统一请求封装

    class RequestUtil:
    # 全局变量 类变量 通过类名调用
    sess=requests.session()

    复制代码
     def send_request(self, url, method, datas=None, **kwargs):
         method=str(method).lower()
         res=None
         if method=='get':
             res=RequestUtil.sess.request(method=method,url=url,params=datas,**kwargs)
         elif method=='post':
             if datas and isinstance(datas,dict):
                 datas=json.dumps(datas)
    
             res=RequestUtil.sess.request(method=method,url=url,datas=datas,**kwargs)
         else:
             pass
         return  res

P18接口自动化框架封装之Yaml数据驱动封装

步骤

1.将接口的数据的格式按照这种方式书写:

复制代码
-
  name:
    request:
      method: get
      url: https://api.weixin.qq.com/cgi-bin/token
      data:
        grant_type: client_crediential
        appid: wx6b11b3efd1cdc290
        secret: 106a9c61571dfdf55f6255cdgs5622
  validate: None

2.在yaml_util新增一个读取测试用例的方法

复制代码
# 读取测试用例的方法
def read_testcase(yaml_name):
    with open(os.getcwd()+'/testcases/'+yaml_case,mode='r',encoding='utf-8') as f:
        value = yaml.load(stream=f, Loader=yaml.FullLoader)
        return value

3.在test_api.py 通过装饰器读取字典

如:

复制代码
@pytest.mark.smoke
@pytest.mark.parametrize("args_name",read_testcase('get_token.yaml'))
def test_get_token(self,args_name):
	url=args_name['request']['url']
	data=args_name['request']['data']
	method=args_name['request']['method']
	res=RequestUtil().send_request(method=method,url=url,datas=data )
相关推荐
FINE!(正在努力!)2 天前
PyTest框架学习
学习·pytest
程序员杰哥2 天前
接口自动化测试之pytest 运行方式及前置后置封装
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
测试老哥3 天前
Pytest+Selenium UI自动化测试实战实例
自动化测试·软件测试·python·selenium·测试工具·ui·pytest
水银嘻嘻3 天前
07 APP 自动化- appium+pytest+allure框架封装
python·appium·自动化·pytest
天才测试猿3 天前
接口自动化测试之pytest接口关联框架封装
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
not coder4 天前
Pytest Fixture 详解
数据库·pytest
not coder5 天前
pytest 常见问题解答 (FAQ)
开发语言·python·pytest
程序员的世界你不懂5 天前
(1)pytest简介和环境准备
pytest
not coder5 天前
Pytest Fixture 是什么?
数据库·oracle·pytest
Tester_孙大壮5 天前
pytest中的元类思想与实战应用
pytest