背景
雖然大家都已經使用了統一的關鍵字,但是在檢查了一些測試用例之后,還是發現因為大家對RF的熟悉程度不一導致的測試用例顆粒度差異很大的情況;而且在手動方式轉化測試用例過程中,有不少工作是完全重復的且意義不大的,所以我就想了一個使用腳本生成自動化測試用例的方式來解決這個問題。
實現思路
前一篇文章里提到過,我們使用了一個中間的Excel來生成關鍵字信息,所以我這邊的第一想法就是讓這個Excel發揮更大的作用 -- 即直接通過Excel生成標准的測試用例。
具體的思路也很簡單:
- 直接轉化我們填寫在Excel中的請求參數與預期結果。
- 以遞歸的形式處理多層級的數據,存儲到一個列表中,然后倒序寫到robot文件中
最關鍵的代碼如下:
- 以遞歸的形式處理多層級的數據,存儲到一個列表中,然后倒序寫到robot文件中
發送數據轉化
請求的數據如下:
{
"name": "Detector",
"age": 18,
"sex": "male",
"student": false,
"others": [{"nation": "CHINA"}]
}
我們需要對數據逐層進行解析,然后生成下面的這個數據)。
${others} create dictionary nation=CHINA
${others_list} create list ${others}
${param} create dictionary name=Detector age=${18} sex=male student=${False} others=${others_list}
注: RF腳本中所有的非特殊標示的數據皆為string,bool類型及int類型需要用${something}
包裹
因為請求數據是逐行執行,所以所有的參數定義都需要逐行進行賦值,受限於RF的語法,這一塊花了不少時間,最后使用兩個函數相互遞歸的方式解決。
_format_list
用於處理list, _format_dict
用於處理dict, temp_list
用於存儲生成的關鍵字信息,最后使用'\n'.join(temp_list) + "\n"
把所有的子關鍵字組裝成RF關鍵字代碼。
相關代碼:
代碼目錄結構如下:
def _gen_param_data(self, sheet_obj, param_key="param"):
"""
獲取Excel中發送參數的數據
:param sheet_obj:
:param param_key:
:return:
"""
str_params = sheet_obj.cell_value(1, 1)
str_method = sheet_obj.cell_value(1, 6)
if not len(str_params):
return ""
try:
# 轉化 json類型為Python標准類型
params = eval(str_params.replace("false", "False").replace("true", "True").replace("null", "None"))
if not len(params):
return ""
except Exception as f:
logger.warning("====================================================")
logger.error("=" + str(f))
logger.warning("====================================================")
params = ""
if str_method.upper() == "GET":
# 格式化get請求
format_str = self.format_get_params(params, param_key)
else:
# 格式化post請求
format_str = self.format_post_params(params)
return format_str
def format_post_params(self, params):
"""
格式化post請求傳遞數據
:param params:
:return:
"""
temp = []
# 格式化數據
if isinstance(params, dict):
self._format_dict(params, "param", temp)
temp.reverse()
if isinstance(params, list):
print("list")
self._format_list(params, "param", temp)
temp.reverse()
return '\n'.join(temp) + "\n"
def _format_list(self, _list, key_word, temp_list):
tem_str = " ${%s_list} create list " % key_word
if len(_list) > 0 and isinstance(_list, list):
for i in _list:
if isinstance(i, dict):
tem_str = " ${%s_list} create list ${%s}" % (key_word, key_word)
self._format_dict(i, key_word, temp_list)
elif isinstance(i, list):
self._format_list(i, key_word, temp_list)
else:
tem_str += self.format_bool_int(i) + " "
temp_list.insert(0, tem_str)
def _format_dict(self, _dict, key_word, temp_list):
tem_str = " ${%s} create dictionary " % key_word
if len(_dict) > 0 and isinstance(_dict, dict):
for k, v in _dict.items():
v = self.format_bool_int(v)
if isinstance(v, str):
tem_str += k + "=" + v + " "
if isinstance(v, dict):
tem_str += k + "=" + "${%s_dict} " % k
self._format_dict(v, "%s_dict" % k, temp_list)
if isinstance(v, list):
tem_str += k + "=" + "${%s_list} " % k
self._format_list(v, k, temp_list)
temp_list.insert(0, tem_str)
def format_get_params(params, param_key):
"""
格式化get請求傳遞數據
:param params:
:param param_key:
:return:
"""
format_str = " ${%s} set variable ?" % param_key
if isinstance(params, dict):
for k, v in params.items():
if isinstance(v, int) or isinstance(v, bool):
v = "${%s}" % v
format_str = format_str + str(k) + "=" + str(v) + "&"
if isinstance(params, list):
pass # 后續完善
return format_str.strip("&") + '\n'
用例初始化
首先,我們把每條用的引用數據寫到文件中。其中/env/env.robot
用於存放我們的配置文件。
def gen_testcase_init(target_robot_name):
"""
測試用例初始化內容
:param target_robot_name:
:return:
"""
if os.path.exists(target_robot_name):
os.remove(target_robot_name)
with open(target_robot_name, 'a') as f:
f.write('*** Settings ***' + '\n')
f.write('Documentation documentation' + '\n')
f.write('Suite Setup Setup func' + '\n')
f.write('Suite Teardown Teardown func' + '\n')
f.write('Test Setup log Test Setup' + '\n')
f.write('Test Teardown log Teardown' + '\n')
f.write('Resource ../../../Common/DT_Hb_kw/DT_Hb_kwRequests.robot' + '\n')
f.write('Resource ./env/env.robot' + '\n')
# f.write('Library TestLibrary' + '\n')
f.write('\n')
f.write('*** Variables ***' + '\n')
f.write('${OK} OK' + '\n')
f.write('\n')
f.write('*** Test Cases ***' + '\n')
前置條件及數據清理
def gen_end_keyword(target_robot_name):
"""
測試用例初始化內容
:param target_robot_name:
:return:
"""
with open(target_robot_name, 'a') as f:
f.write('*** Keywords ***' + '\n')
f.write('Setup func' + '\n')
f.write(' log setup func' + '\n')
f.write('Teardown func' + '\n')
f.write(' log teardown func' + '\n')
f.write('\n')
apitest/
├── Common
│ ├── DT_Hb_kw # 公共RF關鍵字存儲目錄
│ │ └── DT_Hb_kwRequests.robot
│ ├── DT_Hb_kw_excel # 公共過程Excel存儲目錄
│ │ ├── detector.xls
│ │ ├── detector_Get.xls
│ │ ├── detector_getBingo.xls
│ │ ├── detector_post.xls
│ │ └── detector_postName.xls
│ └── Testscript # 輔助腳本存儲目錄
│ ├── DT_Hb_kwRequests.robot
│ ├── common
│ │ ├── __init__.py
│ │ ├── gen_rf_demo_case.py
│ │ ├── gen_rf_kw.py
│ │ ├── gen_testcase.py
│ │ ├── har_parse.py
│ ├── common_testcase # 普通用例存儲目錄
│ │ └──
│ ├── display
│ │ ├── client.py
│ │ ├── ipsitter.xls
│ │ └── server.py
│ ├── har_files # har文件存儲目錄
│ │ └── 20190812-Demo.har
│ ├── logs
│ │ └── 2019-08-16.log
│ ├── rf_demo_cases # 生成的自動化用例存儲
│ ├── run.py
│ ├── source_xls
│ │ ├── new_keyword_excel # 用於生成關鍵字的Excel存儲目錄
│ │ └── templates # 模板Excel存儲目錄
│ │ ├── case_template.xls
│ │ └── kw_template.xls
│ └── utils
│ ├── __init__.py
│ ├── logger.py
│ └── operate_xls.py
├── Testcase
│ ├── DT_Hb_case
│ │ ├── Demo_server # 測試用例提交處
│ │ ├── Post_Demo # 測試用例提交處
│ │ └── _common # 項目公共關鍵字存儲目錄
│ │ ├── commonFun.robot
│ │ ├── common_func.py
│ │ └── env.robot