范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Selenium4Python3系列(十二)测试框架的设计与开发

  前言
  自己从未没想过能使用 python 来做自动化测试框架的设计、开发。
  可能有人会好奇说,六哥,你怎么也用 python 写测试框架了?
  领导说:  ❝
  python你也没有实际工作经验,可能就是自己自学的。
  ❞
  听完, 「那一刻,我真的特别证明自己,我也行!」  框架搭建
  整个框架的实现,大约也就1.5天,关于框架的开发并不是很难,主要难在 测试报告增加失败自动截图功能 和echart的饼子图 统计功能,两者的整合花了近半天的时间吧。
  image.png
  效果:
  image.png  1、核心思想
  延续使用 Page Object 和Page Factory 思想,使页面、数据、元素、脚本进行分离,此处演示仅仅为了讲解框架搭建思路,并非为我在公司写的那套框架,主要使用selenium4+python3+pytest ,这里只贴核心代码,仅供学习交流使用。
  「目录结构」
  image.png  2、日志封装
  主要用于方便定位用例脚本执行步骤,示例代码如下:  # -*- coding: utf-8 -*- """ @Time : 2022/12/7 19:36 @Auth : 软件测试君 @File :LogUtils.py @IDE :PyCharm @Motto:ABC(Always Be Coding) """ import time import os import logging  currrent_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.logger   logger = 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 time  from selenium.common import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait as WD  from util.LogUtils import LogUtils from util.ParseConFile import ParseConFile  logger = 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 BasePage from util.LogUtils import LogUtils from util.ParseConFile import ParseConFile  logger = 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__":     pass 5、业务操作
  主要用于记录用例步骤,示例代码如下:  # -*- coding: utf-8 -*- """ @Time : 2022/12/7 20:27 @Auth : 软件测试君 @File :LoginPage.py @IDE :PyCharm @Motto:ABC(Always Be Coding) """ from Page.BasePage import BasePage from util.LogUtils import LogUtils from util.ParseConFile import ParseConFile  logger = 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__":     pass 6、测试报告之失败带截图
  这块确实很坑,看了很多网上的教程,笔者不才,整了一下午才弄出失败带截图,主要是对 conftest.py 的设计编写,示例代码如下: # -*- coding: utf-8 -*- """ @Time : 2022/12/10 18:13 @Auth : 软件测试君 @File :conftest.py @IDE :PyCharm @Motto:ABC(Always Be Coding) """ import pytest from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager  driver = 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 = ""screenshot"" % 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 driver   def _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 sys  import pytest  from config.conf import ROOT_DIR, HTML_NAME   def 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、测试效果
  「用例执行效果:」
  image.png
  「测试报告:」
  image.png  总结
  其实写框架并不难,掌握核心思路,实现起来就会变得容易很多,与语言无关哦( 因为我是Java党 )。
  关于 API 及很多细节部分,没做详细处理和封装,这里笔者仅仅是提供思路,感兴趣的同学,可自行去尝试进行进一步扩展,如想要源代码的同学可以文末留言或者加我好友领取哦。

