媒介

本身从未没想过能利用python来做主动化测试框架的设想、开发。

可能有人会猎奇说,六哥,你怎么也用python写测试框架了?

指导说:

python你也没有现实工做经历,可能就是本身自学的。

听完,「那一刻,我实的出格证明本身,我也行!」

框架搭建

整个框架的实现,大约也就1.5天,关于框架的开发并非很难,次要难在测试陈述增加失败主动截图功用和echart的饼子图统计功用,两者的整合花了近半天的时间吧。

Selenium4+Python3系列(十二) - 测试框架的设计与开发  第1张

image.png

效果:

Selenium4+Python3系列(十二) - 测试框架的设计与开发  第2张

image.png

1、核心思惟

延续利用Page Object和Page Factory思惟,使页面、数据、元素、脚本停止别离,此处演示仅仅为了讲解框架搭建思绪,并不是为我在公司写的那套框架,次要利用selenium4+python3+pytest,那里只贴核心代码,仅供进修交换利用。

「目次构造」

Selenium4+Python3系列(十二) - 测试框架的设计与开发  第3张

image.png

2、日记封拆

次要用于便利定位用例脚本施行步调,示例代码如下:

# -*- coding: utf-8 -*-"""@Time : 2022/12/7 19:36@Auth : 软件测试君@File :LogUtils.py@IDE :PyCharm@Motto:ABC(Always Be Coding)"""import timeimport osimport loggingcurrrent_path = os.path.dirname(__file__)log_path = os.path.join(currrent_path, '../logs')class LogUtils: def __init__(self, log_path=log_path): """ 通过python自带的logging模块停止封拆 """ self.logfile_path = log_path # 创建日记对象logger self.logger = logging.getLogger(__name__) # 设置日记级别 self.logger.setLevel(level=logging.INFO) # 设置日记的格局 formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') """在log文件中输出日记""" # 日记文件名称显示一天的日记 self.log_name_path = os.path.join(self.logfile_path, "log_%s" % time.strftime('%Y_%m_%d')+".log") # 创建文件处置法式并实现逃加 self.file_log = logging.FileHandler(self.log_name_path, 'a', encoding='utf-8') # 设置日记文件里的格局 self.file_log.setFormatter(formatter) # 设置日记文件里的级别 self.file_log.setLevel(logging.INFO) # 把日记信息输出到文件中 self.logger.addHandler(self.file_log) # 封闭文件 self.file_log.close() """在控造台输出日记""" # 日记在控造台 self.console = logging.StreamHandler() # 设置日记级别 self.console.setLevel(logging.INFO) # 设置日记格局 self.console.setFormatter(formatter) # 把日记信息输出到控造台 self.logger.addHandler(self.console) # 封闭控造台日记 self.console.close() def get_log(self): return self.loggerlogger = LogUtils().get_log()if __name__ == '__main__': logger.info('123') logger.error('error')3、根底页面

用于存放,控件及API的常用操做,示例代码如下:

# -*- coding: utf-8 -*-"""@Time : 2022/12/7 19:58@Auth : 软件测试君@File :BasePage.py@IDE :PyCharm@Motto:ABC(Always Be Coding)"""import timefrom selenium.common import TimeoutExceptionfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.wait import WebDriverWait as WDfrom util.LogUtils import LogUtilsfrom util.ParseConFile import ParseConFilelogger = LogUtils().get_log()class BasePage(object): """控件及API的常用操做""" cf = ParseConFile() def __init__(self, driver, timeout=30): self.byDic = { 'id': By.ID, 'name': By.NAME, 'class_name': By.CLASS_NAME, 'xpath': By.XPATH, 'link_text': By.LINK_TEXT, 'css': By.CSS_SELECTOR } self.driver = driver self.outTime = timeout def find_element(self, by, locator): """ 通过id, name, xpath, css,class....,查找元素 """ try: logger.info("通过 " + by + " 定位") element = WD(self.driver, self.outTime).until(lambda x: x.find_element(self.byDic.get(by), locator)) except TimeoutException as e: logger.error('请确认元素定位体例,' + e) else: return element def find_elements(self, by, locator): """ 通过id, name, xpath, css,class....,查找一组元素 """ try: logger.info("通过 " + by + " 定位") elements = WD(self.driver, self.outTime).until(lambda x: x.find_elements(self.byDic.get(by), locator)) except TimeoutException as e: logger.error('请确认元素定位体例,' + e) else: return elements def get_text(self, by, locator): """ 获取元素文本/属性信息 """ logger.info("获取元素文本胜利!") return self.find_element(by, locator).text def oPEn_url(self, url): """翻开阅读器""" logger.info("翻开项目首页:" + url) self.driver.get(url) def quit_browser(self): self.driver.quit() def send_keys(self, by, locator, keys=''): """输入操做""" logger.info("输入:" + keys) self.find_element(by, locator).clear self.sleep(1) self.find_element(by, locator).send_keys(keys) def click(self, by, locator): """点击操做""" logger.info("点击按钮:" + locator) self.find_element(by, locator).click() @staticmethod def sleep(num=0): """强迫期待""" logger.info("法式期待:" + str(num) + " 秒") time.sleep(num)4、登岸页面

次要用于存放控件及元素操做,示例代码如下:

