1. 程式人生 > >介面自動化測試,完整入門篇

介面自動化測試,完整入門篇

1. 什麼是介面測試

顧名思義,介面測試是對系統或元件之間的介面進行測試,主要是校驗資料的交換,傳遞和控制管理過程,以及相互邏輯依賴關係。其中介面協議分為HTTP,WebService,Dubbo,Thrift,Socket等型別,測試型別又主要分為功能測試,效能測試,穩定性測試,安全性測試等。

在分層測試的“金字塔”模型中,介面測試屬於第二層服務整合測試範疇。相比UI層(主要是WEB或APP)自動化測試而言,介面自動化測試收益更大,且容易實現,維護成本低,有著更高的投入產出比,是每個公司開展自動化測試的首選。

下面我們以一個HTTP介面為例,完整的介紹介面自動化測試流程:從需求分析到用例設計,從指令碼編寫、測試執行到結果分析,並提供完整的用例設計及測試指令碼。

2. 基本流程

基本的介面功能自動化測試流程如下:
需求分析 -> 用例設計 -> 指令碼開發 -> 測試執行 -> 結果分析

2.1 示例介面

3. 需求分析

需求分析是參考需求、設計等文件,在瞭解需求的基礎上還需清楚內部的實現邏輯,並且可以在這一階段提出需求、設計存在的不合理或遺漏之處。

如:豆瓣電影搜尋介面,我理解的需求即是支援對片名,演職人員及標籤的搜尋,並分頁返回搜尋結果。

4. 用例設計

用例設計是在理解介面測試需求的基礎上,使用MindManager或XMind等思維導圖軟體編寫測試用例設計,主要內容包括引數校驗,功能校驗、業務場景校驗、安全性及效能校驗等,常用的用例設計方法有等價類劃分法,邊界值分析法,場景分析法,因果圖,正交表等。

針對豆瓣電影搜尋介面功能測試部分,我們主要從引數校驗,功能校驗,業務場景校驗三方面,設計測試用例如下:
這裡寫圖片描述

5. 指令碼開發

依據上面編寫的測試用例設計,我們使用python+nosetests框架編寫了相關自動化測試指令碼。可以完整實現介面自動化測試、自動執行及郵件傳送測試報告功能。

5.1 相關lib安裝

必要的lib庫如下,使用pip命令安裝即可:

pip install nose
pip install nose-html-reporting
pip install requests

5.2 介面呼叫

使用requests庫,我們可以很方便的編寫上述介面呼叫方法(如搜尋q=劉德華,示例程式碼如下):

#coding=utf-8
import requests
import json

url = 'https://api.douban.com/v2/movie/search'
params=dict(q=u'劉德華')
r = requests.get(url, params=params)
print 'Search Params:\n', json.dumps(params, ensure_ascii=False)
print 'Search Response:\n', json.dumps(r.json(), ensure_ascii=False, indent=4)

在實際編寫自動化測試指令碼時,我們需要進行一些封裝。如下程式碼中我們對豆瓣電影搜尋介面進行了封裝,test_q方法只需使用nosetests提供的yield方法即可很方便的迴圈執行列表qs中每一個測試集:

class test_doubanSearch(object):

    @staticmethod
    def search(params, expectNum=None):
        url = 'https://api.douban.com/v2/movie/search'
        r = requests.get(url, params=params)
        print 'Search Params:\n', json.dumps(params, ensure_ascii=False)
        print 'Search Response:\n', json.dumps(r.json(), ensure_ascii=False, indent=4)

    def test_q(self):
        # 校驗搜尋條件 q
        qs = [u'白夜追凶', u'大話西遊', u'周星馳', u'張藝謀', u'周星馳,吳孟達', u'張藝謀,鞏俐', u'周星馳,大話西遊', u'白夜追凶,潘粵明']
        for q in qs:
            params = dict(q=q)
            f = partial(test_doubanSearch.search, params)
            f.description = json.dumps(params, ensure_ascii=False).encode('utf-8')
            yield (f,)

我們按照測試用例設計,依次編寫每個功能的自動化測試指令碼即可。

5.3 結果校驗

在手工測試介面的時候,我們需要通過介面返回的結果判斷本次測試是否通過,自動化測試也是如此。

