基於 python--selenium 與 requests 的 web ui/ 介面混合測試框架
一、序
接觸自動化測試已經好多年,也好多年停留在哪裡。
最開始跟大部分同學一樣,是從@蟲師那裡瞭解到併入門的,那時候python還沒這麼流行
最開始的時候還是用java跟ruby寫的,想想種種過往也無奈感嘆,為啥當時就不能好好學習一下java;
說到底還是當初太年輕,好了廢話不多說,我們開始吧!
二、開始
對於UI型別的自動化我一直不太感冒,可以說不支援;
去年年初兩個月寫了大幾百條UI自動化,半年後只有一半湊合使用
(業務發展太快就不要用了UI,當時痛定思痛編寫 WEB遍歷工具 接下來也會跟大家分享!)
今年下任務編寫UI自動化時,通過不斷思考,將UI與介面相結合能更好的在業務發生較大變化時,及時響應,及時調整;
# 三、程式碼原理圖
共計分為5層:
1、底層驅動------可以隨意切換底層驅動【UI底層框架-selenium,介面底層驅動--requests】
2、元素/介面胚層【儲存元素及介面yaml檔案;介面yaml資料進行初始化】
3、Case層
4、Scene層
5、TestCase層
其中Scene層和Case層可按照業務複雜度及個人編寫愛好進行整合。
四、TestCase層程式碼
- 每個TestCase都可以獨立執行;
- 每個Scene方法會返回True或False;
- 通過self.assertTrue()判斷是否執行正確
class TestCaseRoleAdd(unittest.TestCase):
def setUp(self):
self.sm_first = SceneRoleAdd()
def test_1_role_add_delete(self):
self.assertTrue(self.sm_first.login_erp())
self.assertTrue(self.sm_first.add_role())
self.assertTrue(self.sm_first.allocate_function_button())
self.assertTrue(self.sm_first.delete_role())
print("Test finished-noReport:Pass")
def tearDown(self):
self.sm_first.close()
if __name__ == '__main__':
testSuite1 = unittest.TestLoader().loadTestsFromTestCase(TestCaseRoleAdd)
suite = unittest.TestSuite(testSuite1)
unittest.TextTestRunner(verbosity=2).run(suite)
五、Scene層程式碼
- 主要對Case層程式碼進行拼接組合;
- 此層的出現主要是讓Case層能夠實現PO,這樣出現問題時,能夠快速查詢。
class SceneRoleAdd():
step_role = StepRole()
step_ERP_login = StepERPLogin()
# 登入ERP系統
@catch_exception
def login_erp(self):
self.step_ERP_login.login()
# 新增角色
@catch_exception
def add_role(self):
self.step_role.into_role()
self.step_role.add_role()
# 角色新增功能及按鈕
@catch_exception
def allocate_function_button(self):
role_name = self.step_role.role_name
self.step_role.search_role(role_name)
self.step_role.allocate_function()
self.step_role.search_role(role_name)
self.step_role.allocate_button()
# 刪除角色
@catch_exception
def delete_role(self):
role_name = self.step_role.role_name
self.step_role.search_role(role_name)
self.step_role.delete_role()
self.step_role.delete_role_verify(role_name)
# 退出瀏覽器
@catch_exception
def close(self):
self.step_role.close()
if __name__ == '__main__':
pass
裝飾器--捕獲Scene層異常
mLog = log.Log()
mTag = 'base_scene'
# 採集操作日誌,捕獲異常
+ 捕獲異常的同時進行截圖操作
def catch_exception(origin_func):
def wrapper(self, *args, **kwargs):
try:
print(f"Test---{origin_func.__name__} start")
origin_func(self, *args, **kwargs)
print(f"Test---{origin_func.__name__} end")
return True
except Exception as err:
traceback.print_exc()
self.step_ERP_login.sc_shot(origin_func.__name__)
mLog.log(mTag, f"Test---{origin_func.__name__} err:" + str(err))
return False
return wrapper
六、Case層程式碼
- 每個Case層方法會繼承一個Base_step,以便通過Base_step型別進行方法擴充套件
- Case是此框架中的核心所有的錯誤均需定位到此層
class StepRole(Base_step):
role_name = "test" + datetime.today().strftime("%Y%m%d%H%M%S")
loginName = None
def __init__(self):
super(StepRole,self).__init__()
@property
def userId(self):
return self.yaml_config_data.get_key_by_str_list(self.env_name + '.TEST_ERP.userId')
def into_role(self):
self.into_menu(menu_role)
self.isElementExistXpathByName(menu_depot_assert)
self.switch_to_frame(1)
def add_role(self):
# 增加按鈕
self.id_click(role_add_btn)
#
self.xpath_input(role_add_role_name, self.role_name)
# 儲存
self.id_click(role_add_save_btn)
def allocate_function(self):
self.id_click(role_allocate_func_btn)
# self.switch_to_default()
self.xpath_await_visibility(role_allocate_frame)
self.xpath_switch_to_frame(role_allocate_frame)
self.xpath_click(role_allocate_all)
self.id_click(role_allocate_save_btn)
self.xpath_click(role_allocate_assure_btn)
self.switch_to_parent_frame()
七、元素及介面層
1. 元素層
1.1 Selenum 驅動
- 所有的selenium方法在此層進行封裝以便將來選擇其他框架是能夠快速更換。
class BaseSelenium(object):
driver = webdriver.Chrome()
driver.maximize_window()
driver.implicitly_wait(10)
@base_log_aop
def xpath_input(self, value, input=''):
self.input_data('xpath', value, input)
@base_log_aop
def xpath_click(self, value):
self.click('xpath', value=value)
@base_log_aop
def xpath_text_click(self, value):
value = f'//*[text()="{value}"]'
self.click('xpath', value=value)
@base_log_aop
def xpath_double_click(self, value):
self.double_click('xpath', value=value)
@base_log_aop
def elements_index_click(self, locate_type, value, index):
self.get_element_from_elements(locate_type, value, index).click()
@base_log_aop
def xpath_elements_click(self, value):
locate_elements = self.locate_elements('xpath', value)
for i in locate_elements:
i.click()
# 裝飾器儲存相關底層操作
def base_log_aop(origin_func):
def wrapper(self, *args, **kwargs):
mLog.log("BaseSelenium", f"{origin_func.__name__}:" + ','.join([str(i) for i in args]))
return origin_func(self, *args, **kwargs)
return wrapper
1.2 定位元素
- 定位元素建議使用一種型別就可以,當前系統中使用id進行定位越來越少,CSS或xpath選一種吧。
menu_role = ['系統', '角色管理']
menu_role_assert = '角色列表'
role_add_btn = "addRole"
# 角色名稱
role_add_role_name = '//form[@id="role"]//tr//span/input[1]'
role_add_save_btn = "saveRole"
# 分配功能
role_allocate_frame = '//iframe[@class="cboxIframe"]'
role_allocate_func_btn = "btnSetFunctions"
role_allocate_all = '//ul[@id="tt"]/li/div/span[3]'
role_allocate_save_btn = 'btnOK'
role_allocate_assure_btn = '//span[text()="確定"]'
# 分配按鈕
role_allocate_button_btn = "btnSetPushBtn"
role_allocate_button_pos = '//input[@type="checkbox"]'
2. 介面層
2.1 介面底層驅動
- 這個介面方法已經使用很長時間了,沒毛病
def request_fun(http, api_path, json_p, change_dict, special=1):
api_data = ERPYaml(api_path)
mode = api_data.get_key_by_str_list('request.method')
url = api_data.get_key_by_str_list('request.url')
postData = api_data.get_key_by_str_list('request.json')
headers = api_data.get_key_by_str_list('request.headers')
if change_dict != {} and change_dict is not None:
# url = str_change_data(url, change_dict)
#
if special == 1:
postData = dict_change_data(postData, change_dict)
url = str_change_data(url, change_dict)
#
elif special == 2:
postData = special_1_postdata(postData, change_dict)
url = str_change_data(url, change_dict)
#
elif special == 3:
postData = dict_change_data(postData, change_dict)
url = str2_change_data(url, change_dict)
elif special == 4:
postData = special_2_postdata(postData, change_dict)
url = str_change_data(url, change_dict)
else:
pass
headers = dict_change_data(headers, change_dict)
url = http + url
# 先區分資料傳送方式(json or para)
# 再區分是GET or POST
mLog.log("request_fun", "mode:%s" % mode)
mLog.log("request_fun","url:%s" % url)
mLog.log("request_fun", "postData:%s" % postData)
mLog.log("request_fun", "headers:%s" % headers)
if json_p == 1 or json_p == '1':
postData = json.dumps(postData)
if mode == 'POST':
reponse_data = requests.post(url=url, data=postData, headers=headers)
elif mode == "GET":
reponse_data = requests.get(url=url, data=postData, headers=headers)
else:
raise AttributeError(u'mode輸入錯誤,mode=%s' % mode)
elif json_p == 0 or json_p == '0':
if mode == 'POST':
reponse_data = requests.post(url=url, params=postData, headers=headers)
elif mode == "GET":
reponse_data = requests.get(url=url, params=postData, headers=headers)
else:
raise AttributeError(u'mode輸入錯誤,mode=%s' % mode)
else:
raise AttributeError(u'json_p輸入錯誤,json_p=%s' % json_p)
# 獲取介面返回資料中的資料,進行全域性化(
# 新增介面時需要新增引數名稱(global_name)及正在表示式(regular_input)
mLog.log("request_fun", f"reponse_data:{reponse_data.text}")
return reponse_data
2.2 介面底層封裝
- 這裡相當於對yaml中儲存的http介面進行初始化
class APIRealization(BaseAPI):
api_path = mAPIPathERP
yaml_path = mYaml
role_add = api_path + os.sep + 'a5_role_add.yml'
role_add_functions = api_path + os.sep + 'a6_role_add_functions.yml'
# 新增角色
def response_role_add(self, change_dict):
change_dict.update(self.local_change_dict)
return request_fun(self.url_header, self.role_add, 0, change_dict, 2)
# 角色新增功能
def response_role_add_functions(self, change_dict):
change_dict.update(self.local_change_dict)
return request_fun(self.url_header, self.role_add_functions, 0, change_dict, 2)
2.3 yaml檔案
- 儲存http介面的檔案,yaml檔案作為配置檔案使用的頻率越來高,主要還是好用。
request:
url: /role/add
method: POST
headers:
Content-Type: "application/x-www-form-urlencoded"
Cookie: $Cookie
json:
info: "{\"name\":\"$role_name\"}"
相關文章
- 介面測試框架Requests框架
- python+requests介面測試基礎Python
- 基於 Pytest+Requests+Allure 實現介面自動化測試
- 一種基於 cypress 的 UI 自動化測試框架UI框架
- pytest+python3+requests+jenkins+git+allure介面測試框架PythonJenkinsGit框架
- Python + requests + unittest + ddt 進行介面自動化測試的框架Python框架
- 基於Selenium+Python的web自動化測試框架PythonWeb框架
- 關於Web端-UI自動化測試WebUI
- 關於介面測試——自動化框架的設計與實現框架
- FastAPI(43)- 基於 pytest + requests 進行單元測試ASTAPI
- 基於影像差異識別與頁面自動遍歷的開源 web ui 測試庫WebUI
- 測試中,介面測試的必要性與介面測試的基礎用例設計
- 一個基於多介面的業務自動化測試框架框架
- 基於Python+requests搭建的自動化框架-實現流程化的介面串聯Python框架
- 基於事件驅動的測試框架ETS事件框架
- Web測試框架SeleniumBaseWeb框架
- 基於 Web 引擎擴充套件技術的 RTC 混合開發框架實踐Web套件框架
- 介面測試檔案上傳 (python+requests)Python
- 介面測試檔案上傳(python+requests)Python
- 基於 HttpRunner + Django + Vue + Element UI 的介面自動化測試平臺,生產可用HTTPDjangoVueUI
- 『居善地』介面測試 — 7、介面自動化測試框架的設計與實現框架
- web ui 框架WebUI框架
- 基於Python的介面自動化-unittest測試框架和ddt資料驅動Python框架
- 基於 Django 的 Dubbo 介面測試工具平臺Django
- 淺談web介面測試Web
- python+requests進行get、post方法介面測試Python
- python+requests 進行 get、post 方法介面測試Python
- 『居善地』介面測試 — 3、Requests庫介紹
- 介面自動化使用requests生成測試報告測試報告
- 介面測試框架選擇框架
- 介面測試之unittest框架框架
- [Hacker News 週報] Deno 原生 SSR web 框架;基於 Vite 的單元測試框架;程式設計師社交新嘗試Web框架Vite程式設計師
- Apifox介面測試教程(一)介面測試的原理與工具API
- Java Web UI框架JavaWebUI框架
- Dore 混合應用框架 —— 基於 React Native 的混合應用遷移方案框架React Native
- 銀行軟體測試:基於網際網路金融平臺的測試框架設計與分析框架
- App測試、Web測試和介面測試一般測試流程APPWeb
- 基於 RF 的 WEB 版自動管理測試平臺Web