天天看點

python3接口自動化測試架構:python3+requests+資料驅動(json)

接口測試是單元測試的一個子集,但又不等同于單元測試。從測試的角度

來看,接口測試的價值在于其測試投入比單元測試少,而且技術難度也比單元測

試小。一般來說,接口測試的粒度要比單元測試更粗,它主要是基于子系統或者

子子產品的接口層面的測試。是以,接口測試需要測試的接口或者函數的數量會遠

遠小于單元測試,與此同時,接口定義的穩定性會遠遠高于類級别的函數。是以,

接口測試用例代碼的改動量也遠遠小于單元測試,代碼維護成本會比單元測試少

很多,因而測試的投入量會小很多。從另外一個層面來看,借助于接口測試,可

以保證子系統或子子產品在各種應用場景下接口調用的正确性,那麼子系統或子模

塊的産品品質也可以得到充分的保證。是以,接口測試是一種适度的白盒測試技

術,準确說它是一種灰盒測試,投入産出是非常理想的。

總的來說,接口測試是保證高複雜性系統品質的内在要求和低成本的經

濟利益的驅動作用下的最佳解決方案。主要展現在下面的二個方面:

首先,接口測試節省了測試成本。

其次,接口測試不同于傳統開發的單元測試,接口測試是站在使用者的角

度對系統接口進行全面高效持續的檢測。

(ps:以上有網絡搜尋和自己的總結)

好了,話不多說,接下來我們測試接口的整體思路是這樣的:

1、把接口的相關資料儲存在json檔案中。

2、我們通過去讀這個json檔案,來讀取接口的相關資訊,包括:路徑、參數、請求方式、比對值等。

3、基于requests封裝一個發送請求的方法。

4、使用裝飾器來進行具體的測試。

1 {
 2     "test_1_ip_api": {
 3         "url": "http://httpbin.org/ip",
 4         "assert": {
 5             "origin":"183.16.188.172"
 6         },
 7         "method": "get",
 8         "params": {
 9                         "a":"b"
10 },
11         "case": "測試httpbin的ip接口傳回正常"
12     }
13 }      

以上就是Json檔案的格式,一條這樣的資料,代表一條用例:

test_1_ip_api:算作是一個辨別。

url:接口的路徑。

assert:最後接口的比對值。

method:接口請求方式。

params:請求參數。ps:如果請求方式為post此處為data。

case:此條用例的名字,也就是測試的點。

(注:有些可能需要添加headers,這個使用者自己添加一下就行了,但是後面的代碼稍微修改一下。) 

當然有了檔案,那麼我們就需要把這個資料給讀出來。見如下代碼。

class ReadCaseData(object):

    def __init__(self,filename):
        self.filename = filename
        self.path = os.path.join(get_super_path(),'data',self.filename)
     

    @property
    def read_case_json(self) -> dict:
        '''
        擷取指定檔案的json資料
        :return:
        '''
        try:
            my_json = open(self.path,encoding='utf-8')
            json_data = json.load(my_json)
            return json_data
        except Exception as err:
            print(str(err))


    def get_case_content(self,first_key,second_key):
        '''
        擷取json相應值
        '''
        return self.read_case_json[first_key][second_key]

    def get_assert_keys(self,first_key):
        '''
        根據用例名取assert的keys
        '''
        return self.get_case_content(first_key,'assert').keys()


    def get_assert_value(self,first_key,assert_key):
        '''
        取assert的具體值
        '''
        return self.read_case_json[first_key]['assert'][assert_key]

    def get_case_name(self,first_key):
      
'''
        擷取用例名
        '''      
    return self.get_case_content(first_key,'case')      

以上就是讀取json檔案的各種資料,并根據需要封裝了一些方法。(思路:會根據不同的json檔案資料,給這個json檔案取相應的檔案名,那麼此處的類就是把這個檔案名初始化,然後讀取此檔案相關的一些資料。)

(注:1、get_super_path()是寫的一個擷取根目錄的方法。)

那麼資料讀出來了,要怎麼運用呢,那麼我們就需要借用一下requests(這個庫應該不用說了,您如果在找這樣的文章,那麼我肯定知道這個庫羅。)。

def get_base_url():
    if IniHelper('server.ini').get_value('server', 'select') == 'test':
        return IniHelper('server.ini').get_value('server', 'test')
    elif IniHelper('server.ini').get_value('server', 'select') == 'formal':
        return IniHelper('server.ini').get_value('server', 'formal')
    else:
        raise AttributeError("配制檔案讀取出錯!請檢查!")