對於本次的介面,我們搜尋“q=劉德華”,我們需要判斷返回的結果中是否含有“演職人員劉德華或片名劉德華”,搜尋“tag=喜劇”時,需要判斷返回的結果中電影型別是否為“喜劇”,結果分頁時需要校驗返回的結果數是否正確等。完整結果校驗程式碼如下:

class check_response():
    @staticmethod
    def check_result(response, params, expectNum=None):
        # 由於搜尋結果存在模糊匹配的情況,這裡簡單處理只校驗第一個返回結果的正確性
        if expectNum is not None:
            # 期望結果數目不為None時,只判斷返回結果數目
            eq_(expectNum, len(response['subjects']), '{0}!={1}'.format(expectNum, len(response['subjects'])))
        else:
            if not response['subjects']:
                # 結果為空,直接返回失敗
                assert False
            else:
                # 結果不為空,校驗第一個結果
                subject = response['subjects'][0]
                # 先校驗搜尋條件tag
                if params.get('tag'):
                    for word in params['tag'].split(','):
                        genres = subject['genres']
                        ok_(word in genres, 'Check {0} failed!'.format(word.encode('utf-8')))

                # 再校驗搜尋條件q
                elif params.get('q'):
                    # 依次判斷片名,導演或演員中是否含有搜尋詞,任意一個含有則返回成功
                    for word in params['q'].split(','):
                        title = [subject['title']]
                        casts = [i['name'] for i in subject['casts']]
                        directors = [i['name'] for i in subject['directors']]
                        total = title + casts + directors
                        ok_(any(word.lower() in i.lower() for i in total),
                            'Check {0} failed!'.format(word.encode('utf-8')))

    @staticmethod
    def check_pageSize(response):
        # 判斷分頁結果數目是否正確
        count = response.get('count')
        start = response.get('start')
        total = response.get('total')
        diff = total - start

        if diff >= count:
            expectPageSize = count
        elif count > diff > 0:
            expectPageSize = diff
        else:
            expectPageSize = 0

        eq_(expectPageSize, len(response['subjects']), '{0}!={1}'.format(expectPageSize, len(response['subjects'])))

5.4 執行測試

對於上述測試指令碼,我們使用nosetests命令可以方便的執行自動化測試,並可使用nose-html-reporting外掛生成html格式測試報告。

執行命令如下:

nosetests -v test_doubanSearch.py:test_doubanSearch --with-html --html-report=TestReport.html

5.5 傳送郵件報告

測試完成之後,我們可以使用smtplib模組提供的方法傳送html格式測試報告。基本流程是讀取測試報告 -> 新增郵件內容及附件 -> 連線郵件伺服器 -> 傳送郵件 -> 退出,示例程式碼如下:

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

def send_mail():
    # 讀取測試報告內容
    with open(report_file, 'r') as f:
        content = f.read().decode('utf-8')

    msg = MIMEMultipart('mixed')
    # 新增郵件內容
    msg_html = MIMEText(content, 'html', 'utf-8')
    msg.attach(msg_html)

    # 新增附件
    msg_attachment = MIMEText(content, 'html', 'utf-8')
    msg_attachment["Content-Disposition"] = 'attachment; filename="{0}"'.format(report_file)
    msg.attach(msg_attachment)

    msg['Subject'] = mail_subjet
    msg['From'] = mail_user
    msg['To'] = ';'.join(mail_to)
    try:
        # 連線郵件伺服器
        s = smtplib.SMTP(mail_host, 25)
        # 登陸
        s.login(mail_user, mail_pwd)
        # 傳送郵件
        s.sendmail(mail_user, mail_to, msg.as_string())
        # 退出
        s.quit()
    except Exception as e:
        print "Exceptioin ", e

6. 結果分析

開啟nosetests執行完成後生成的測試報告,可以看出本次測試共執行了51條測試用例,50條成功,1條失敗。
這裡寫圖片描述
失敗的用例可以看到傳入的引數是:{“count”: -10, “tag”: “喜劇”},此時返回的結果數與我們的期望結果不一致(count為負數時,期望結果是介面報錯或使用預設值20,但實際返回的結果數目是189。趕緊去給豆瓣提bug啦- -)
這裡寫圖片描述

7. 完整指令碼

下載完成之後,使用如下命令即可進行完整的介面自動化測試並通過郵件傳送最終的測試報告:
python test_doubanSearch.py

最終傳送測試報告郵件,截圖如下:
這裡寫圖片描述

8. 參考資料