正月十五元宵节,家宴菜单已备好,6荤4素照着做,简单省事颜值高大家好,欢迎大家来到我的美食自媒体,我是美食领域创作者锦绣V山东专注美食,让生活更有味。今天为大家带来了几道家常美食的做法,这几道美食也是深受大家的喜欢,而且是很常见的几道美食。天湿气最怕的粗粮,很多人不知道,简单一做,祛湿健脾,快收藏导语生活中唯有美食与爱不可辜负,忙碌的一天结束后,回到家餐桌上有一顿热气腾腾的家常菜,瞬间变的暖洋洋的。美食带给人的是喜悦,与时间无关。花4个小时为家人做一顿大餐和用4分钟等一碗美一家四口早餐,2天不重样,有干有稀一小桌,花钱不多全家满意冷空气来袭,温度一下就降了下来,减少了很多不必要的外出。天冷,家里的饮食不能将就,尤其是早餐,多补充热量,增加营养,暖暖地过冬!宝爸和儿子每天有晨跑的习惯,我这个家庭主妇也没闲着,过年买带鱼,不管黄眼睛还是黑眼睛,牢记4不买冬日生活打卡季说到春节办年货,我想家家户户少不了的就是带鱼了,虽然它不是什么稀罕物,但是只要过年,大家都会买上一些,似乎只有过年吃带鱼,才能感受到那种浓烈的年味。带鱼想要好吃,除了冬天的不老菜,天然叶酸高!中老年人宁可不吃肉也要多吃点冬日生活打卡季没了烟火气,人生就是一段孤独的旅程。冬天的不老菜,天然叶酸高!中老年人宁可不吃肉也要多吃点,快快收藏吧!第一包菜推荐食谱腊肠手撕包菜家常小炒腊肠手撕包菜,好吃下饭,看这就是韩国韩国的紫禁城(景福宫)就像来到北京必定要参观故宫紫禁城那样,来到首尔也总要前往参观那里的紫禁城景福宫。景福宫景福宫坐落在首尔的中心。步入景福宫的感觉,就跟步入紫禁城差不多,一样的中国传统的挑檐斗拱式建筑春节假期游之江苏常州篇10个景区景点三吴重镇,八邑名都,季子故里,运河水岸。常州,长江南岸重要城市,毗邻太湖。这里是南朝时期齐梁故里,这里有春秋淹城遗址。这里是苏南经济中心,与苏州,无锡组成苏锡常都市圈。1,中华恐龙冬游鹊山拜谒烈士纪念地2023年1月新冠阳康后,与妻二人来到位于济南北部的鹊山,它在黄河北岸,与泺口码头斜相对。相传昔日每年七八月间,乌鹊飞翔,布满山颠。又传先秦名医扁鹊曾在这里炼丹,死后葬此,故名鹊山山西古建筑资源全国最丰富,为啥大部分不能变成旅游资源?山西有全国最丰富的古建筑资源,而且精品多,应县木塔平遥古城乔家大院五台山建筑群这些响当当的古建筑或古建筑群落都在山西,而且建国以来山西因煤炭资源丰富,被定位为能源基地,大部分工业投仙本那巴瑶族没有国籍的海上吉普赛人(原创)南海老林为了考察砗磲贝人工繁育项目,本人曾与海南省海洋环保协会秘书长陈惠女士前往东马沙劳越仙本那进行考察。仙本那位于马来西亚印尼菲律宾三国交界处滨海。我们从吉隆坡乘机到东马户外游玩!被领队安排异性混居,3位美女说出混居经历在户外旅游的时候,多数女性游客们都遇到过一些较为尴尬的事情。正是由于这种尴尬事情的存在,这也使得多数女性游客们在户外旅游的时候,都会极其地警惕。(此处已添加小程序,请到今日头条客户
他们育出有中国芯的苹果来源科技日报原标题他们育出有中国芯的苹果本报记者王延斌通讯员郭翠华名片上,陈学森是作物生物学国家重点实验室副主任国家苹果工程技术研究中心主任山东农业大学教授。在获得国家科技进步奖二随手花30元买彩票中了100万!接下来的事太意外过年前能中个大奖那太好了,来浙江杭州旅游的崔先生就这么幸运!然而接下来发生的事情简直一波三折。崔先生花30元买的彩票中了百万大奖,正当他为此高兴时,却没想到因为车门未关,差点错失这人口负增长2022年末我国人口锐减85万为啥小区保安都是大爷?为啥年轻人越来越少?1月17日,国家统计局发布2022年国民经济运行数据。2022年,中国人口出现近61年来的首次人口负增长。2022年年末全国人口(包括31把准改革风向标激活发展内生力近日,由健康报社主办诺华制药公益支持的高质量发展下的国考全局部署交流会在线上召开。在以评促建行稳致远推动公立医院高质量发展学科群建设分论坛上,相关政策制定者业内专家三级公立医院管理最后没阳的原来是这几种人!转眼间,全国各地变阳的高峰期已经基本过了,大家发现没有,总有一些人自始至终都没阳过,从头到尾感觉新冠这个病毒和他没有啥关系,真的是很神奇的存在。但依然没有阳的,主要是这几种人!1。国乒排名更新!男队仅4人进前十,巴西神童一路飙升,王皓有压力1月17日,国际乒联公布了最新一期世界排名。上周乒坛主要经历了多哈世预赛和德班常规挑战赛,其中前者不设置积分,但事关世乒赛名额。由于国乒大部队并未前往非洲南部,欧美参赛选手积分明显梁子岛复牌,王者能否归来?2023年伊始鄂州旅游业发展迎来开门红梁子岛生态旅游区和杏福园旅游区新晋国家4A级旅游景区说起梁子岛许多人并不陌生从上世纪90年代初设立风景区起旅游业在这里一路蓬勃兴旺梁子岛之名如渔阳郡遗址,在怀柔和密云交界处,是怀柔区不可移动文物在101国道怀柔和密云交界处不远的地方,即怀柔区北房镇梨园庄村东0。5公里处,有一座古城遗址公园,从101道右转前行500米左右,就可以看到路边立着的一块牌子,上面写着渔阳城遗址,警惕!德尔塔克戎变异毒株XAY已向全球延蔓,毒性有所增强一波未平一波又起,病毒反复变异,而且毒性在原来的基础上有所增强。近日,世界卫生组织部又检测出新的变异毒株XAY。2,最引人注目的是,它是德尔塔毒株和奥密克戎毒株的合体,由AY。45公园中的科大讯飞全球总部人工智能是一门广泛交叉的前沿科学,对于一家有全球影响力的中国人工智能科技企业来说,总部园区不仅代表着企业外在的形象,更重要的是展现科技外表下的东方智慧与人文内里。我们尝试通过超级细白话派币白皮书头条创作挑战赛我在头条搞创作第二期头条群星10月榜经不住朋友的反复安利,决定再好好看看这个派币,网上找到中文版的白皮书,一瞧,确实出乎意料有点料。怪不得受众这么广。派的目标前言里写