# -*- coding: utf-8 -*-"""@Time : 2022/12/7 20:27@Auth : 软件测试君@File :LoginPage.py@IDE :PyCharm@Motto:ABC(Always Be Coding)"""from Page.BasePage import BasePagefrom util.LogUtils import LogUtilsfrom util.ParseConFile import ParseConFilelogger = LogUtils().get_log()class LoginPage(BasePage): """ 存放控件及元素操做 """ # 设置装备摆设文件读取元素 do_conf = ParseConFile() # 用户名输入框 username = do_conf.get_locator('LoginPage_Elements', 'username') # 密码输入框 password = do_conf.get_locator('LoginPage_Elements', 'password') # 登录按钮 loginBtn = do_conf.get_locator('LoginPage_Elements', 'loginBtn') # 登录失败的提醒信息 error_msg = do_conf.get_locator('LoginPage_Elements', 'errorMsg') def login(self, username, password): """登录流程""" self.open() self.send_username(username) self.send_password(password) self.click_login_btn() msg = self.get_errorMsg() return msg def open(self): self.open_url('http://localhost:8080/login') def quit(self): self.quit_browser() def send_username(self, username): self.send_keys(*LoginPage.username, username) def send_password(self, password): self.send_keys(*LoginPage.password, password) def click_login_btn(self): self.click(*LoginPage.loginBtn) def get_errorMsg(self): return self.get_text(*LoginPage.error_msg)if __name__ == "__main__": pass5、营业操做

次要用于记录用例步调,示例代码如下:

# -*- coding: utf-8 -*-"""@Time : 2022/12/7 20:27@Auth : 软件测试君@File :LoginPage.py@IDE :PyCharm@Motto:ABC(Always Be Coding)"""from Page.BasePage import BasePagefrom util.LogUtils import LogUtilsfrom util.ParseConFile import ParseConFilelogger = LogUtils().get_log()class LoginPage(BasePage): """ 存放控件及元素操做 """ # 设置装备摆设文件读取元素 do_conf = ParseConFile() # 用户名输入框 username = do_conf.get_locator('LoginPage_Elements', 'username') # 密码输入框 password = do_conf.get_locator('LoginPage_Elements', 'password') # 登录按钮 loginBtn = do_conf.get_locator('LoginPage_Elements', 'loginBtn') # 登录失败的提醒信息 error_msg = do_conf.get_locator('LoginPage_Elements', 'errorMsg') def login(self, username, password): """登录流程""" self.open() self.send_username(username) self.send_password(password) self.click_login_btn() msg = self.get_errorMsg() return msg def open(self): self.open_url('http://localhost:8080/login') def quit(self): self.quit_browser() def send_username(self, username): self.send_keys(*LoginPage.username, username) def send_password(self, password): self.send_keys(*LoginPage.password, password) def click_login_btn(self): self.click(*LoginPage.loginBtn) def get_errorMsg(self): return self.get_text(*LoginPage.error_msg)if __name__ == "__main__": pass6、测试陈述之失败带截图

那块确实很坑,看了良多网上的教程,笔者鄙人,整了一下战书才弄出失败带截图,次要是对conftest.py的设想编写,示例代码如下:

# -*- coding: utf-8 -*-"""@Time : 2022/12/10 18:13@Auth : 软件测试君@File :conftest.py@IDE :PyCharm@Motto:ABC(Always Be Coding)"""import pytestfrom selenium import webdriverfrom webdriver_manager.chrome import ChromeDriverManagerdriver = None@pytest.hookimpl(hookwrapper=True, tryfirst=True)def pytest_runtest_makereport(item): pytest_html = item.config.pluginmanager.getplugin('html') outcome = yield report = outcome.get_result() extra = getattr(report, 'extra', []) if report.when == 'call' or report.when == "setup": xfail = hasattr(report, 'wasxfail') if (report.skipped and xfail) or (report.failed and not xfail): file_name = report.nodeid.replace("::", "_") + ".png" screen_img = _capture_screenshot() if file_name: html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \ 'οnclick="window.open(this.src)" align="right"/></div>' % screen_img extra.append(pytest_html.extras.html(html)) report.extra = extra@pytest.fixture(scope='session')def browser(): global driver if driver is None: driver = webdriver.Chrome(ChromeDriverManager().install()) driver.maximize_window() yield driver driver.quit() return driverdef _capture_screenshot(): """截图""" return driver.get_screenshot_as_base64()7、施行脚本

次要用于挪用测试用例脚本,示例代码如下:

# -*- coding: utf-8 -*-"""@Time : 2022/12/10 18:04@Auth : 软件测试君@File :RunTestCase.py@IDE :PyCharm@Motto:ABC(Always Be Coding)"""import sysimport pytestfrom config.conf import ROOT_DIR, HTML_NAMEdef main(): if ROOT_DIR not in sys.path: sys.path.append(ROOT_DIR) # 施行用例 args = ['--html=' + './report/' + HTML_NAME] pytest.main(args)if __name__ == '__main__': main()8、测试效果

「用例施行效果:」

Selenium4+Python3系列(十二) - 测试框架的设计与开发  第4张

image.png

「测试陈述:」

Selenium4+Python3系列(十二) - 测试框架的设计与开发  第5张

image.png

总结

其实写框架其实不难,掌握核心思绪,实现起来就会变得容易良多,与语言无关哦(因为我是Java党)。

关于API及良多细节部门,没做详细处置和封拆,那里笔者仅仅是供给思绪,感兴趣的同窗,可自行去测验考试停止进一步扩展,如想要源代码的同窗能够文末留言或者加我老友领取哦。