天天看點

python 接口自動化測試_簡單實作接口自動化測試(基于python)

歡迎關注公衆号 學習資料不會少

01

python 接口自動化測試_簡單實作接口自動化測試(基于python)

簡介

本文從一個簡單的登入接口測試入手,一步步調整優化接口調用姿勢,然後簡單讨論了一下接口測試架構的要點,最後介紹了一下我們目前正在使用的接口測試架構pithy。期望讀者可以通過本文對接口自動化測試有一個大緻的了解。

02

python 接口自動化測試_簡單實作接口自動化測試(基于python)

引言

接口自動化測試的重要性:

在目前網際網路産品疊代頻繁的背景下,回歸測試的時間越來越少,很難在每個疊代都對所有功能做完整回歸。但接口自動化測試因其實作簡單、維護成本低,容易提高覆寫率等特點,越來越受重視。

怎樣提高架構的編寫效率:

用requets + unittest很容易實作接口自動化測試,而且requests的api已經非常人性化,非常簡單,但通過封裝以後(特别是針對公司内特定接口),再加上對一些常用工具的封裝,可以進一步提高業務腳本編寫效率。

03

python 接口自動化測試_簡單實作接口自動化測試(基于python)

環境準備

確定本機已安裝python3.7.2以上版本,然後安裝如下:

● pip install flask

● pip install requests

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

04

python 接口自動化測試_簡單實作接口自動化測試(基于python)

測試接口準備

下面使用flask實作兩個http接口,一個登入,另外一個查詢詳情,但需要登入後才可以,建立一個demo.py檔案(注意,不要使用windows記事本),把下面代碼copy進去,然後儲存、關閉

接口代碼

#!/usr/bin/python

# coding=utf-8

from 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

請求參數

python 接口自動化測試_簡單實作接口自動化測試(基于python)

響應資訊

python 接口自動化測試_簡單實作接口自動化測試(基于python)

詳情接口

請求url

● /info

請求方法

● get

請求cookies

python 接口自動化測試_簡單實作接口自動化測試(基于python)

響應資訊

python 接口自動化測試_簡單實作接口自動化測試(基于python)

05

python 接口自動化測試_簡單實作接口自動化測試(基于python)

編寫接口測試

測試思路

使用requests [使用連結] 庫模拟發送HTTP請求

使用python标準庫裡unittest寫測試case

腳本實作

#!/usr/bin/python

# coding=utf-8

import requests

import unittest

class 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'

06

python 接口自動化測試_簡單實作接口自動化測試(基于python)

優化

封裝接口調用

寫完這個測試登入腳本,你或許會發現,在整個項目的測試過程,登入可能不止用到一次,如果每次都這麼寫,會不會太備援了?對,确實太備援了,下面做一下簡單的封裝,把登入接口的調用封裝到一個方法裡,把調用參數暴漏出來,示例腳本如下:

#!/usr/bin/python

# coding=utf-8

import requests

import unittest

try:

    from urlparse import urljoin

except ImportError:

    from urllib.parse import urljoin

class 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-8

import unittest

from pprint import pprint

from requests.sessions import Session

try:

    from urlparse import urljoin

except 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請求并列印日志

07

python 接口自動化測試_簡單實作接口自動化測試(基于python)

擴充

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

● 非HTTP協定接口

● 測試用例編寫

● 配置檔案管理

● 測試資料管理

● 工具類編寫

● 測試報告生成

● 持續內建

● 等等等等

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

 ●是否有利于提高團隊生産效率

 ●是否有利于提高測試品質

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

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

測試報告

python 接口自動化測試_簡單實作接口自動化測試(基于python)

請輸入

python 接口自動化測試_簡單實作接口自動化測試(基于python)

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

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

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

unittest:推薦使用HTMLTestRunner

持續內建

python 接口自動化測試_簡單實作接口自動化測試(基于python)

請輸入

python 接口自動化測試_簡單實作接口自動化測試(基于python)

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

測試用例編寫

python 接口自動化測試_簡單實作接口自動化測試(基于python)

請輸入

python 接口自動化測試_簡單實作接口自動化測試(基于python)

推薦遵守如下規則:

● 必需參數覆寫。對于接口的參數,接口文檔一般都會說明哪些兒是必需的,哪兒是非必需的。對于必需的參數,一定要測試傳參數和不傳參數接口是否報錯?

● 必需的參數各種情況覆寫。傳非法的字元,特殊的字元,空值,超過邊界的參數是否報錯?錯誤資訊是否正确?

● 非必需參數覆寫。一般接口對于非必需參數都不會做非正常性傳值的判斷,是以要測試合法的參數值 ,接口傳回的内容是否正确。如果有接口文檔說明對非必需參數做了非正常的驗證的話,也要對其進行驗證。

● 參數的組合覆寫。有些兒參數需要互相配合着才起作用,如“offset”和“count”組合起來進行翻頁,這個時候要組合起來進行測試。

● 業務邏輯相關的覆寫。有些兒接口與業務邏輯關聯密切,單獨從接口角度測試,可能會遺漏掉一些兒因業務邏輯而産生的bug。是以如果和業務邏輯相關,也要考慮到業務邏輯相關的測試用例。

其實接口的測試用例差不多也就這些兒情況,也許有特殊的接口,到時候和産品,開發人員做好溝通,盡量先從接口層面保證品質。這樣再從測試接口的應用層的時候,就可以少很多工作量,隻注重樣式和各個接口調用的配合就可以了。

