1. 简介
前面文章, 我们实现了框架的一部分功能, 包括日志类和浏览器引擎类的封装, 今天我们继续封装一个基类和介绍如何实现 POM. 关于基类, 是这样定义的: 把一些常见的页面操作的 selenium 封装到 base_page.py 这个类文件, 以后每个 POM 中的页面类, 都继承这个基类, 这样每个页面类都有基类的方法, 这个我们会在这篇文章由宏哥实现.
2. 项目层级结构
1. 上一篇中我们已经创建好了项目层级结构, 具体项目层级结构如下图. 这里不再赘述, 相关文件也如下:
3. 定位和截图类封装
1. 在实现封装基类里, 我们实现了元素八大方式的定位和截图类封装.
2. 基类 base_page.py 的具体实现代码, 这里就封装了几个常用方法, 其他方法, 你自己去练习封装下.
3.1 代码实现:
3.2 参考代码:
- # -*- coding:utf-8 -*-
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-20
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《《一头扎进》系列之 Python+Selenium 框架设计篇 4- 价值好几 K 的框架, 不看别后悔, 过时不候
- '''
- # 3. 导入模块
- import time
- from selenium.common.exceptions import NoSuchElementException
- import os.path
- from automation_framework_demo.framework.logger import Logger
- # create a logger instance
- logger = Logger(logger="BasePage").getlog()
- class BasePage(object):
- """
- 定义一个页面基类, 让所有页面都继承这个类, 封装一些常用的页面操作方法到这个类
- """
- def __init__(self, driver):
- self.driver = driver
- # quit browser and end testing
- def quit_browser(self):
- self.driver.quit()
- # 浏览器前进操作
- def forward(self):
- self.driver.forward()
- logger.info("Click forward on current page.")
- # 浏览器后退操作
- def back(self):
- self.driver.back()
- logger.info("Click back on current page.")
- # 隐式等待
- def wait(self, seconds):
- self.driver.implicitly_wait(seconds)
- logger.info("wait for %d seconds." % seconds)
- # 点击关闭当前窗口
- def close(self):
- try:
- self.driver.close()
- logger.info("Closing and quit the browser.")
- except NameError as e:
- logger.error("Failed to quit the browser with %s" % e)
- # 保存图片
- def get_windows_img(self):
- """
- 在这里我们把 file_path 这个参数写死, 直接保存到我们项目根目录的一个文件夹.\Screenshots 下
- """ file_path = os.path.dirname(os.path.abspath('.')) +'/screenshots/' rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
- screen_name = file_path + rq + '.png'
- try:
- self.driver.get_screenshot_as_file(screen_name)
- logger.info("Had take screenshot and save to folder : /screenshots")
- except NameError as e:
- logger.error("Failed to take screenshot! %s" % e)
- self.get_windows_img()
- # 定位元素方法
- def find_element(self, selector):
- """
- 这个地方为什么是根据 => 来切割字符串, 请看页面里定位元素的方法
- submit_btn = "id=>su"
- login_lnk = "xpath => //*[@id='u1']/a[7]" # 百度首页登录链接定位
- 如果采用等号, 结果很多 xpath 表达式中包含一个 =, 这样会造成切割不准确, 影响元素定位
- :param selector:
- :return: element
- """ element =''
- if '=>' not in selector:
- return self.driver.find_element_by_id(selector)
- selector_by = selector.split('=>')[0]
- selector_value = selector.split('=>')[1]
- print(selector_value)
- if selector_by == "i" or selector_by == 'id':
- try:
- element = self.driver.find_element_by_id(selector_value)
- logger.info("Had find the element \' %s \'successful"
- "by %s via value: %s" % (element.text, selector_by, selector_value))
- except NoSuchElementException as e:
- logger.error("NoSuchElementException: %s" % e)
- self.get_windows_img() # take screenshot
- elif selector_by == "n" or selector_by == 'name':
- element = self.driver.find_element_by_name(selector_value)
- elif selector_by == "c" or selector_by == 'class_name':
- element = self.driver.find_element_by_class_name(selector_value)
- elif selector_by == "l" or selector_by == 'link_text':
- element = self.driver.find_element_by_link_text(selector_value)
- elif selector_by == "p" or selector_by == 'partial_link_text':
- element = self.driver.find_element_by_partial_link_text(selector_value)
- elif selector_by == "t" or selector_by == 'tag_name':
- element = self.driver.find_element_by_tag_name(selector_value)
- elif selector_by == "x" or selector_by == 'xpath':
- try:
- element = self.driver.find_element_by_xpath(selector_value)
- logger.info("Had find the element \' %s \'successful"
- "by %s via value: %s" % (element.text, selector_by, selector_value))
- except NoSuchElementException as e:
- logger.error("NoSuchElementException: %s" % e)
- self.get_windows_img()
- elif selector_by == "s" or selector_by == 'selector_selector':
- element = self.driver.find_element_by_CSS_selector(selector_value)
- else:
- raise NameError("Please enter a valid type of targeting elements.")
- print(element)
- return element
- # 输入
- def type(self, selector, text):
- el = self.find_element(selector)
- el.clear()
- try:
- el.send_keys(text)
- logger.info("Had type \' %s \'in inputBox" % text)
- except NameError as e:
- logger.error("Failed to type in input box with %s" % e)
- self.get_windows_img()
- # 清除文本框
- def clear(self, selector):
- el = self.find_element(selector)
- try:
- el.clear()
- logger.info("Clear text in input box before typing.")
- except NameError as e:
- logger.error("Failed to clear in input box with %s" % e)
- self.get_windows_img()
- # 点击元素
- def click(self, selector):
- el = self.find_element(selector)
- try:
- el.click()
- logger.info("The element \' %s \'was clicked." % el)
- except NameError as e:
- logger.error("Failed to click the element with %s" % e)
- # 或者网页标题
- def get_page_title(self):
- logger.info("Current page title is %s" % self.driver.title)
- return self.driver.title
- @staticmethod
- def sleep(seconds):
- time.sleep(seconds)
- logger.info("Sleep for %d seconds" % seconds)
4. pageObjects 文件夹下相关代码
1. 页面对象中, 百度主页的元素定位和简单的操作函数, 页面类主要是元素定位和页面操作写成函数, 供测试类调用.
baidu_homepage.py
4.1 代码实现:
4.2 参考代码:
- # -*- coding:utf-8 -*-
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-20
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《《一头扎进》系列之 Python+Selenium 框架设计篇 4- 价值好几 K 的框架, 不看别后悔, 过时不候
- '''
- # 3. 导入模块
- from automation_framework_demo.framework.base_page import BasePage
- class HomePage(BasePage):
- input_box = "id=>kw"
- search_submit_btn = "xpath=>//*[@id='su']"
- #百度新闻入口
- #news_link = "xpath=>//*[@id='u1']/a[@name='tj_trnews']"
- news_link = "xpath=>//*[@id='u1']/a[@name='tj_trnews']"
- def type_search(self, text):
- self.type(self.input_box, text)
- def send_submit_btn(self):
- self.click(self.search_submit_btn)
- def click_news(self,):
- self.click(self.news_link)
- self.sleep(2)
这里注意下元素定位写法,=> 和 base_page.py 中 find_element() 方法元素定位切割有关系, 网上有些人写根据逗号切割或者等号切割, 在实际使用 xpath 定位, 发现单独逗号或者单独等号切割都不精确, 造成元素定位失败.
5. 测试类的写法举例
1. 新建一个测试类 baidu_search1.py.
baidu_search1.py
5.1 代码实现:
5.2 参考代码:
- # -*- coding:utf-8 -*-
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-20
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《《一头扎进》系列之 Python+Selenium 框架设计篇 4- 价值好几 K 的框架, 不看别后悔, 过时不候
- '''
- # 3. 导入模块
- import time
- import unittest
- from automation_framework_demo.framework.browser_engine import BrowserEngine
- from automation_framework_demo.pageobjects.baidu_homepage import HomePage
- class BaiduSearch(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- """
- 测试固件的 setUp() 的代码, 主要是测试的前提准备工作
- :return:
- """
- browse = BrowserEngine(cls)
- cls.driver = browse.open_browser(cls)
- @classmethod
- def tearDownClass(cls):
- """
- 测试结束后的操作, 这里基本上都是关闭浏览器
- :return:
- """
- cls.driver.quit()
- def test_baidu_search(self):
- """
- 这里一定要 test 开头, 把测试逻辑代码封装到一个 test 开头的方法里.
- :return:
- """ # self.driver.find_element_by_id('kw').send_keys('selenium')
- # time.sleep(1)
- homepage = HomePage(self.driver)
- homepage.type_search('selenium') # 调用页面对象中的方法
- homepage.send_submit_btn() # 调用页面对象类中的点击搜索按钮方法
- time.sleep(2)
- homepage.get_windows_img() # 调用基类截图方法
- print(self.driver.title)
- try:
- assert('selenium' in HomePage.get_page_title(self))
- print('Test Pass.')
- except Exception as e:
- print('Test Fail.', format(e))
- def test_search2(self):
- homepage = HomePage(self.driver)
- homepage.type_search('python') # 调用页面对象中的方法
- homepage.send_submit_btn() # 调用页面对象类中的点击搜索按钮方法
- time.sleep(2)
- homepage.get_windows_img() # 调用基类截图方法
- if __name__ == '__main__':
- unittest.main()
5.3 运行结果:
运行代码后, 控制台打印如下图的结果
5.4 代码说明
homepage = HomePage(self.driver)
上面这行代码要注意, 意思是: 到一个页面, 第一件事情是初始化这个页面的一个页面对象实例. 注意, 一定要带 self.driver, 不然会报错, 这个 self.driver, 可以这样理解: 在当前测试类里面, self.driver 是来自浏览器引擎类中方法得到的, 在初始化一个页面对象
的时候, 也把这个来自浏览器引擎类的 driver 给赋值给当前的页面对象, 这样, 才能执行页面对象或者基类里面的相关 driver 方法. 写多了 selenium 的自动化脚本, 你会明白, 最重要的是保持前后 driver 的唯一性.
5.5 生成图片
1. 测试结果: 会在 logs 文件夹生成一个日志文件, 也会在 screenshots 文件夹生成一个 PNG 图片. 日志看过了, 这里我们看一下图片
2. 图片内容如下图:
6. 小结
好了, 今天的分享就到这里吧!!! 谢谢各位的耐心阅读. 有问题加群交流讨论
您的肯定就是我进步的动力. 如果你感觉还不错, 就请鼓励一下吧! 记得随手点波推荐不要忘记哦!!!
别忘了点 推荐留下您来过的痕迹
来源: https://www.cnblogs.com/du-hong/p/12066776.html