def find_element_by_locator_adapter(self, selector): logger.info('开始根据元素定位器:%s 自定位元素' % selector) if not selector: logger.warn('无法通过非法选择器:%s定位元素' % selector) return None try: if (selector[:3]).lower() == 'id=': selector = selector[3:] logger.info('find_element_by_id:%s' % selector) element = selenium_util.find_element_by_id(selector) return element elif (selector[:6]).lower() == 'xpath=': selector = selector[6:] logger.info('find_element_by_xpath:%s' % selector) element = selenium_util.find_element_by_xpath(selector) return element elif (selector[:5]).lower() == 'name=': selector = selector[5:] logger.info('find_element_by_name:%s' % selector) element = selenium_util.find_element_by_name(selector) return element elif (selector[:5]).lower() == 'link=': selector = selector[5:] logger.info('find_element_by_link_text:%s' % selector) element = selenium_util.find_element_by_link_text(selector) return element elif (selector[:10]).lower() == 'link_text=': selector = selector[10:] logger.info('find_element_by_link_text:%s' % selector) element = selenium_util.find_element_by_link_text(selector) return element elif (selector[:4]).lower() == 'css=': selector = selector[4:] logger.info('find_element_by_css_selector:%s' % selector) element = selenium_util.find_element_by_css_selector(selector) return element elif (selector[:9]).lower() == 'tag_name=': selector = selector[9:] logger.info('find_element_by_tag_name:%s' % selector) element = selenium_util.find_element_by_tag_name(selector) return element elif (selector[:11]).lower() == 'class_name=': selector = selector[11:] logger.info('find_element_by_class_name:%s' % selector) element = selenium_util.find_element_by_class_name(selector) return element elif (selector[:17]).lower() == 'partial_link_text=': selector = selector[:17] logger.info('find_element_by_partial_link_text:%s' % selector) element = selenium_util.find_element_by_partial_link_text( selector) return element else: logger.info('error, 非法选择器:%s:' % selector) return None except Exception as e: logger.error('定位元素出错,%s' % e) return None
def exectue_element_operator_in_step(self, selector1, selector2, command, inparameters, expected_result): logger.info('操作:%s' % command) if inparameters: # 有输入参数 logger.info('正在对输入参数:%s 进行参数处理' % inparameters) inparameters = other_tools.conver_data_from_phppage(inparameters) inparameters = inparameters.split('||') logger.info('处理后的输入参数为:%s ' % inparameters) command = command.lower() if command == '查找': try: element = self.find_element(selector1, selector2) if element: return ('Pass', '找到元素') else: return ('Fail', '未找到元素') except Exception as e: logger.error('查找元素出错') return ('Fail', e) elif command == '输入': try: element = self.find_element(selector1, selector2) if type(inparameters) == type( []) and inparameters[0] and element: element.send_keys(inparameters[0]) return ('Pass', '输入数据成功') else: return ('Fail', '输入数据失败') except Exception as e: logger.error('执行输入操作出错:%s' % e) return ('Fail', e) elif command == '清空': try: element = self.find_element(selector1, selector2) if element: element.clear() return ('Pass', '清空数据成功') else: return ('Fail', '清空数据失败') except Exception as e: logger.error('执行输入操作出错:%s' % e) return ('Fail', e) elif command == '点击': try: element = self.find_element(selector1, selector2) if element: element.click() return ('Pass', '点击成功') else: return ('Fail', '点击操作失败') except Exception as e: logger.error('执行点击操作出错:%s' % e) return ('Fail', e) else: logger.error('command为空,或者填写错误') return ('Fail', 'comman为空或者填写错误')
def execute_insert(self, query, data): logger.info('query:%s data:%s' % (query, data)) try: db_cursor = self.dbconn.cursor() db_cursor.execute(query, data) db_cursor.execute('commit') db_cursor.close() return ('', True) except Exception as e: logger.error('执行数据库插入操作失败:%s' % e) db_cursor.execute('rollback') db_cursor.close() return (e, False)
def execute_create(self, query): logger.info('query:%s' % query) try: db_cursor = self.dbconn.cursor() db_cursor.execute(query) db_cursor.execute('commit') db_cursor.close() return ('', True) except Exception as e: logger.error('创建数据库表操作失败:%s' % e) db_cursor.execute('rollback') db_cursor.close() return (e, False)
def set_driver(self, browser_type): try: browser_type = browser_type.lower() if 'ie' == browser_type: self.browser_type = 'Ie' self.driver = webdriver.Ie() elif 'chrome' == browser_type: self.browser_type = 'Chrome' self.driver = webdriver.Chrome() elif 'firefox' == browser_type: self.browser_type = 'Firefox' self.driver = webdriver.Firefox() except Exception as e: logger.error('设置浏览器驱动出错:%s' % e)
def execute_update(self, query, data): query = query % data logger.info(query) try: db_cursor = self.dbconn.cursor() db_cursor.execute(query) db_cursor.execute('commit') db_cursor.close() return ('', True) except Exception as e: logger.error('执行数据库更新操作失败:%s' % e) db_cursor.execute('rollback') db_cursor.close() return (e, False)
def download_file(self, local_path, remote_file_path): last_file_name = os.path.split(remote_file_path)[1] local_file_path = os.path.join(local_path, last_file_name) if os.path.isdir(local_file_path): logger.error('请选择文件保存目录路径') return if os.path.isfile(local_file_path): local_file_path = local_file_path.replace('\\', '/') logger.error('文件:%s 已存在' % local_file_path) return with open(local_file_path, 'wb') as file_handle: self.ftp.retrbinary('RETR %s' % remote_file_path, file_handle.write)
def select_one_record(self, query, data=""): '''返回结果只包含一条记录''' logger.info('query:%s data:%s' % (query, data)) try: db_cursor = self.dbconn.cursor() if data: db_cursor.execute(query, data) else: db_cursor.execute(query) query_result = db_cursor.fetchone() db_cursor.close() return (query_result, True) except Exception as e: logger.error('执行数据库查询操作失败:%s' % e) db_cursor.close() return (e, False)
def get_screenshot_as_file(self, step_order, testcase_run_history_id): curr_time = time.strftime('%Y%m%d%H%M%S', time.localtime()) # 截图时间 sub_path = 'tp' + str(self.test_plan_id) image_name = sub_path + '_tc' + str(self.test_case_id) + '_step' + str(step_order) + '_' + str(testcase_run_history_id) + '_' + str(curr_time) base_path = screenshot_dir_path + sub_path + '\\tc' + str(self.test_case_id) + '\\' if not os.path.isdir(base_path): other_tools.mkdirs_once_many(base_path) screenshot_pic_path = base_path + image_name + '.png' result = selenium_util.get_screenshot_as_file(screenshot_pic_path) if result[0] == 'Pass': logger.info('截图文件保存路径为:%s' % screenshot_pic_path) return screenshot_pic_path else: logger.error('截图失败:%s' % result[0]) return ''
def run(self): run_time = '%d-%02d-%02d %d:%d:%d' % time.localtime()[0:6] # 记录执行时间 testcase_run_result = 'Block' # 运行具体的用例步骤 # 1.获取用例所有测试步骤 test_case_steps = self.get_test_case_steps() if test_case_steps == []: logger.warn('未查找到归属用例[id=%s]的步骤' % self.test_case_id) return # 2.运行测试用例步骤 test_case_step = TestCaseStep(self.test_case_id, self.test_plan_id) logger.info('开始执行测试步骤') for tc_step in test_case_steps: step_run_result = test_case_step.run_tc_step( self.browser_type, self.db, tc_step, self.run_history_id, self.runmode) testcase_run_result = step_run_result if step_run_result != 'Pass': #步骤运行失败,提前终止 logger.info('用例[id:%s]步骤[步序:%s] 运行失败,停止运行用例' % (self.test_case_id, tc_step[0])) break if self.runmode == '运行流水': # 更新记录 logger.info('测试步骤执行完毕,正在更新用例运行结果') query_update = 'UPDATE testcase_reporter SET browserType=\'%s\', runTime=\'%s\', runResult=\'%s\' WHERE runHistoryId=\'%s\' AND testcaseId=\'%s\'' query_data = (self.browser_type, run_time, testcase_run_result, self.run_history_id, self.test_case_id) result = self.db.execute_update(query_update, query_data) if result[1] != True: logger.error('更新用例运行结果到测试用例运行报表失败:%s' % result[0]) return else: #新增记录 logger.info('测试步骤执行完毕,正在记录用例运行结果到测试用例运行报表') query_insert = 'INSERT INTO testcase_reporter' + '(runHistoryId, browserType, testcaseId, testplanId, runTime, runResult) ' \ 'VALUES(%s, %s, %s, %s, %s, %s)' query_value = (self.run_history_id, self.browser_type, self.test_case_id, self.test_plan_id, run_time, testcase_run_result) restult = self.db.execute_insert(query_insert, query_value) if restult[1] != True: logger.error('记录用例运行结果到测试用例运行报表失败:%s' % restult[0]) return
def __init__(self, config_file, db): config = configparser.ConfigParser() # 从配置文件中读取数据库服务器IP、域名,端口 config.read(config_file, encoding='utf-8') self.host = config[db]['host'] self.port = config[db]['port'] self.user = config[db]['user'] self.passwd = config[db]['passwd'] self.db_name = config[db]['db'] self.charset = config[db]['charset'] try: self.dbconn = mysql.connector.connect(host=self.host, port=self.port, user=self.user, password=self.passwd, database=self.db_name, charset=self.charset) except Exception as e: logger.error('初始化数据连接失败:%s' % e) sys.exit()
def download_dir(self, local_path, remote_path): if os.path.isfile(local_path): logger.error('出错了,请选择文件保存位置') return local_path = local_path.strip() # 以防万一,去除首尾空格 remote_path = remote_path.strip() remote_path = remote_path.rstrip('/') remote_path = remote_path.rstrip('\\') last_dir = os.path.basename(remote_path) local_path = os.path.join(local_path, last_dir) local_path = local_path.replace('/', '\\') # 转为Windows标准路径 # 如果本地客户端不存在该路径,则创建对应路径下的目录 if not os.path.isdir(local_path): os.mkdir(local_path) sub_items = self.ftp.nlst(remote_path) for sub_item in sub_items: try: self.ftp.cwd(sub_item) #如果子项目为目录 self.download_dir(local_path, sub_item) except Exception: #非目录 self.download_file(local_path, sub_item)
def upload_folder(self, local_path='D:/python/webautotest/screentshot/', remote_path='/home/testftp'): if not os.path.isdir(local_path): logger.error('出错了,请选择要上传的文件夹') return local_path = local_path.strip() # 以防万一,去除首尾空格 local_path = local_path.rstrip('/') # 去除右部 / local_path = local_path.rstrip('\\') # 去除右部 \\ remote_path = remote_path.strip() remote_path = remote_path.rstrip('/') remote_path = remote_path.rstrip('\\') self.ftp.cwd(remote_path) last_dir = os.path.basename(local_path) remote_path = os.path.join(remote_path, last_dir) remote_path = remote_path.replace('\\', '/') # 转为linux标准路径 # 如果ftp服务器上不存在该路径,则创建对应路径下的目录 try: self.ftp.mkd(last_dir) except: logger.warn('dir: %s already exists' % last_dir) pass sub_items = os.listdir(local_path) for sub_item in sub_items: sub_item_path = os.path.join(local_path, sub_item) pass sub_items = os.listdir(local_path) for sub_item in sub_items: sub_item_path = os.path.join(local_path, sub_item) if os.path.isdir(sub_item_path): #如果子项目为目录 self.upload_folder(sub_item_path, remote_path) else: self.upload_file(sub_item_path, remote_path)
def run_test_plan(self, browser_type, db, run_mode, tp_run_history_id, test_plan_id=0): '''运行测试用例或测试计划 其中,run_mode为运行模式: run_tp - 运行测试计划, run_tp_fail_tc--运行测试计划中,所有失败的用例''' # 获取测试需要执行的测试用例ID列表 test_case_list = [] logger.info('正在获取本次测试需要执行的测试用列ID列表,测试计划名称') query_select = 'SELECT testcase_list, test_round_name FROM testcase_runround WHERE id = %s ' query_value = (test_plan_id, ) result = db.select_one_record(query_select, query_value) if result[1] != True or not result[0]: logger.warn('没找到归属于该测试计划的用例,停止运行计划') return 'Block' testcases_str = result[0][0] testplan_name = result[0][1] if testcases_str: test_case_str_list = testcases_str.split('~') # 形如 3~7~10~11 test_case_map = map(int, test_case_str_list) for id in test_case_map: test_case_list.append(id) logger.info('获取到的的测试用例id列表为: %s' % test_case_list) else: logger.warn('未获取到测试用例ID列表,请检查测试计划id(test_plan_id = %d)是否输入正确' % test_plan_id) logger.info('####################### 开始运行测试计划 #######################') # 构造测试用例对象 testcase_objects_list = [] for test_case_id in test_case_list: test_case = TestCase(browser_type, db, test_case_id, tp_run_history_id, run_mode, test_plan_id, testplan_name) testcase_objects_list.append(test_case) run_time = '%d-%02d-%02d %d:%d:%d' % time.localtime()[0:6] # 记录执行时间 mutex_lock = threading.RLock() mutex_lock.acquire() for test_case in testcase_objects_list: # 运行用例 test_case.start() # 等待用例运行完成 test_case.join() mutex_lock.release() logger.info( '####################### 测试计划已运行完毕 #######################') logger.info('正在统计测试计划执行的用例总数') query_select = 'SELECT count(testcaseId) FROM testcase_reporter WHERE runHistoryId = %s' query_value = (tp_run_history_id, ) result = db.select_one_record(query_select, query_value) if result[1] != True or not result[0]: logger.error('没找到执行过的用例') run_testcase_total = 0 else: run_testcase_total = result[0][0] logger.info('正在统计测试计划执行成功的用例总数') query_select = 'SELECT count(testcaseId) FROM testcase_reporter WHERE runHistoryId = %s AND runResult=\'Pass\'' query_value = (tp_run_history_id, ) result = db.select_one_record(query_select, query_value) if result[1] != True or not result[0]: logger.warn('没找到执行成功的用例') run_testcase_passed_total = 0 else: run_testcase_passed_total = result[0][0] logger.info('正在统计测试计划执行失败的用例总数') query_select = 'SELECT count(testcaseId) FROM testcase_reporter WHERE runHistoryId = %s AND runResult=\'Fail\'' query_value = (tp_run_history_id, ) result = db.select_one_record(query_select, query_value) if result[1] != True or not result[0]: logger.info('没找到执行失败的用例') run_testcase_failed_total = 0 else: run_testcase_failed_total = result[0][0] logger.info('正在统计测试计划执行被阻塞的用例总数') query_select = 'SELECT count(testcaseId) FROM testcase_reporter WHERE runHistoryId = %s AND runResult=\'Block\'' query_value = (tp_run_history_id, ) result = db.select_one_record(query_select, query_value) if result[1] != True or not result[0]: logger.info('没找到执行被阻塞的用例') run_testcase_blocked_total = 0 else: run_testcase_blocked_total = result[0][0] logger.info('正在统计测试计划执行成功的用例') query_select = 'SELECT testcaseId FROM testcase_reporter WHERE runHistoryId = %s AND runResult=\'Pass\'' query_value = (tp_run_history_id, ) result = db.select_many_record(query_select, query_value) if result[1] != True or not result[0]: logger.warn('没找到执行成功的用例') run_testcase_passed = '' else: run_testcase_passed = str(result[0][0]) logger.info('正在统计测试计划执行失败的用例') query_select = 'SELECT testcaseId FROM testcase_reporter WHERE runHistoryId = %s AND runResult=\'Fail\'' query_value = (tp_run_history_id, ) result = db.select_many_record(query_select, query_value) if result[1] != True or not result[0]: logger.info('没找到执行失败的用例') run_testcase_failed = '' else: run_testcase_failed = str(result[0][0]) logger.info('正在统计测试计划执行被阻塞的用例') query_select = 'SELECT testcaseId FROM testcase_reporter WHERE runHistoryId = %s AND runResult=\'Block\'' query_value = (tp_run_history_id, ) result = db.select_many_record(query_select, query_value) if result[1] != True or not result[0]: logger.info('没找到计划执行被阻塞的用例') run_testcase_blocked = '' else: run_testcase_blocked = str(result[0][0]) if '运行流水' == run_mode: # 更新测试计划运行记录历史结果表 logger.info('正在更新测试计划运行记录历史结果报表') query_update = 'UPDATE testplan_reporter SET browserType=\'%s\', runTc_total=%s, runPassedTc_total=%s, runFailedTc_total=%s,' \ 'runBlockedTc_total=%s, runPassedTc=\'%s\', runFailedTc=\'%s\', runBlockedTc=\'%s\', runTime=\'%s\' WHERE runHistoryId= %s' query_value = (browser_type, run_testcase_total, run_testcase_passed_total, run_testcase_failed_total, run_testcase_blocked_total, run_testcase_passed, run_testcase_failed, run_testcase_blocked, run_time, tp_run_history_id) result = db.execute_update(query_update, query_value) if result[1] != True: logger.error('更新测试计划运行到测试计划运行报表失败:%s' % result[0]) return elif '运行计划' == run_mode: #新增历史记录 query_insert = 'INSERT INTO testplan_reporter' +'(testPlanId, testPlanName, runHistoryId, browserType, runTc_total, runPassedTc_total, runFailedTc_total,' \ 'runBlockedTc_total, runPassedTc, runFailedTc, runBlockedTc, runTime)'\ 'VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' query_value = (test_plan_id, testplan_name, tp_run_history_id, browser_type, run_testcase_total, run_testcase_passed_total, run_testcase_failed_total, run_testcase_blocked_total, run_testcase_passed, run_testcase_failed, run_testcase_blocked, run_time) logger.info('正在记录测试计划运行结果到测试计划报表') result = db.execute_insert(query_insert, query_value) if result[1] != True: logger.error('记录测试计划运行结果失败')
def run_tc_step(self, browser_type, db, test_case_step, testcase_run_history_id, runmode): run_time = '%d-%02d-%02d %d:%d:%d' % time.localtime()[0:6] element_name = test_case_step[2] # 元素名称 command = test_case_step[5] # 操作 step_order = test_case_step[0] # 步序 inparams = test_case_step[6] # 输入参数 expected_result = test_case_step[7] # 预期结果 logger.info( '步骤信息(元素名称:%s, 步序:%s, 操作:%s, 输入参数:%s,预期结果:%s)' % (element_name, step_order, command, inparams, expected_result)) if (not element_name) and command: # 执行的步骤为函数操作 logger.info('正在执行函数操作') result = self.run_function_in_step(command, inparams, expected_result) if result[0] == 'Fail': # 运行出错,截图 step_screenshot_name = self.get_screenshot_as_file( step_order, testcase_run_history_id) #截图 step_screenshot_name = os.path.basename(step_screenshot_name) else: step_screenshot_name = '' else: logger.info('正在执行最普通的元素操作') # 获取元素选择器 selector1 = test_case_step[3] selector2 = test_case_step[4] logger.info('正在对选择器:selector1: %s,selector2: %s 做数据转换处理' % (selector1, selector2)) selector1 = other_tools.conver_data_from_phppage(selector1) selector2 = other_tools.conver_data_from_phppage(selector2) logger.info('转换后的选择器:selector1: %s,selector2: %s' % (selector1, selector2)) logger.info('开始对步骤中的元素操作进行操作') result = self.exectue_element_operator_in_step( selector1, selector2, command, inparams, expected_result) if result[0] == 'Fail': # 运行出错,截图 step_screenshot_name = self.get_screenshot_as_file( step_order, testcase_run_history_id) #截图 step_screenshot_name = os.path.basename(step_screenshot_name) else: step_screenshot_name = '' step_run_result = result[0] # 运行结果 step_run_result_desc = result[1] # 运行结果描述 if step_screenshot_name: step_screenshot_url = remote_screenshot_baselink.rstrip( '/') + '/' + step_screenshot_name else: step_screenshot_url = '' if '运行流水' == runmode: logger.info('正在更新测试步骤结果报表中的测试步骤运行结果') query_update = 'UPDATE case_step_run_detail SET elementName=\'%s\', command=\'%s\', inparams=\'%s\', expectedResult=\'%s\', stepRunResult=\'%s\', stepRunResultDesc=\'%s\', '\ 'browserType=\'%s\', runTime=\'%s\', screenshotUrl=\'%s\' WHERE runHistoryId=\'%s\' AND testcaseId=\'%s\' AND stepOrder=\'%s\' AND browserType=\'%s\'' query_data = (element_name, command, inparams, expected_result, step_run_result, step_run_result_desc, browser_type, run_time, step_screenshot_url, testcase_run_history_id, self.test_case_id, step_order, browser_type) result = db.execute_update(query_update, query_data) if result[1] != True: logger.error('更新用例运行结果到测试用例运行报表失败:%s' % result[0]) return else: logger.info('正在记录测试步骤运行结果到测试步骤结果报表') query_insert = 'INSERT INTO case_step_run_detail' + '(testplanId, testcaseId, runHistoryId, stepOrder, ' \ 'elementName, command, inparams, expectedResult, stepRunResult, stepRunResultDesc, browserType, runTime, screenshotUrl) ' \ 'VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' query_value = (self.test_plan_id, self.test_case_id, testcase_run_history_id, step_order, element_name, command, inparams, expected_result, step_run_result, step_run_result_desc, browser_type, run_time, step_screenshot_url) result = db.execute_insert(query_insert, query_value) if result[1] != True: logger.error('记录到测试步骤结果报表失败 %s' % result[0]) logger.info('返回步骤运行结果给测试用例') return step_run_result
run_history_info_list.append([tc_run_history_id,browser_type,time_took]) elif run_mode == '运行计划': # 运行测试计划 logger.info('####################### 开始测试 #######################') test_plan = TestPlan() for browser_type in browser_type_to_test: start_time = datetime.datetime.now() #记录测试开始时间 selenium_util.set_driver(browser_type) driver = selenium_util.get_driver() driver.get(web_site.get_web_url()) driver.implicitly_wait(30) #页面等待 driver.maximize_window() #浏览器最大化 tp_run_history_id = int(time.time()) # 作为运行测试计划流水编号 run_result = test_plan.run_test_plan(browser_type, db, run_mode, tp_run_history_id, test_plan_id) if 'Block' == run_result: logger.error('未找到测试计划[id=%s]的用例,提前终止用例' % test_plan_id) logger.info('计划运行完成,正在关闭浏览器') driver.close() driver.quit() break logger.info('计划运行完成,正在关闭浏览器') driver.close() driver.quit() logger.info('####################### 测试结束 #######################') end_time = datetime.datetime.now() # 记录测试结束时间 time_took = end_time - start_time run_history_info_list.append([tp_run_history_id, browser_type, time_took]) elif run_mode == '运行流水': # 运行测试计划中,所有失败的用例 tp_run_history_id = int(input('请输入测试计划对应的流水号ID:')) logger.info('####################### 开始测试 #######################')