zoukankan      html  css  js  c++  java
  • 【python自学】Python接口自动化测试(转)


     

    写在前面:

    接口测试之前一直使用jmeter来测试,单个接口还好,多个接口的时候,需要传入各种各样的值,感觉工作量挺大的。

    最近学习python,其他java也可以实现,鉴于团队中使用python的人员比较多,所以就想着尝试搭建个python接口自动化的框架。

    对于python而言,我就是个小白,正在不断的充电中。。。

    之前使用java+selenium+TestNG+log4j +Jenkins自己搭建的UI自动化框架很好用,前两天一个项目,三四天时间就把基本流程和功能写完,

    帮助自己减少了不少重复工作量。

    今天呢,也想通过学习python自动化框架+requests+uinitest(或其他更好的框架),来让接口测试更有条不紊。

    加油吧,少年!


    转自:https://www.cnblogs.com/zhengyao9236/p/11122446.html

    1)环境准备:

      接口测试的方式有很多,比如可以用工具(jmeter,postman)之类,也可以自己写代码进行接口测试,工具的使用相对来说都比较简单,重点是要搞清楚项目接口的协议是什么,然后有针对性的进行选择,甚至当工具不太适合项目时需要自己进行开发。

      在我们项目的初期,我们采用的是jmeter进行接口测试,当时觉得这个工具上手简单,团队成员学习成本低,并且接口测试的脚本稍微调整一下还可以用来做性能测试。

      不过随着项目规模、团队人数的不断增长,渐渐的这个工具有适应不了当前项目的需求了,为此我们项目也重新开发了相关接口自动化的平台。但是,但是。。。可能是我让大家中毒太深,现在很多同学一提到接口测试关联到jmeter,为此,我深深感到不安。毕竟jmeter只是个工具,换个项目换个协议你是否还能玩转接口测试呢?session和cookie有什么区别?工具又是怎么实现的呢?

      比如session如何保存,接口依赖如何处理,case如何管理及执行顺序,测试数据如何管理等等题,这个过程也有助于我们更加深刻的理解接口测试和http协议。

      本文主要采用python语言,python中http协议接口相关的库有urllib,urllib2以及reqeusts库,这其中reqeusts库用来起来最方便,因此我也主要采用requests库来做http协议的接口测试。首先来看下需要哪些环境信息:

    一、安装python

      mac下自带安装了python,这个不多说了。

    二、安装虚拟环境:

    我们在一台机器上可以安装多个python版本,为了使每个版本的环境相互不受干扰,可以安装虚拟环境,安装方法如下:

    1、安装virtualenv:pip install virtualenv

    2、新建名为venv的虚拟环境:virtualenv venv

    3、进入新环境:source venv/bin/activate

    4、退出:deactivate

    三、安装requests库:

    >>>pip install requests

    ps:用python做http协议的接口测试会用到这个库。

    四、http测试工具:

    一个使用 Python + Flask 编写的 HTTP 请求和响应服务,该服务主要用于测试 HTTP 库。后续测试我们都基于这个网站。

    http://httpbin.org

    五、在本地搭建httpbin:

    考虑到测试时要不断访问 httpbin 网站,请求过多担心被拉到黑名单,我们自己在本志搭建一套httpbin服务。

    1、安装:pip install gunicorn

    2、安装:pip install httpbin

    3、启动:gunicorn httpbin:app

    至此,环境搭建已经完毕,可以开始玩了~

    (2)requests.get()

    环境搭建好后,接下来我们先来了解一下requests的一些简单使用,主要包括:

    1. requests常用请求方法使用,包括:get,post
    2. requests库中的Session、Cookie的使用
    3. 其它高级部分:认证、代理、证书验证、超时配置、错误异常处理等。

    本节首先来了解一下requests库中如何发送get请求:

    一、看下方法定义:

    1、到官方文档去了下requests.get()方法的定义,如下:

    2、点击右上角的【source】,看一下它的源码如下:

    看到最后一行return,get方法最后是通过调用 requests.request 方法实现的,其实在其它的请求方法如post,put,head,delete等方法都是调用的request方法,然后把请求方法的类型传递给request方法第一个参数。

    3、HTTP协议是一个基于请求/响应模式的、无状态的,应用层协议。既然有请求,就有响应,来看下resquest中常用的响应信息:

    二、get方法简单使用: 

    1、不带参数的get:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    #不带参数的get
    
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "get"
    
    url = ''.join([host,endpoint])
    r = requests.get(url)
    #response = r.json()
    
    print type(r.text)
    print (eval(r.text))
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
        'origin': '183.14.133.88',
        'headers': {
            'Connection': 'close',
            'Host': 'httpbin.org',
            'Accept-Encoding': 'gzip,
            deflate',
            'Accept': '*/*',
            'User-Agent': 'python-requests/2.18.1'
        },
        'args': {
            
        },
        'url': 'http: //httpbin.org/get'
    }
    复制代码
    复制代码

    2、 带参数的get:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    #带参数的get
    
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "get"
    
    url = ''.join([host,endpoint])
    params = {"show_env":"1"}
    r = requests.get(url=url,params=params)
    
    print r.url
    复制代码
    复制代码

     输出:

    复制代码
    复制代码
    http://httpbin.org/get?show_env=1
    {
        'origin': '183.14.133.88',
        'headers': {
            'X-Request-Id': 'ebe922b4-c463-4fe9-9faf-49748d682fd7',
            'Accept-Encoding': 'gzip,
            deflate',
            'X-Forwarded-Port': '80',
            'Total-Route-Time': '0',
            'Connection': 'close',
            'Connect-Time': '0',
            'Via': '1.1vegur',
            'X-Forwarded-For': '183.14.133.88',
            'Accept': '*/*',
            'User-Agent': 'python-requests/2.18.1',
            'X-Request-Start': '1504755961007',
            'Host': 'httpbin.org',
            'X-Forwarded-Proto': 'http'
        },
        'args': {
            'show_env': '1'
        },
        'url': 'http: //httpbin.org/get?show_env=1'
    }
    复制代码
    复制代码

    3、带header的get:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "get"
    
    url = ''.join([host,endpoint])
    headers = {"User-Agent":"test request headers"}
    
    r = requests.get(url)
    r = requests.get(url,headers=headers)
    #response = r.json()
    print (eval(r.text))['headers']['User-Agent']
    复制代码
    复制代码

    输出:

    test request headers

    4、同时带参数和header:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "get"
    
    url = ''.join([host,endpoint])
    headers = {"User-Agent":"test request headers"}
    params = {"show_env":"1"}
    
    r = requests.get(url)
    r = requests.get(url,headers=headers,params=params)
    
    #response = r.json()
    print (eval(r.text))['headers']['User-Agent']
    print r.url
    复制代码
    复制代码

    输出:

    test request headers
    http://httpbin.org/get?show_env=1

    (3)requests.post()

    一、方法定义

    二、post方法简单使用

      1、带数据的post

      2、带header的post

      3、带json的post

      4、带参数的post

      5、普通文件上传

      6、定制化文件上传

      7、多文件上传

    一、方法定义:

    1、到官方文档去了下requests.post()方法的定义,如下:

    2、源码:

    3、常用返回信息:

    二、post方法简单使用:

     1、带数据的post:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    url = ''.join([host,endpoint])
    data = {'key1':'value1','key2':'value2'}
    
    r = requests.post(url,data=data)
    #response = r.json()
    print (r.text)
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
      "args": {}, 
      "data": "", 
      "files": {}, 
      "form": {
        "key1": "value1", 
        "key2": "value2"
      }, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Content-Length": "23", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.18.1"
      }, 
      "json": null, 
      "origin": "183.14.133.88", 
      "url": "http://httpbin.org/post"
    }
    复制代码
    复制代码

    2、带header的post:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    
    url = ''.join([host,endpoint])
    headers = {"User-Agent":"test request headers"}
    
    # r = requests.post(url)
    r = requests.post(url,headers=headers)
    #response = r.json()
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
      "args": {}, 
      "data": "", 
      "files": {}, 
      "form": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Content-Length": "0", 
        "Host": "httpbin.org", 
        "User-Agent": "test request headers"
      }, 
      "json": null, 
      "origin": "183.14.133.88", 
      "url": "http://httpbin.org/post"
    }
    复制代码
    复制代码

    3、带json的post:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    
    url = ''.join([host,endpoint])
    data = {
        "sites": [
                    { "name":"test" , "url":"www.test.com" },
                    { "name":"google" , "url":"www.google.com" },
                    { "name":"weibo" , "url":"www.weibo.com" }
        ]
    }
    
    r = requests.post(url,json=data)
    # r = requests.post(url,data=json.dumps(data))
    response = r.json()
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
      "args": {}, 
      "data": "{"sites": [{"url": "www.test.com", "name": "test"}, {"url": "www.google.com", "name": "google"}, {"url": "www.weibo.com", "name": "weibo"}]}", 
      "files": {}, 
      "form": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Content-Length": "140", 
        "Content-Type": "application/json", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.18.1"
      }, 
      "json": {
        "sites": [
          {
            "name": "test", 
            "url": "www.test.com"
          }, 
          {
            "name": "google", 
            "url": "www.google.com"
          }, 
          {
            "name": "weibo", 
            "url": "www.weibo.com"
          }
        ]
      }, 
      "origin": "183.14.133.88", 
      "url": "http://httpbin.org/post"
    }
    复制代码
    复制代码

    4、带参数的post:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    
    url = ''.join([host,endpoint])
    params = {'key1':'params1','key2':'params2'}
    
    # r = requests.post(url)
    r = requests.post(url,params=params)
    #response = r.json()
    print (r.text)
    复制代码
    复制代码

    输出:

    {
    "args": {
    "key1": "params1", 
    "key2": "params2"
    }, 
    "data": "", 
    "files": {}, 
    "form": {}, 
    "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "close", 
    "Content-Length": "0", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.1"
    }, 
    "json": null, 
    "origin": "183.14.133.88", 
    "url": "http://httpbin.org/post?key2=params2&key1=params1"
    }

    5.普通文件上传:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    
    url = ''.join([host,endpoint])
    #普通上传
    files = {
                'file':open('test.txt','rb')
            }
    
    r = requests.post(url,files=files)
    print (r.text)
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
      "args": {}, 
      "data": "", 
      "files": {
        "file": "hello world!
    "
      }, 
      "form": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Content-Length": "157", 
        "Content-Type": "multipart/form-data; boundary=392865f79bf6431f8a53c9d56c62571e", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.18.1"
      }, 
      "json": null, 
      "origin": "183.14.133.88", 
      "url": "http://httpbin.org/post"
    }
    复制代码
    复制代码

    6.定制化文件上传:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    
    url = ''.join([host,endpoint])
    #自定义文件名,文件类型、请求头
    files = {
            'file':('test.png',open('test.png','rb'),'image/png')
    }
    
    r = requests.post(url,files=files)
    print (r.text)heman793
    复制代码
    复制代码

    7.多文件上传:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    
    url = ''.join([host,endpoint])
    #多文件上传
    files = [
        ('file1',('test.txt',open('test.txt', 'rb'))),
        ('file2', ('test.png', open('test.png', 'rb')))
        ]
    
    r = requests.post(url,files=files)
    print (r.text)
    复制代码
    复制代码

    8.流式上传:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "post"
    
    url = ''.join([host,endpoint])
    
    #流式上传
    with open( 'test.txt' ) as f:
        r = requests.post(url,data = f)
    
    print (r.text)
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
      "args": {}, 
      "data": "hello world!
    ", 
      "files": {}, 
      "form": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Content-Length": "13", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.18.1"
      }, 
      "json": null, 
      "origin": "183.14.133.88", 
      "url": "http://httpbin.org/post"
    }
    复制代码
    复制代码

    (4)Cookie&Session

    掌握了前面几节的的内容,就可以做一些简单的http协议接口的请求发送了,但是这些还不够。HTTP协议是一个无状态的应用层协议,也就是说前后两次请求是没有任何关系的,那如果我们测试的接口之前有相互依赖关系怎么办呢(比如我要在博客园发文章,是需要先登录的),这时我们就要用到cookie和session技术来保持客户端与服务器端连接的状态,这也就是本节要介绍的内容:

    一、Cookie:

    1、获取cookie:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    #获取cookie
    import requests
    import json
    
    url = "https://www.baidu.com/"
    r = requests.get(url)
    
    #将RequestsCookieJar转换成字典
    c = requests.utils.dict_from_cookiejar(r.cookies)
    
    print r.cookies
    print c
    
    for a in r.cookies:
        print a.name,a.value
    复制代码
    复制代码

    输出:

    <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
    {'BDORZ': '27315'}
    BDORZ 27315

    2、发送Cookie

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    #发送cookie到服务器
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "cookies"
    
    url = ''.join([host,endpoint])
    #方法一:简单发送
    # cookies = {"aaa":"bbb"}
    # r = requests.get(url,cookies=cookies)
    # print r.text
    
    #方法二:复杂发送
    s = requests.session()
    c = requests.cookies.RequestsCookieJar()
    c.set('c-name','c-value',path='/xxx/uuu',domain='.test.com')
    s.cookies.update(c) 
    复制代码
    复制代码

    二、Session

    1、保持会话同步:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "cookies"
    
    url = ''.join([host,endpoint])
    url1 = "http://httpbin.org/cookies/set/sessioncookie/123456789"
    
    r = requests.get(url)
    print r.text
    
    print "------"
    
    
    s = requests.session()    #初始化一个session对象
    s.get(url1)               #cookie的信息存在了session中
    r = s.get(url)
    
    print r.text
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
      "cookies": {}
    }
    
    ------
    {
      "cookies": {
        "sessioncookie": "123456789"
      }
    }
    复制代码
    复制代码

    2、保存绘画信息:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "headers"
    
    url = ''.join([host,endpoint])
    
    header1 = {"testA":"AAA"}
    header2 = {"testB":"BBB"}
    
    s = requests.session()    #初始化一个session对象
    s.headers.update(header1)   #已经存在于服务中的信息
    r = s.get(url,headers=header2) #发送新的信息
    
    print r.text
    复制代码
    复制代码

    输出:

    复制代码
    复制代码
    {
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Host": "httpbin.org", 
        "Testa": "AAA", 
        "Testb": "BBB", 
        "User-Agent": "python-requests/2.18.1"
      }
    }
    复制代码
    复制代码

    3.删除已存在的会话信息,保存为None

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    import json
    
    host = "http://httpbin.org/"
    endpoint = "headers"
    
    url = ''.join([host,endpoint])
    
    header1 = {"testA":"AAA"}
    header2 = {"testB":"BBB"}
    
    s = requests.session()    #初始化一个session对象
    s.headers.update(header1)   #已经存在于服务中的信息
    r = s.get(url,headers=header2) #发送新的信息
    
    print r.text
    
    print '--------'
    
    s.headers['testA'] = None   #删除会话里的信息testA
    r1 = s.get(url,headers = header2)
    print r1.text
    复制代码
    复制代码
    复制代码
    复制代码
    {
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Host": "httpbin.org", 
        "Testa": "AAA", 
        "Testb": "BBB", 
        "User-Agent": "python-requests/2.18.1"
      }
    }
    
    --------
    {
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Connection": "close", 
        "Host": "httpbin.org", 
        "Testb": "BBB", 
        "User-Agent": "python-requests/2.18.1"
      }
    }
    复制代码
    复制代码

    4、提供默认数据:

    复制代码
    复制代码
    s = requests.Session()
    s.auth = ('user', 'pass')
    s.headers.update({'x-test': 'true'})
    
    # both 'x-test' and 'x-test2' are sent
    s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
    复制代码
    复制代码

    参考:

    http://docs.python-requests.org/en/master/user/quickstart/#cookies
    http://docs.python-requests.org/en/master/user/advanced/#session-objects

    (5)其他(认证&代理&超时设置)

    一、认证

    1、基本认证:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    import requests
    
    url = "http://httpbin.org/basic-auth/user/passwd"
    
    r1 = requests.get(url)
    print "未提供用户名密码:" + str(r1.status_code)
    
    #Basic Authentication
    r2 = requests.get(url,auth=('user','passwd'))
    print "已提供用户名密码:" + str(r2.status_code)
    复制代码
    复制代码

    输出:

    未提供用户名密码:401
    已提供用户名密码:200

    2、数字认证:

    >>> from requests.auth import HTTPDigestAuth
    >>> url = 'http://httpbin.org/digest-auth/auth/user/pass'
    >>> requests.get(url, auth=HTTPDigestAuth('user', 'pass'))
    <Response [200]>

    3、OAuth认证:

      参考:http://docs.python-requests.org/en/master/user/authentication/

    二、代理

    1、方法一:proxy参数:

    复制代码
    复制代码
    import requests
    
    proxies = {
      "https": "http://41.118.132.69:4433"
    }
    r = requests.post("http://httpbin.org/post", proxies=proxies)
    print r.text
    复制代码
    复制代码

    2、方法二:设置环境变量:

    复制代码
    复制代码
    $ export HTTP_PROXY="http://10.10.1.10:3128"
    $ export HTTPS_PROXY="http://10.10.1.10:1080"
    
    $ python
    >>> import requests
    >>> requests.get('http://example.org')
    复制代码
    复制代码

    3、HTTP Basic Auth使用代理方法:http://user:password@host/

    proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}

    三、证书验证

    1、SSL证书(HTTPS):

    import requests
    
    #跳过12306 的证书验证,把 verify 设置为 False: 
    r = requests.get('https://kyfw.12306.cn/otn/', verify=False)
    print r.text

    2、客户端证书:

    >>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
    <Response [200]>

    or

    s = requests.Session()
    s.cert = '/path/client.cert'

    四、超时配置

    1 、利用timeout参数来配置最大请求时间:

    r = requests.get('https://github.com', timeout=5)

    2、设置timeout=None,告诉请求永远等待响应,而不将请求作为超时值传递

    r = requests.get('https://github.com', timeout=None)

    五、错误异常:

    1、所有Requests显式抛出的异常都继承自:requests.exctptions.RequestException

    2、遇到网络问题(如:DNS查询失败,拒绝连接等)时,requests会抛出一个 ConnectionError 异常

    3、遇到罕见的无效HTTP响应时,Request则会抛出一个 HTTPError 异常

    4、若请求超时,则抛出一个 Timeout 异常

    5、若请求超过了最大的重写向次数,则会抛出一个 TooManyRedirects 异常

     

    (6)unittest-单个用例管理:

    上面主要介绍了环境搭建和requests库的使用,可以使用这些进行接口请求的发送。但是如何管理接口案例?返回结果如何自动校验?这些内容光靠上面五节是不行的,因此从本节开始我们引入python单元测试框架 unittest,用它来处理批量用例管理,校验返回结果,初始化工作以及测试完成后的环境复原工作等等。

    一、单个用例管理起来比较简单,参考如下图,单个用例一般多用在调试的时候:

    二、代码如下:

    复制代码
    复制代码
    # -*- coding:utf-8 -*-
    # 单个用例执行
    # 1、导入模块
    import unittest
    
    # 2、继承自unittest.TestCase类
    class TestOne(unittest.TestCase):
        # 3、配置环境:进行测试前的初始化工作
        def setUp(self):
  • 相关阅读:
    可闭环、可沉淀、可持续的企业级数据赋能体系
    案例解读|迁云的灵魂3问,降多少本,增多少效,真平滑否?
    Serverless 实战——使用 Rendertron 搭建 Headless Chrome 渲染解决方案
    从零开始入门 K8s | etcd 性能优化实践
    State Processor API:如何读取,写入和修改 Flink 应用程序的状态
    阿里云叔同:以容器为代表的云原生技术,已成为释放云价值的最短路径
    Flink SQL 如何实现数据流的 Join?
    仅1年GitHub Star数翻倍,Flink 做了什么?
    codeforces div2_603 F. Economic Difficulties(树dfs预处理+dp)
    codeforces div2_604 E. Beautiful Mirrors(期望+费马小定理)
  • 原文地址:https://www.cnblogs.com/conquerorren/p/13041555.html
Copyright © 2011-2022 走看看