class Myrequests(ReadCaseData):

    def __init__(self, filename, case_name):
        ReadCaseData.__init__(self, filename)
        self.case_name = case_name
        self.base_url = get_base_url()


    def make_requests_template(self):
        '''
        根據讀取到的json檔案中的method,來發送不到的請求,目前隻能發送get和post請求
        :return:
        '''
        if self.get_case_content(self.case_name, 'method').lower() == 'get':
            body = {}
            body['url'] = self.base_url + self.get_case_content(self.case_name, 'url')
            body['params'] = self.get_case_content(self.case_name, 'params')
            return self.get(**body)
        elif self.get_case_content(self.case_name, 'method').lower() == 'post':
            body = {}
            body['url'] = self.base_url + self.get_case_content(self.case_name, 'url')
            body['params'] = self.get_case_content(self.case_name, 'data')
            return self.post(**body)
        else:
            raise AttributeError("錯誤的請求方法, 請檢查配置檔案中的請求方法, 目前隻支援['GET', 'POST']")

    def get(self, **kw):
        '''
        使用requests發送get請求
        :param kw: 參數需要傳一個字典表
        :return: 
        '''
        return requests.get(**kw)

    def post(self, **kw):
        '''
        使用requests發送post請求
        :param kw:參數需要傳一個字典表
        :return:
        '''
        return requests.post(**kw)

    @property
    def get_json(self):
        '''
        傳回接口的json資料
        :return:
        '''
        try:
            return self.make_requests_template().json()
        except Exception as e:
            print('json format error' + str(e))

    @property
    def get_status_code(self):
        '''
        傳回接口發送後的status_code值
        :return:
        '''
        return self.make_requests_template().status_code
      

這裡首先是繼承了我們剛剛封裝的讀取的json檔案的類,因為繼承過後就可以使用他的一些方法,然後通過讀取裡面的資料來發送請求等。

注:我這裡的接口的伺服器位址是通過讀取配置檔案裡的,這個代碼就不分享了,大家網找一下,很多。(随意啦)

ps:1、IniHelper是封裝的一個讀取配置檔案的類。

  2、此處隻封裝了get和post,當然也可以繼續添加其它(比如:put、delete等等!!)

資料也讀出來了,發送請求的方法也封裝好了,那麼接下來要怎麼用這些資料來測試呢?

def test_case_run(data_file_name, test_case_key):
    def _test_case_run(func):
        def wrap(self):
            r = Myrequests(filename=data_file_name, case_name=test_case_key)
            self.r = r
            self._testMethodDoc = r.get_case_name(test_case_key)
            self.status_code = r.get_status_code
            self.json = r.get_json
            self.assert_key = r.get_assert_keys(test_case_key)
            log.get_log(test_case_key).info(f'開始測試:{test_case_key}')
            log.get_log(test_case_key).info('比對:status_code')
            self.assertEqual(r.get_status_code, 200)
            for key in r.get_assert_keys(test_case_key):
                log.get_log(test_case_key).info(f'比對:{key}')
                self.assertEqual(get_dict_value(key, **r.get_json), r.get_assert_value(test_case_key, key))
            func(self)

        return wrap

    return _test_case_run      

以上就是根據讀取的資料和發送請求的方法封裝的一個裝飾器。

其中:

log:這個是基于logging封裝的記錄log的類。

get_assert_value():這個是寫的一個根據json資料裡讀出來的assert的資料裡面的Key,來擷取實際接口裡面傳回的value的方法。

(注:1、json裡面的assert的資料至少一條,有可能多條。2、使用Key從實際結果中擷取value時,可能是多級字典,是以此方法使用了遞歸)

最後就是我們使用python的unittest來進行測試了。

from src.testcase.method.base_test import BaseTest
from src.testcase.method import wraps
import unittest


class TestApi(BaseTest):

    @wraps.test_case_run('data.json', 'test_1_ip_api')
    def test_01(self):
        self._testMethodDoc = self._testMethodDoc

    @wraps.test_case_run('data.json', 'test_2_headers_api')
    def test_02(self):
        self._testMethodDoc = self._testMethodDoc

    @wraps.test_case_run('data.json', 'test_3_post_api')
    def test_03(self):
        self._testMethodDoc = self._testMethodDoc


if __name__ == '__main__':
    unittest.main(verbosity=2)      

以上就是實際具體的用例,我們在每一條case上使用裝飾器,然後裝飾器有二個參數,這裡說明一下:1、就是此case對應的檔案名。2、就是此case在json檔案中對應的辨別。

建議:

1、每一個接口對應一個json檔案,然後在這個json檔案中為每一條case添一個辨別。(文中的Json檔案就是一個接口對應的一條case。)

2、所有的json檔案放在同一個檔案夾下。

3、每一個接口對應一個測試檔案。

4、結果最後采用htmltestrunner的html報告輸出。

5、把報告通過email發送。

6、使用jenkins在伺服器部署。

歡迎大家指證!!!

轉載于:https://www.cnblogs.com/Alin-2016/p/8891471.html