<wbr id="juant"></wbr>
  • <wbr id="juant"></wbr>
    更多課程 選擇中心


    Python培訓

    400-111-8989

    基于 python 的接口自動化測試

    • 發布:Python培訓
    • 來源:Python練習題庫
    • 時間:2017-07-28 17:53

    一、簡介

    本文從一個簡單的登錄接口測試入手,一步步調整優化接口調用姿勢;

    然后簡單討論了一下接口測試框架的要點;

    最后介紹了一下我們目前正在使用的接口測試框架 pithy。

    期望讀者可以通過本文對接口自動化測試有一個大致的了解。

    二、引言

    為什么要做接口自動化測試?

    在當前互聯網產品迭代頻繁的背景下,回歸測試的時間越來越少,很難在每個迭代都對所有功能做完整回歸。

    但接口自動化測試因其實現簡單、維護成本低,容易提高覆蓋率等特點,越來越受重視。

    為什么要自己寫框架呢?

    使用 requets + unittest 很容易實現接口自動化測試,而且 requests 的api已經非常人性化,非常簡單。

    但通過封裝以后(特別是針對公司內特定接口),再加上對一些常用工具的封裝,可以進一步提高業務腳本編寫效率。

    三、環境準備

    確保本機已安裝 python2.7 以上版本,然后安裝如下庫:

    pip install flask

    pip install requests

    后面我們會使用 flask 寫一個用來測試的接口,使用requests去測試。

    四、測試接口準備

    下面使用 flask 實現兩個 http 接口,一個登錄,另外一個查詢詳情,但需要登錄后才可以,新建一個 demo.py 文件(注意,不要使用windows記事本),把下面代碼 copy 進去,然后保存、關閉。

    接口代碼

    #!/usr/bin/python# coding=utf-8from flask import Flask, request, session, jsonify

    USERNAME = 'admin'PASSWORD = '123456'app = Flask(__name__)

    app.secret_key = 'pithy'@app.route('/login', methods=['GET', 'POST'])def login():

    error = None

    if request.method == 'POST': if request.form['username'] != USERNAME:

    error = 'Invalid username'

    elif request.form['password'] != PASSWORD:

    error = 'Invalid password'

    else:

    session['logged_in'] = True

    return jsonify({'code': 200, 'msg': 'success'}) return jsonify({'code': 401, 'msg': error}), 401@app.route('/info', methods=['get'])def info():

    if not session.get('logged_in'): return jsonify({'code': 401, 'msg': 'please login !!'}) return jsonify({'code': 200, 'msg': 'success', 'data': 'info'})if __name__ == '__main__':

    app.run(debug=True)

    最后執行如下命令:

    python demo.py

    響應如下:

    * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

    * Restarting with stat

    大家可以看到服務已經起來了。

    接口信息

    登錄接口

    請求url

    /login

    請求方法

    post

    請求參數

    | 參數名稱 | 參數類型 | 參數說明 |

    | :————: | :——-: | :——: |

    | username | String | 登錄名稱 |

    | password | String | 登錄密碼 |

    響應信息

    | 參數名稱 | 參數類型 | 參數說明 |

    | :————: | :——-: | :——: |

    | code | Integer | 結果code |

    | msg | String | 結果信息 |

    詳情接口

    請求url

    /info

    請求方法

    get

    請求 cookies

    | 參數名稱 | 參數類型 | 參數說明 |

    | :————: | :——-: | :——: |

    | session | String | session |

    響應信息

    | 參數名稱 | 參數類型 | 參數說明 |

    | :————: | :——-: | :——: |

    | code | Integer | 結果code |

    | msg | String | 結果信息 |

    | data | String | 數據信息 |

    五、編寫接口測試

    測試思路

    使用 requests [ http://docs.python-requests.org/zh_CN/latest/user/quickstart.html ] 庫模擬發送 HTTP 請求。

    使用 python 標準庫里 unittest 寫測試 case。

    腳本實現

    #!/usr/bin/python# coding=utf-8import requestsimport unittestclass TestLogin(unittest.TestCase):

    @classmethod

    def setUpClass(cls):

    cls.login_url = 'http://127.0.0.1:5000/login'

    cls.info_url = 'http://127.0.0.1:5000/info'

    cls.username = 'admin'

    cls.password = '123456'

    def test_login(self):

    """

    測試登錄

    """

    data = { 'username': self.username, 'password': self.password

    }

    response = requests.post(self.login_url, data=data).json() assert response['code'] == 200

    assert response['msg'] == 'success'

    def test_info(self):

    """

    測試info接口

    """

    data = { 'username': self.username, 'password': self.password

    }

    response_cookies = requests.post(self.login_url, data=data).cookies

    session = response_cookies.get('session') assert session

    info_cookies = { 'session': session

    }

    response = requests.get(self.info_url, cookies=info_cookies).json() assert response['code'] == 200

    assert response['msg'] == 'success'

    assert response['data'] == 'info'

    六、優化

    封裝接口調用

    寫完這個測試登錄腳本,你或許會發現,在整個項目的測試過程,登錄可能不止用到一次,如果每次都這么寫,會不會太冗余了?

    對,確實太冗余了,下面做一下簡單的封裝,把登錄接口的調用封裝到一個方法里,把調用參數暴漏出來,示例腳本如下:

    #!/usr/bin/python# coding=utf-8import requestsimport unittesttry: from urlparse import urljoinexcept ImportError: from urllib.parse import urljoinclass DemoApi(object):

    def __init__(self, base_url):

    self.base_url = base_url def login(self, username, password):

    """

    登錄接口

    :param username: 用戶名

    :param password: 密碼

    """

    url = urljoin(self.base_url, 'login')

    data = { 'username': username, 'password': password

    } return requests.post(url, data=data).json() def get_cookies(self, username, password):

    """

    獲取登錄cookies

    """

    url = urljoin(self.base_url, 'login')

    data = { 'username': username, 'password': password

    } return requests.post(url, data=data).cookies def info(self, cookies):

    """

    詳情接口

    """

    url = urljoin(self.base_url, 'info') return requests.get(url, cookies=cookies).json()class TestLogin(unittest.TestCase):

    @classmethod

    def setUpClass(cls):

    cls.base_url = 'http://127.0.0.1:5000'

    cls.username = 'admin'

    cls.password = '123456'

    cls.app = DemoApi(cls.base_url) def test_login(self):

    """

    測試登錄

    """

    response = self.app.login(self.username, self.password) assert response['code'] == 200

    assert response['msg'] == 'success'

    def test_info(self):

    """

    測試獲取詳情信息

    """

    cookies = self.app.get_cookies(self.username, self.password)

    response = self.app.info(cookies) assert response['code'] == 200

    assert response['msg'] == 'success'

    assert response['data'] == 'info'

    OK,在這一個版本中,我們不但在把登錄接口的調用封裝成了一個實例方法,實現了復用,而且還把 host(self.base_url)提取了出來。

    但問題又來了,登錄之后,登錄接口的 http 響應會把 session 以 cookie 的形式 set 到客戶端,之后的接口都會使用此 session 去請求。

    還有,就是在接口調用過程中,希望可以把日志打印出來,以便調試或者出錯時查看。

    好吧,我們再來改一版。

    保持 cookies &增加 log 信息

    使用 requests 庫里的同一個 Session 對象 (它也會在同一個 Session 實例發出的所有請求之間保持 cookie ),即可解決上面的問題,示例代碼如下:

    #!/usr/bin/python# coding=utf-8import unittestfrom pprint import pprintfrom requests.sessions import Sessiontry: from urlparse import urljoinexcept ImportError: from urllib.parse import urljoinclass DemoApi(object):

    def __init__(self, base_url):

    self.base_url = base_url # 創建session實例

    self.session = Session() def login(self, username, password):

    """

    登錄接口

    :param username: 用戶名

    :param password: 密碼

    """

    url = urljoin(self.base_url, 'login')

    data = { 'username': username, 'password': password

    }

    response = self.session.post(url, data=data).json()

    print('\n*****************************************')

    print(u'\n1、請求url: \n%s' % url)

    print(u'\n2、請求頭信息:')

    pprint(self.session.headers)

    print(u'\n3、請求參數:')

    pprint(data)

    print(u'\n4、響應:')

    pprint(response) return response def info(self):

    """

    詳情接口

    """

    url = urljoin(self.base_url, 'info')

    response = self.session.get(url).json()

    print('\n*****************************************')

    print(u'\n1、請求url: \n%s' % url)

    print(u'\n2、請求頭信息:')

    pprint(self.session.headers)

    print(u'\n3、請求cookies:')

    pprint(dict(self.session.cookies))

    print(u'\n4、響應:')

    pprint(response) return responseclass TestLogin(unittest.TestCase):

    @classmethod

    def setUpClass(cls):

    cls.base_url = 'http://127.0.0.1:5000'

    cls.username = 'admin'

    cls.password = '123456'

    cls.app = DemoApi(cls.base_url) def test_login(self):

    """

    測試登錄

    """

    response = self.app.login(self.username, self.password) assert response['code'] == 200

    assert response['msg'] == 'success'

    def test_info(self):

    """

    測試獲取詳情信息

    """

    self.app.login(self.username, self.password)

    response = self.app.info() assert response['code'] == 200

    assert response['msg'] == 'success'

    assert response['data'] == 'info'

    大功告成,我們把多個相關接口調用封裝到一個類中,使用同一個 requests Session 實例來保持 cookies,并且在調用過程中打印出了日志,我們所有目標都實現了。

    但再看下腳本,又會感覺不太舒服,在每個方法里,都要寫一遍print 1、2、3… 要拼url、還要很多細節等等。

    但其實我們 真正需要做的只是拼出關鍵的參數(url 參數、body 參數或者傳入 headers 信息),可不可以只需定義必須的信息,然后把其它共性的東西都封裝起來呢,統一放到一個地方去管理?

    封裝重復操作

    來,我們再整理一下我們的需求:

    首先,不想去重復做拼接 url 的操作。

    然后,不想每次都去手工打印日志。

    不想和 requests session 打交道。

    只想定義好參數就直接調用。

    我們先看一下實現后,腳本可能是什么樣:

    class DemoApi(object):

    def __init__(self, base_url):

    self.base_url = base_url @request(url='login', method='post')

    def login(self, username, password):

    """

    登錄接口

    """

    data = { 'username': username, 'password': password

    } return {'data': data} @request(url='info', method='get')

    def info(self):

    """

    詳情接口

    """

    pass

    調用登錄接口的日志:

    ******************************************************

    1、接口描述

    登錄接口

    2、請求url

    http://127.0.0.1:5000/login

    3、請求方法

    post

    4、請求headers

    {

    "Accept": "*/*",

    "Accept-Encoding": "gzip, deflate",

    "Connection": "keep-alive",

    "User-Agent": "python-requests/2.7.0 CPython/2.7.10 Darwin/16.4.0"

    }

    5、body參數

    {

    "password": "123456",

    "username": "admin"

    }

    6、響應結果

    {

    "code": 200,

    "msg": "success"

    }

    在這里,我們使用 python 的裝飾器功能,把公共特性封裝到裝飾器中去實現。現在感覺好多了,沒什么多余的東西了,我們可以專注于關鍵參數的構造,剩下的就是如何去實現這個裝飾器了,我們先理一下思路:

    獲取裝飾器參數

    獲取函數/方法參數

    把裝飾器和函數定義的參數合并

    拼接 url

    處理 requests session,有則使用,無則新生成一個

    組裝所有參數,發送http請求并打印日志

    因篇幅限制,源碼不再列出,有興趣的同學可以查看已經實現的源代碼。

    源代碼查看地址:https://github.com/yuyu1987/pithy-test/blob/master/pithy/api.py

    七、擴展

    http 接口請求的姿勢我們定義好了,我們還可以做些什么呢?

    [x] 非HTTP協議接口

    [x] 測試用例編寫

    [x] 配置文件管理

    [x] 測試數據管理

    [x] 工具類編寫

    [x] 測試報告生成

    [x] 持續集成

    [x] 等等等等

    需要做的還是挺多的,要做什么不要做什么,或者先做哪個,我覺得可以根據以下幾點去判斷:

    是否有利于提高團隊生產效率?

    是否有利于提高測試質量?

    有沒有現成的輪子可以用?

    下面就幾項主要的點進行一下說明,限于篇幅,不再展開了。

    測試報告

    這個應該是大家最關心的了,畢竟這是測試工作的產出;

    目前 python 的主流單元測試框均有 report 插件,因此不建議自己再編寫,除非有特殊需求的。

    pytest:推薦使用 pytest-html 和 allure pytest。

    unittest:推薦使用 HTMLTestRunner。

    持續集成

    持續集成推薦使用 Jenkins,運行環境、定時任務、觸發運行、郵件發送等一系列功能均可以在 Jenkins 上實現。

    測試用例編寫

    推薦遵守如下規則:

    原子性:每個用例保持獨立,彼此不耦合,以降低干擾。

    專一性:一個用例應該專注于驗證一件事情,而不是做很多事情,一個測試點不要重復驗證。

    穩定性:絕大多數用例應該是非常穩定的,也就是說不會經常因為除環境以外的因素掛掉,因為如果在一個測試項目中有很多不穩定的用例的話,測試結果就不能很好的反應項目質量。

    分類清晰:有相關性的用例應寫到一個模塊或一個測試類里,這樣做即方便維護,又提高了報告的可讀性。

    測試工具類

    這個可以根據項目情況去做,力求簡化一些類庫的使用,數據庫訪問、日期時間、序列化與反序列化等數據處理,或者封裝一些常用操作,如隨機生成訂單號等等,以提高腳本編寫效率。

    測試數據管理

    常見的方式有寫在代碼里、寫在配置文件里(xml、yaml、json、.py、excel等)、寫在數據庫里等,該處沒有什么好推薦的,建議根據個人喜好,怎么方便怎么來就可以。

    八、pithy 測試框架介紹

    pithy 意為簡潔有力的,意在簡化自動化接口測試,提高測試效率。

    項目地址:https://github.com/yuyu1987/pithy-test

    幫助文檔:http://pithy-test.readthedocs.io/

    目前實現的功能如下:

    一鍵生成測試項目

    http client封裝

    thrift接口封裝

    簡化配置文件使用

    優化JSON、日期等工具使用

    編寫測試用例推薦使用 pytest(https://docs.pytest.org/),pytest 提供了很多測試工具以及插件(http://plugincompat.herokuapp.com/),可以滿足大部分測試需求。

    安裝

    pip install pithy-test

    pip install pytest

    使用

    一鍵生成測試項目

    >>> pithy-cli init

    請選擇項目類型,輸入api或者app: api

    請輸入項目名稱,如pithy-api-test: pithy-api-test

    開始創建pithy-api-test項目

    開始渲染...

    生成 api/.gitignore [√]

    生成 api/apis/__init__.py [√]

    生成 api/apis/pithy_api.py [√]

    生成 api/cfg.yaml [√]

    生成 api/db/__init__.py [√]

    生成 api/db/pithy_db.py [√]

    生成 api/README.MD [√]

    生成 api/requirements.txt [√]

    生成 api/test_suites/__init__.py [√]

    生成 api/test_suites/test_login.py [√]

    生成 api/utils/__init__.py [√]

    生成成功,請使用編輯器打開該項目

    生成項目樹:

    >>> tree pithy-api-test

    pithy-api-test

    ├── README.MD

    ├── apis

    │ ├── __init__.py

    │ └── pithy_api.py

    ├── cfg.yaml

    ├── db

    │ ├── __init__.py

    │ └── pithy_db.py

    ├── requirements.txt

    ├── test_suites

    │ ├── __init__.py

    │ └── test_login.py

    └── utils

    └── __init__.py

    4 directories, 10 files

    調用 HTTP 登錄接口示例

    from pithy import request@request(url='http://httpbin.org/post', method='post')def post(self, key1='value1'):

    """

    post method

    """

    data = { 'key1': key1

    } return dict(data=data)# 使用response = post('test').to_json() # 解析json字符,輸出為字典response = post('test').json # 解析json字符,輸出為字典response = post('test').to_content() # 輸出為字符串response = post('test').content # 輸出為字符串response = post('test').get_cookie() # 輸出cookie對象response = post('test').cookie # 輸出cookie對象# 結果取值, 假設此處response = {'a': 1, 'b': { 'c': [1, 2, 3, 4]}}response = post('13111111111', '123abc').jsonprint response.b.c # 通過點號取值,結果為[1, 2, 3, 4]print response('$.a') # 通過object path取值,結果為1for i in response('$..c[@>3]'): # 通過object path取值,結果為選中c字典里大于3的元素

    print i

    優化 JSON、字典使用

    # 1、操作JSON的KEYfrom pithy import JSONProcessor

    dict_data = {'a': 1, 'b': {'a': [1, 2, 3, 4]}}

    json_data = json.dumps(dict_data)

    result = JSONProcessor(json_data)print result.a # 結果:1print result.b.a # 結果:[1, 2, 3, 4]# 2、操作字典的KEYdict_data = {'a': 1, 'b': {'a': [1, 2, 3, 4]}}

    result = JSONProcessor(dict_data)print result.a # 1print result.b.a # [1, 2, 3, 4]# 3、object path取值raw_dict = { 'key1':{ 'key2':{ 'key3': [1, 2, 3, 4, 5, 6, 7, 8]

    }

    }

    }

    jp = JSONProcessor(raw_dict)for i in jp('$..key3[@>3]'): print i# 4、其它用法dict_1 = {'a': 'a'}

    json_1 = '{"b": "b"}'jp = JSONProcessor(dict_1, json_1, c='c')

    print(jp)

    更多使用方法:http://pithy-test.readthedocs.io/

    九、總結

    在本文中,我們以提高腳本開發效率為前提,一步一步打造了一個簡易的測試框架。

    但因水平所限,并未涉及測試數據初始化清理、測試中如何 MOCK 等話題,前路依然任重而道遠,希望給大家一個啟發,不足之處還望多多指點,非常感謝。

    預約申請免費試聽課

    填寫下面表單即可預約申請免費試聽!怕錢不夠?可就業掙錢后再付學費! 怕學不會?助教全程陪讀,隨時解惑!擔心就業?一地學習,可全國推薦就業!

    上一篇:Python在人工智能中的作用 | 深度解讀
    下一篇:Python3中PyMongo的用法

    2021年Python面試題及答案匯總詳解

    python數據分析,你需要這些工具

    Python培訓講解二叉樹的三種深度

    Python培訓:如何實現窮舉搜索?

    • 掃碼領取資料

      回復關鍵字:視頻資料

      免費領取 達內課程視頻學習資料

    • 視頻學習QQ群

      添加QQ群:1143617948

      免費領取達內課程視頻學習資料

    Copyright ? 2021 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

    選擇城市和中心
    黑龍江省

    吉林省

    河北省

    湖南省

    貴州省

    云南省

    廣西省

    海南省

    天天日天天射天天干天天伊|奇米电影|奇米网_奇米首页|奇米首页 百度 好搜 搜狗
    <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>