1. 程式人生 > >Python做接口自動化測試框架

Python做接口自動化測試框架

success pre gin HERE settings 不同 請求頭 toc encode

框架結構如下:

Test_Api_Project
|
|---base.py
|---base_api
|   |---register_api.py

|   |---send_sms_code_api.py
|---settings.py
|---test_case
|   |---test_register_api.py

|---utilities

|  |---conn_db.py

|  |---user.py

一、先說settings配置文件,與WEB端自動化類似,settings文件中存放整個項目的配置,如:被測項目域名、數據庫地址、redis地址、APP版本號、請求頭等。

# env config
ENV = test APP_VERSION = 2.3.7 HEADERS = {content-type: application/x-www-form-urlencoded; charset=UTF-8} # test url test config API_TEST_BASE_URL = "http://api.abc.com" # redis config REDIS_HOST = ‘‘ REDIS_PORT = ‘‘ # mysql config DB_HOST = ‘‘ DB_PORT = ‘‘ DB_USER = ‘‘ DB_PASSWORD = ‘‘

二、base.py

該文件中主要是對測試url的處理、對常用的請求類型重新封裝(如:GET、POST等)

# -*- coding:utf-8 -*-
import json,requests
import settings


class BaseApi(object):
    url = ""
    base_url= settings.API_TEST_BASE_URL

    def __init__(self,url_params=None):
        if not url_params:
            url_params = []
        self.url_params 
= url_params self.response = None self.base_url = self.base_url # 拼接url def api_url(self): if not self.url: raise RuntimeError("no url been set") return self._get_url() def _get_url(self): format_url = self.url.format(self.url_params) return "{0}{1}".format(self.base_url, format_url) # 封裝POST請求類型 def post(self, data=None): if not data: data = {} base_param = self.build_base_param() custom_param = self.build_custom_param(data) data.update(base_param) data.update(custom_param) self.response = requests.post(url=self.api_url(), data=data, headers=settings.HEADERS) return self.response # 封裝GET請求類型 def get(self,data=None): if not data: data={} base_param = self.build_base_param() custom_param = self.build_custom_param(data) data.update(base_param) data.update(custom_param) response = requests.get(url=self.api_url(),params=data) return self.response # 獲取回參中狀態碼 def get_code(self): if self.response: return json.loads(self.response.text)[code] # 獲取HTTP狀態碼 def get_status_code(self): if self.response: return self.response.status_code # 獲取回參中message def get_response_message(self): if self.response: return json.loads(self.response.text)[msg] # 所有接口共有的入參,比如:app_version、token等 def build_base_param(self): return { "app_version": SETTINGS.APP_VERSION, "token":"" } # 被測接口除公共參數外所需的其余參數 def build_custom_param(self, data): return {}

三、conn_db.py 連接數據庫

import pymysql.cursors,settings


