1. 程式人生 > >請以excel管理你的介面測試用例

請以excel管理你的介面測試用例

閒話休扯,上需求:自動讀取、執行excel裡面的介面測試用例,測試完成後,返回錯誤結果併發送郵件通知。

分析:

1、設計excel表格
2、讀取excel表格
3、拼接url,傳送請求
4、彙總錯誤結果、傳送郵件

開始實現:

1、設計excel介面用例表格,大概長這樣:
在這裡插入圖片描述
依次為:用例編號、介面名稱、介面主host、介面路由、請求方式、請求引數型別、請求引數、斷言

這次案例中用到的介面,其實就是如何優雅的進行介面測試使用的快遞查詢介面,一時半會兒沒找到好用的,之前寫的也找不到了,只好作罷。

2、讀取excel表格,獲取每個用例的資料

import xlrd
import sys

def test_cases_in_excel(test_case_file):
    test_case_file = os.path.join(os.getcwd(), test_case_file)
    # 獲取測試用例全路徑 如:E:\Python\httprunner\interface_excel\testcases.xlsx
    print(test_case_file)
    if not os.path.exists(test_case_file):
        print("測試用例excel檔案存在或路徑有誤!")
        # 找不到指定測試檔案,就退出程式 os.system("exit")是用來退出cmd的
        sys.exit()
    # 讀取excel檔案
    test_case = xlrd.open_workbook(test_case_file)
    # 獲取第一個sheet,下標從0開始
    table = test_case.sheet_by_index(0)
    # 記錄錯誤用例
    error_cases = []
    # 一張表格讀取下來,其實就像個二維陣列,無非是讀取第一行的第幾列的值,由於下標是從0開始,第一行是標題,所以從第二行開始讀取資料
    for i in range(1, table.nrows):
        num = str(int(table.cell(i, 0).value)).replace("\n", "").replace("\r", "")
        api_name = table.cell(i, 1).value.replace("\n", "").replace("\r", "")
        api_host = table.cell(i, 2).value.replace("\n", "").replace("\r", "")
        request_url = table.cell(i, 3).value.replace("\n", "").replace("\r", "")
        method = table.cell(i, 4).value.replace("\n", "").replace("\r", "")
        request_data_type = table.cell(i, 5).value.replace("\n", "").replace("\r", "")
        request_data = table.cell(i, 6).value.replace("\n", "").replace("\r", "")
        check_point = table.cell(i, 7).value.replace("\n", "").replace("\r", "")
        print(num, api_name, api_host, request_url, method, request_data_type, request_data, check_point)
        try:
            # 呼叫介面請求方法,後面會講到
            status, resp = interface_test(num, api_name, api_host, request_url, method, 
                                            request_data_type, request_data, check_point)
            if status != 200 or check_point not in resp:
                # append只接收一個引數,所以要講四個引數括在一起,當一個引數來傳遞
                # 請求失敗,則向error_cases中增加一條記錄
                error_cases.append((num + " " + api_name, str(status), api_host + request_url))
        except Exception as e:
            print(e)
            print("第{}個介面請求失敗,請檢查介面是否異常。".format(num))
            # 訪問異常,也向error_cases中增加一條記錄
            error_cases.append((num + " " + api_name, "請求失敗", api_host + request_url))
    return error_cases

3、拼接url,判斷請求方式(get/post),傳送請求

傳入讀取用例的各種引數,先判斷請求方式,再拼接引數通過requests庫來發送請求

import requests