測試資料管理

自動化測試過程中,現在大多都預設測試腳本與測試資料分離的設計,這樣做的好處是:降低維護成本,遷移成本以及提高效率。

是以測試資料放在哪裡,如何管理,不能一概而論。個人覺得應該從以下幾方面來考慮:

業務場景

● 比如在UI自動化測試中,需要測試某個電商網站的各個業務子產品,但前提是要使用者登入。這個用來執行登入的測試賬号資料往往是固定的,那麼專門将一組username和password放在一個測試資料檔案或者測試資料庫中,這樣就顯得太笨重,耗時費力。将其寫入測試腳本或者寫入配置檔案,直接引用效率會更高。

● 同樣,測試電商網站,賬号體系分為普通賬号,會員賬号,會員還分很多等級,有時候為了測試會員中心不同的賬号展示的資訊是否不同,就需要使用不同的等級的賬号登入,這種場景下,可以将測試資料放在測試檔案裡(比如excel、csv),通過參數化的方式來循環讀取,執行後續操作。

● 在API自動化測試中,比如針對restful風格的接口,它的域名相對來說都是固定的,隻是不同接口的path不同,那麼也可以将域名寫入配置檔案,測試過程中隻需要将執行個體化的域名和path進行拼接即可,這樣也省卻了在測試資料檔案中維護的成本,一定程度上提升了測試效率。

資料類型

測試資料也分不同類型,大概分為以下幾種類型:

base-data:即基礎資料,比如電商網站的商品資訊、SKU,比如物流公司的倉儲管理等,這類資料往往基數比較大,可以視為持久層,儲存在DB中;test-data:測試資料,根據業務場景不同,資料無論量級還是變更頻次也不同,基于測試腳本與資料分離的概念,可放在專門的測試檔案中,比如excel、csv;ephemeral-data:臨時資料,即使用一次的資料,這種類型的資料可以用臨時檔案存儲(比如dat、csv等)格式,然後進行參數化讀取,或者直接寫入腳本中;

資料量級

● 還是電商網站的某個場景,需要先執行登入,登入的賬号比如是專門配置的一個測試賬号,相對固定,那麼将測試賬号寫入測試腳本也無可厚非。不過我本人不喜歡将測試資料直接寫入腳本,這種情況我會寫入配置檔案,然後執行個體化調用,這種情況就需要根據個人習慣來設計,沒有固定的套路;

● 資料量級在幾十——幾百上千之間,這種時候,可以寫入excel檔案進行存儲管理,但是excel的局限在于其本身目前最大支援65500+行的資料存儲,而且隻支援單事務,如果需要多線程讀取,就會變成瓶頸。

● csv檔案,結構簡單、通用,可以和excel進行轉換,可以減少存儲檔案size,且具備簡單的安全性,可以在一定程度上替代excel成為資料存儲檔案。我本人目前在大多數場景下也是使用csv類型的檔案進行測試資料存儲管理;

● 當測試資料超過一定量級,比如性能測試中,如果要執行并發測試或者穩定性測試,那麼所需測試資料量級就很大,這時使用excel或者csv就會變得很不友善。無論是從維護的成本還是便捷性考慮,都應該選擇利用DB或其他高效的管理方式來存儲和管理測試資料;

使用頻次

測試資料的重用頻次不同,也需要選擇不同的存儲方式,比如:

● once:隻使用一次的測試資料,那麼隻需要寫入臨時檔案,用完廢棄或者删除即可;

● often:即經常使用的測試資料,應根據資料量級,使用場景,資料類型選擇合适的存儲管理方式;

● alway:可以了解為base-data或者持久資料,這種類型的資料因為其本身更新頻次很低,或者資料量級較大,一般存儲在DB中是比較好的一種管理方案。

08

python 接口自動化測試_簡單實作接口自動化測試(基于python)

pithy測試架構介紹

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

目前實作的功能如下:

一鍵生成測試項目

http client封裝

thrift接口封裝

簡化配置檔案使用

優化JSON、日期等工具使用

編寫測試用例推薦使用pytest,pytest提供了很多測試工具以及插件,可以滿足大部分測試需求。

安裝

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').json

print response.b.c   # 通過點号取值,結果為[1, 2, 3, 4]

print response('$.a') # 通過object path取值,結果為1

for i in response('$..c[@>3]'): # 通過object path取值,結果為選中c字典裡大于3的元素

    print i

優化JSON、字典使用

# 1、操作JSON的KEY

from 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  )   # 結果:1

print( result.b.a   )# 結果:[1, 2, 3, 4]

# 2、操作字典的KEY

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

result = JSONProcessor(dict_data)

print (result.a )    # 1

print( 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)

09

python 接口自動化測試_簡單實作接口自動化測試(基于python)

總結

在本文中,我們以提高腳本開發效率為前提,一步一步打造了一個簡易的測試架構,但因水準所限,并未涉及測試資料初始化清理、測試中如何MOCK等話題,前路依然任重而道遠,希望給大家一個啟發,不足之處還望多多指點,非常感謝。

往期精彩文章

喜報來了!凡貓學員薪資最高16K!

金融行業軟體測試介紹

2020年為什麼大家都開始學習自動化測試?

學習測試開發前 你需要掌握的python 代碼水準

1萬+軟體測試人員都在學的精品課程免費送,大家别錯過

python 接口自動化測試_簡單實作接口自動化測試(基于python)

繼續閱讀