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

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 及很多细节部分,没做详细处理和封装,这里笔者仅仅是提供思路,感兴趣的同学,可自行去尝试进行进一步扩展,如想要源代码的同学可以文末留言或者加我好友领取哦。

徐福第二次东渡后不知去向,日本人奉其为祖先,有依据吗?关于日本的祖先,当代流传着很多说法,包括东渡的徐福。在日本的传说中,徐福是日本人的祖先,他甚至还有可能是日本的第一代天皇。那么,这种说法究竟有何科学依据呢?徐福是否真的是日本人的祖日本汇率暴跌!跨境卖家该如何降低运费成本?今年9月,日本政府自1998年以来首次干预汇率,当时日元汇率跌至145。90。图源图虫日元汇率的不断贬值,不但让日本连续14个月都出现巨额贸易逆差,也让日本工业生产所需的国际原材料1985年中日围棋擂台大战,日本高手连赢六局,中方主帅力挽狂澜千百年来,琴棋书画就被列为中国文人雅士的基本技能。蕴含着中华文化丰富内涵的围棋一直被人们所推崇,黑白对弈被引为风雅之事。明清两代,围棋文化达到了一个巅峰,各种流派纷起,并涌现了一批福泽谕吉对近代日本影响最大的人,死前为日本设计侵华战争阅读之前,麻烦您点一个关注,既方便您进行讨论与分享,又能给您带来不一样的参与感,感谢您的支持!一个好的思想家能带领国家走向巅峰,也能将国家带向深渊。可以肯定地说,福泽谕吉对于日本而日军最怕部队,八路军骑兵团,日本人闻之四散奔逃在热兵器时代,骑兵团的战斗力往往抵不过装甲部队,可就在抗战期间,拥有强大机动能力的日军部队,最害怕的竟然就是八路军的骑兵团,害怕到,闻之都会四散奔逃的程度,那么这支骑兵团究竟做了什手机厂商集体大降价今年双十一可以说是手机降价力度最大的一次了苹果最新一代iPhone发布不到一个月就降到5000元左右。5月发售的iQOONeo6SE,12GB256GB已降价300元,到2199元小米推出1830枚零件的捍卫者发射器,能击发能抛壳,还支持AR玩法说起千变万化的积木玩具,可能很多人都会想到乐高,不过值得一提的是,乐高价格往往很贵,一个几百颗零件的乐高通常就要上千,这对于很多普通人来说,可能都舍不得买吧。所以我个人觉得,如果你詹姆斯数据实时更新2022年11月日在今日凌晨结束洛杉矶NBA赛场上湖人以116130输骑士队,二连败,常规赛战记2胜7败詹姆斯全场得到27分7板4助,三分14。失误1,1323命中率此战过后,詹姆(体育国际足球)西甲综合塞维利亚德比三红牌一乌龙贝蒂斯战平塞维利亚新华社马德里11月6日电(谢宇智)西班牙足球甲级联赛第13轮6日再赛数场。在一场异常火爆的塞维利亚德比中,皇家贝蒂斯主场11艰难战平塞维利亚。来自塞维利亚城的两支球队皇家贝蒂斯与塞空间智能化碰撞鸿蒙智联生态,华为携手伙伴创造无限可能2022年11月5日,以一起创造无限可能新空间再出发为主题的HarmonyOSConnect伙伴峰会在东莞举办,峰会深度解读了HarmonyOSConnect全面升级给伙伴带来的新谁说互联网出海不能从ToB赛道入手?在谈到中国互联网公司的出海时,人们首先想到的肯定是那些平台型应用,例如TikTokShein,以及刚刚搅乱海外电商一池春水的Temu(拼多多出品)也很容易想到游戏产品,包括PUBG
内地网红参选港姐,被嘲粤语差劲泳装暴露五五分的身材近日,香港小姐2022即将举行总决赛,而作为入围19强的人气选手陈铭凤,也引起了不少人的关注。现年24岁的陈铭凤是一名四川网红,她在网上拥有近200万的粉丝,网名叫做香港姑娘Che这对恋情实锤!别墅过夜女方撒娇,她搞定了自己老板?hzt和xyy又被拍到亲密同行了!两人当天都打扮得很是休闲,一起吃饭结束后,两个人一起等助理前来。韬子把自己皮肤晒成了小麦色,搭配黄色的头发,颇为潮流。一起等待的时候,双方之间的暧每天晚上睡觉后,都被室友捅醒?沙雕网友可别把我笑死了哈哈哈哈今天的各位宝子们有没有一如既往保持开心呢今日份快乐已送达快来查收咯01hr离谱的室友画面感太足了我手里仿佛已经拿起了杆子02hr警惕人工智障人工智障Q33Q还有一个真正需要人工帮助60岁以后,这4种疾病容易找上门,需定期做好体检上了年纪的人疾病患病率高,如果可以定期进行检查了解健康情况,有问题尽早发现,及时处理,自然有效延长寿命,提高晚年生活质量。有的人觉得检查只是浪费钱,只要没有疾病信号就是健康的标志,80岁以上的高龄老人如何平衡膳食?高龄和衰弱的老年人,多种慢性病的患病率高,身体各系统功能显著衰退,生活能力和心理调节能力明显下降,营养不良发生率高。专业精细化个体化的膳食营养管理有助于改善老年人的营养状况维护身体年薪1953万!24岁前榜眼因手术缺阵,火箭少了一个潜在交易目标无论是ESPN还是CBS的专家团,都不把休斯顿未来传奇巨星小凯文波特放在眼里啊。两份NBATOP100榜单里都没有小波特的名字,而他在2K23里的能力值也只有77。曾经单场轰下50泰山VS深足14阿哥领衔,4位鲁能功勋反戈旧主!郝伟赢球不容易北京时间9月22日,距离山东泰山VS深足的比赛还有2天的时候,已经2轮不胜的山东泰山队从目前的状态来看,想在客场赢下深足难度不小,这场比赛因为深足有多位前鲁能功勋球员更让比赛具有了范加尔我告诉球员希望他们更多施压,对球队表现和防守很满意直播吧9月23日讯在欧国联A级第5轮的一场较量中,荷兰20击败波兰。赛后在接受采访时,荷兰主帅范加尔表示,球队以非常好的结果通过了考验。范加尔这样谈道这对我们来说是一个巨大的考验,巴舒亚伊进球的感觉非常棒球队需要在场上保持一致性直播吧9月23日讯在刚刚结束的欧国联A级的一场比赛中,比利时21击败了威尔士。比赛中,比利时前锋巴舒亚伊打入一球。赛后,巴舒亚伊在采访中表示,取得进球的感觉非常棒,但球队仍有些工作NBA太阳队官方同意老板萨沃尔出售球队的决定,向所有那些受伤的人道歉当地时间9月21日,菲尼克斯太阳队和水星队老板萨沃尔发布声明正在为这两支球队寻找买家。太阳官方随后发布声明我们同意罗伯特萨沃尔出售太阳队和水星队的决定,这符合球队和社区的最佳利益。跨界联名浪潮,Z世代下国货体育的开挂时刻y1s1,特步这是要开挂了???这个怪兽大学联名款,深深击中了我的审美!!入了!随着特步361红双喜李宁等频繁出现在时尚媒体的报道里,体育老国货逐渐成为新一代年轻人彰显自我的文化符