def execute(sql, params=None, db=‘‘, is_fetchone=True):
    # Connect to the database
    connection = pymysql.connect(host=settings.DB_HOST,
                                 port=settings.DB_PORT,
                                 user=settings.DB_USER,
                                 password=settings.DB_PASSWORD,
                                 db=db,
                                 autocommit=True,
                                 charset=utf8mb4,
                                 cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            cursor.execute(sql, params)
            if is_fetchone:
                return cursor.fetchone()
            else:
                return cursor.fetchall()
    finally:
        connection.close()

user.py文件主要是對數據庫中用戶相關的一些操作

# -*- coding:utf-8 -*-
import db

def get_sms_captcha(mobile):
    # 獲取短信驗證碼
    sms_captcha = db.execute(select code from send_sms_code where mobile=%s order by id desc,params=(mobile))
    return sms_captcha[code]

def delete_user(mobile):
    # 刪除用戶
    db.execute(delete from user where mobile=%s,params=(mobile))

四、下面以註冊接口為例子

因註冊時需要獲取短信驗證碼,所以除了調用註冊接口之外,還需要調用獲取短信驗證碼皆苦,在base_api下新建register_api.py、send_sms_code_api.py,內容如下:

register_api.py

# -*- coding:utf-8 -*-
from base_api.base_api import BaseApi
import settings

class RegisterApi(BaseApi):
    url = /home/register
    
    #對BaseApi類中build_custom_param方法重寫
    def build_custom_param(self, data):
        return {login_name:data[login_name],password:data[password],code:data[code],nickname:data[nickname]}

send_sms_code_api.py

# -*- coding:utf-8 -*-
from base_api.base_api import BaseApi
import settings


class SendSmsCaptcha(BaseApi):
    url = /user/sendsms

    def build_custom_param(self, data):
        return {type: data[type], phone: data[phone]}

對兩個接口的url地址和所需要的入參都已經封裝好了,接下來開始寫case。

在test_case下新建test_register_api.py

下面是一個註冊成功的例子

# -*- coding:utf-8 -*-
from unittest import TestCase
from base_api.register_api import RegisterApi
from base_api.send_sms_code_api import SendSmsCaptcha
from utilities import user
import settings,json



class TestRegisterApi(TestCase):
    new_mobile = 13000000001
    password = 123abc
    nick_name = XiangXi


    def test_register_success(self):
        # 調用發送短信驗證碼接口
        send_sms_code_api = SendSmsCaptcha()
        send_sms_code_api.post({type:register,phone:self.new_mobile})

        # 校驗發送短信驗證碼接口HTTP狀態碼為200
        self.assertEqual(send_sms_code_api.get_status_code(),200)

        # 校驗發送短信驗證碼接口反參中code為0,代表成功(不同項目該字段值不一定為0)
        self.assertEqual(send_sms_code_api.get_code(),0)

        # 通過數據庫獲取短信驗證碼
        sms_code = user.get_sms_captcha(self.new_mobile)

        # 調用註冊接口
        register_api = RegisterApi()
        response = register_api.post({login_name:self.new_mobile,password:self.password,code:sms_code,nickname:self.nick_name})

        # 校驗註冊接口HTTP狀態碼為200
        self.assertEqual(send_sms_code_api.get_status_code(),200)

        # 校驗註冊接口反參中code為0
        self.assertEqual(register_api.get_code(),0)

        # 校驗反參中手機號、登錄名與用戶昵稱是否與入參值一樣
        identity_obj = json.loads(response.content)[result][identity_obj]
        self.assertEqual(identity_obj[nickname],self.nick_name)
        self.assertEqual(identity_obj[login_name],self.new_mobile)
        self.assertEqual(identity_obj[mobilephone],self.new_mobile)

    def tearDown(self):
        user.delete_user(self.new_mobile)

最後和WEB端類似,通過teardown()方法將新註冊的用戶在數據庫中刪除,防止該手機號下次執行case時候報手機號已被註冊。

為了更好了驗證通過接口註冊的用戶信息,也可以調用登錄接口測試一下註冊接口反參中的用戶名是否可以正常登陸返回token。

五、如果調用接口的前提需要用戶登錄,那麽就需要BaseApi類中build_base_param方法中的token有一個有效的值,否則調用接口時可能會提示token錯誤或者用戶未登錄等,這種情況下可以新建一個LoginBaseApi類,繼承BaseApi,代碼如下:

假設對登錄接口已經進行了封裝,入參為login_name和password

# -*- coding:utf-8 -*-
from base_api.base_api import BaseApi
from base_api.login_api import LoginApi

class LoginBaseApi(BaseApi):
    def __init__(self, login_name,password, *args, **kwargs):
        super(LoginBaseApi, self).__init__(*args, **kwargs)
        self.login_name = login_name
        self.password = password

    def build_base_param(self):
        base_param = super(LoginBaseApi, self).build_base_param()
        response = LoginApi().post(self.login_name, self.password)
        base_param[token] = token
        return base_param

這時,底層類就有兩個,一個是BaseApi() 一個是LoginBaseApi(),需要登錄的接口在封裝入參時繼承LoginBaseApi,不需要登錄的接口繼承BaseApi

不同項目接口的入參格式,反參格式可能會不同,僅供參考

Python做接口自動化測試框架