def interface_test(num, api_name, api_host, request_url, method, 
                    request_data_type, request_data, check_point):
    # 構造請求headers
    headers = {'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
                      'X-Requested-With' : 'XMLHttpRequest',
                      'Connection' : 'keep-alive',
                      'Referer' : 'http://' + api_host,
                      'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36'
                }
    # 判斷請求方式,如果是GET,則呼叫get請求,POST調post請求,都不是,則丟擲異常
    if method == "GET":
        r = requests.get(url=api_host+request_url, params=json.loads(request_data), headers=headers)
        # 獲取請求狀態碼
        status = r.status_code
        # 獲取返回值
        resp = r.text
        if status == 200:
            # 斷言,判斷設定的斷言值,是否在返回值裡面
            if check_point in str(r.text):
                print("第{}條用例'{}'執行成功,狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
            else:
                print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
        else:
            print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
            return status, resp
    elif method == "POST":
        # 跟GET裡面差不多,就不一一註釋了
        r = requests.post(url=api_host+request_url, params=json.loads(request_data), headers=headers)
        status = r.status_code
        resp = r.text
        if status == 200:
            if check_point in str(r.text):
                print("第{}條用例'{}'執行成功,狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
            else:
                print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
        else:
            print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
            return status, resp
    else:
        print("第{}條用例'{}'請求方式有誤!!!請確認欄位【Method】值是否正確,正確值為大寫的GET或POST。".format(num, api_name))
        return 400, "請求方式有誤"

4、彙總錯誤結果、傳送郵件

4.1、彙總錯誤結果,儲存為簡易html報告,並通過郵件傳送到指定接收人

def main():
    # 執行所以測試用例,獲取錯誤的用例
    error_cases = test_cases_in_excel("testcases.xlsx")
    # 如果有錯誤介面,則開始構造html報告
    if len(error_cases) > 0:
        html = '<html><body>介面自動化掃描,共有 ' + str(len(error_cases)) + ' 個異常介面,列表如下:' + '</p><table><tr><th style="width:100px;text-align:left">介面</th><th style="width:50px;text-align:left">狀態</th><th style="width:200px;text-align:left">介面地址</th></tr>'
        for test in error_cases:
            html = html + '<tr><td style="text-align:left">' + test[0] + '</td><td style="text-align:left">' + test[1] + '</td><td style="text-align:left">' + test[2] + '</td></tr>'
        send_email(html)
        print(html)
        with open ("report.html", "w") as f:
            f.write(html)
    else:
        print("本次測試,所有用例全部通過")
        send_email("本次測試,所有用例全部通過")

4.2、構造郵件函式

先讀取配置檔案,新建config.yml配置檔案,內容如下:

sender為傳送郵件的郵箱,receiver為接收者著的郵箱,支援多個,smtpserver郵箱服務,username傳送者郵箱少去後綴,password密碼
在這裡插入圖片描述

import yaml

def get_conf():
    with open ("config.yml", "r", encoding='utf-8') as f:
        cfg = f.read()
        dic = yaml.load(cfg)
        sender = dic['email']['sender']
        receiver = dic['email']['receiver']
        smtpserver = dic['email']['smtpserver']
        username = dic['email']['username']
        password = dic['email']['password']
        print(sender, receiver, smtpserver, username, password)
        return sender, receiver, smtpserver, username, password

然後構造傳送郵件的函式

import smtplib
import time
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.header import Header


def send_email(text):
    today = time.strftime('%Y.%m.%d',time.localtime(time.time()))
    sender, receiver, smtpserver, username, password = get_conf()
    # subject為郵件主題 text為郵件正文
    subject = "[api_test]介面自動化測試結果通知 {}".format(today)
    msg = MIMEText(text, 'html', 'utf-8')
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = "".join(receiver)
    smtp = smtplib.SMTP()
    smtp.connect(smtpserver)
    smtp.login(username, password)
    smtp.sendmail(sender, receiver, msg.as_string())
    smtp.quit()

以上內容就將需求實現了,由於現在很晚了(懶),上面所以函式就對在一個py檔案裡面了,來執行下吧
在這裡插入圖片描述
郵件一會兒就收到了
在這裡插入圖片描述
所有程式碼如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

'''
需求:自動讀取、執行excel裡面的介面測試用例,測試完成後,返回錯誤結果併發送郵件通知。
一步一步捋清需求:
1、設計excel表格
2、讀取excel表格
3、拼接url,傳送請求
4、彙總錯誤結果、傳送郵件
'''
import xlrd
import os
import requests
import json
import yaml
import smtplib
import time
import sys
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.header import Header


def test_cases_in_excel(test_case_file):
    test_case_file = os.path.join(os.getcwd(), test_case_file)
    # 獲取測試用例全路徑 如:E:\Python\httprunner\interface_excel\testcases.xlsx
    print(test_case_file)
    if not os.path.exists(test_case_file):
        print("測試用例excel檔案存在或路徑有誤!")
        # 找不到指定測試檔案,就退出程式 os.system("exit")是用來退出cmd的
        sys.exit()
    # 讀取excel檔案
    test_case = xlrd.open_workbook(test_case_file)
    # 獲取第一個sheet,下標從0開始
    table = test_case.sheet_by_index(0)
    # 記錄錯誤用例
    error_cases = []
    # 一張表格讀取下來,其實就像個二維陣列,無非是讀取第一行的第幾列的值,由於下標是從0開始,第一行是標題,所以從第二行開始讀取資料
    for i in range(1, table.nrows):
        num = str(int(table.cell(i, 0).value)).replace("\n", "").replace("\r", "")
        api_name = table.cell(i, 1).value.replace("\n", "").replace("\r", "")
        api_host = table.cell(i, 2).value.replace("\n", "").replace("\r", "")
        request_url = table.cell(i, 3).value.replace("\n", "").replace("\r", "")
        method = table.cell(i, 4).value.replace("\n", "").replace("\r", "")
        request_data_type = table.cell(i, 5).value.replace("\n", "").replace("\r", "")
        request_data = table.cell(i, 6).value.replace("\n", "").replace("\r", "")
        check_point = table.cell(i, 7).value.replace("\n", "").replace("\r", "")
        print(num, api_name, api_host, request_url, method, request_data_type, request_data, check_point)
        try:
            # 呼叫介面請求方法,後面會講到
            status, resp = interface_test(num, api_name, api_host, request_url, method, 
                                            request_data_type, request_data, check_point)
            if status != 200 or check_point not in resp:
                # append只接收一個引數,所以要講四個引數括在一起,當一個引數來傳遞
                # 請求失敗,則向error_cases中增加一條記錄
                error_cases.append((num + " " + api_name, str(status), api_host + request_url))
        except Exception as e:
            print(e)
            print("第{}個介面請求失敗,請檢查介面是否異常。".format(num))
            # 訪問異常,也向error_cases中增加一條記錄
            error_cases.append((num + " " + api_name, "請求失敗", api_host + request_url))
    return error_cases


def interface_test(num, api_name, api_host, request_url, method, 
                    request_data_type, request_data, check_point):
    # 構造請求headers
    headers = {'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
                      'X-Requested-With' : 'XMLHttpRequest',
                      'Connection' : 'keep-alive',
                      'Referer' : 'http://' + api_host,
                      'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36'
                }
    # 判斷請求方式,如果是GET,則呼叫get請求,POST調post請求,都不是,則丟擲異常
    if method == "GET":
        r = requests.get(url=api_host+request_url, params=json.loads(request_data), headers=headers)
        # 獲取請求狀態碼
        status = r.status_code
        # 獲取返回值
        resp = r.text
        if status == 200:
            # 斷言,判斷設定的斷言值,是否在返回值裡面
            if check_point in str(r.text):
                print("第{}條用例'{}'執行成功,狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
            else:
                print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
        else:
            print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
            return status, resp
    elif method == "POST":
        # 跟GET裡面差不多,就不一一註釋了
        r = requests.post(url=api_host+request_url, params=json.loads(request_data), headers=headers)
        status = r.status_code
        resp = r.text
        if status == 200:
            if check_point in str(r.text):
                print("第{}條用例'{}'執行成功,狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
            else:
                print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
                return status, resp
        else:
            print("第{}條用例'{}'執行失敗!!!狀態碼為{},結果返回值為{}.".format(num, api_name, status, r.text))
            return status, resp
    else:
        print("第{}條用例'{}'請求方式有誤!!!請確認欄位【Method】值是否正確,正確值為大寫的GET或POST。".format(num, api_name))
        return 400, "請求方式有誤"


def main():
    # 執行所以測試用例,獲取錯誤的用例
    error_cases = test_cases_in_excel("testcases.xlsx")
    # 如果有錯誤介面,則開始構造html報告
    if len(error_cases) > 0:
        # html = '<html><body>介面自動化掃描,共有 ' + str(len(error_cases)) + ' 個異常介面,列表如下:' + '</p><table><tr><th style="width:100px;text-align:left">介面</th><th style="width:50px;text-align:left">狀態</th><th style="width:200px;text-align:left">介面地址</th><th   style="text-align:left">介面返回值</th></tr>'
        html = '<html><body>介面自動化掃描,共有 ' + str(len(error_cases)) + ' 個異常介面,列表如下:' + '</p><table><tr><th style="width:100px;text-align:left">介面</th><th style="width:50px;text-align:left">狀態</th><th style="width:200px;text-align:left">介面地址</th></tr>'
        for test in error_cases:
            # html = html + '<tr><td style="text-align:left">' + test[0] + '</td><td style="text-align:left">' + test[1] + '</td><td style="text-align:left">' + test[2] + '</td><td style="text-align:left">' + test[3] + '</td></tr>'
            html = html + '<tr><td style="text-align:left">' + test[0] + '</td><td style="text-align:left">' + test[1] + '</td><td style="text-align:left">' + test[2] + '</td></tr>'
        send_email(html)
        print(html)
        with open ("report.html", "w") as f:
            f.write(html)
    else:
        print("本次測試,所有用例全部通過")
        send_email("本次測試,所有用例全部通過")


def get_conf():
    with open ("config.yml", "r", encoding='utf-8') as f:
        cfg = f.read()
        dic = yaml.load(cfg)
        # print(type(dic))
        # print(dic)
        sender = dic['email']['sender']
        receiver = dic['email']['receiver']
        smtpserver = dic['email']['smtpserver']
        username = dic['email']['username']
        password = dic['email']['password']
        print(sender, receiver, smtpserver, username, password)
        return sender, receiver, smtpserver, username, password


def send_email(text):
    today = time.strftime('%Y.%m.%d',time.localtime(time.time()))
    sender, receiver, smtpserver, username, password = get_conf()
    # subject為郵件主題 text為郵件正文
    subject = "[api_test]介面自動化測試結果通知 {}".format(today)
    msg = MIMEText(text, 'html', 'utf-8')
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = "".join(receiver)
    smtp = smtplib.SMTP()
    smtp.connect(smtpserver)
    smtp.login(username, password)
    smtp.sendmail(sender, receiver, msg.as_string())
    smtp.quit()


if __name__ == "__main__":
    # send_email("test")
    main()

思考:

需要改進的地方有很多:

1、增加日誌:匯入logging模組,程式碼裡面的print一通copy即可,自己嘗試哈

2、回寫excel表格:xlrd既然可以讀取excel文件,肯定可以寫入的。可以新增一列,每次執行完用例,將結果寫進去,自己去嘗試哈

3、request data type沒有做判斷,這裡偷懶了,因為只用了一個介面,而且大晚上在趕工,就沒有做判斷。可以參照判斷請求方式(get/post)來寫。

4、報告渣:1、可以嘗試使用htmlreport庫;2、也可以自己嘗試使用一些前端框架生成,如bootstrap

5、未做持續整合:什麼是持續整合?聽起來高大上,說白了就是找個資料庫或者其他玩意兒,將用例、執行結果等等,都儲存起來。python有很多庫,可以連線各種資料庫(mysql、mongoDB),讀取excel或者其他介面指令碼文件,存入資料庫;然後請求介面後,再從庫裡面讀取出來。balabala…

6、無介面:沒有介面,其實要不要都無所謂,畢竟只要維護一份excel表格即可。如果一定要的話,可以考慮使用django或者flask框架,構造web頁面,將用例的匯入匯出、新增、編輯、傳送請求,生成報告等等一系列操作,全部移交到前端。這就需要懂一點前端程式碼,如果有興趣,你也可以嘗試。

最後,這篇文章其實是借鑑大神的,原文地址為:https://testerhome.com/topics/4948

本文章在部落格留有備份,不對,微信公眾號的才是備份,你不知道編輯公眾號的文章是多麼痛苦的一件事,但既然說了,要做,再難也要搞下去不是。

我的公眾號:萬事屋丫