• 定义 WebDriverAgent ```python

    !/usr/local/bin/py3

    -- coding:utf-8 --

“”” @File: common.py @Description: 核心文件。是浏览器驱动实例的封装,元素操作等。适用于Web自动化 @Author: hailong.chen @Date: 12/11/20 “”” import logging import os import time from datetime import datetime

from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support.select import Select from selenium.webdriver.support.wait import WebDriverWait

logging.basicConfig(level=logging.INFO, format=’%(asctime)s - %(filename)s - %(funcName)s - LINE: %(lineno)s -%(levelname)s - %(message)s’) logger = logging.getLogger(name)

_HIGH_LIGHT = “background: yellow; board: 2px solid red”

_chrome_driver = ‘/Library/Frameworks/Python.framework/Versions/3.9/bin/chromedriver_current’

class BaseWebDriverAgent(object): def init(self, executable_path): try: if executable_path: self.driver = webdriver.Chrome(executable_path=executable_path) else: self.driver = webdriver.Chrome(executable_path=_chrome_driver) except Exception: raise

  1. def get(self, url):
  2. self.driver.get(url)
  3. def refresh(self):
  4. self.driver.refresh()
  5. @property
  6. def window_handles(self):
  7. return self.driver.window_handles
  8. @property
  9. def switch_to(self):
  10. return self.driver.switch_to
  11. def maximize_window(self):
  12. self.driver.maximize_window()
  13. def fullscreen_window(self):
  14. self.driver.fullscreen_window()
  15. def screenshot_as_png(self):
  16. dt = datetime.now().strftime('%Y-%m-%d')
  17. _path_dir = "../../screenshots/%s" % dt
  18. now = datetime.now().strftime('%H:%M:%S')
  19. print("当前的工作目录的绝对路径:" + os.getcwd())
  20. if os.path.exists(_path_dir):
  21. self.driver.get_screenshot_as_file(filename='%s/%s.png' % (_path_dir, now))
  22. else:
  23. try:
  24. os.makedirs(_path_dir)
  25. self.driver.get_screenshot_as_file(filename='%s/%s.png' % (_path_dir, now))
  26. except (IOError, FileExistsError, FileNotFoundError):
  27. raise Exception("截图文件创建异常")
  28. def get_screenshot_as_base64(self):
  29. return self.driver.get_screenshot_as_base64()
  30. def quit(self):
  31. self.driver.quit()
  32. def close(self):
  33. self.driver.close()

class WebDriverAgent(BaseWebDriverAgent): def init(self, executablepath=chrome_driver): super().__init(executable_path)

  1. def find_element(self, locator, value=None, timeout=10):
  2. try:
  3. # 解决 IDE 无法判断 < el > 对象的类型,避免导致引用方法时 IDE 无提示问题(实际可调用)而加入类型注解
  4. el = WebDriverWait(self.driver, timeout).until(
  5. lambda driver: driver.find_element(by=locator, value=value)) # type: WebElement
  6. self.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", el, _HIGH_LIGHT)
  7. return el
  8. except (AttributeError, Exception):
  9. self.screenshot_as_png()
  10. logger.info("\n 》》》====== DOM节点 %s \t 在 %s 秒内没找到 ====== 《《《 \n" % ((locator, value), timeout))
  11. def find_elements(self, locator, value, timeout=10):
  12. try:
  13. # 解决 IDE 无法判断 < el > 对象的类型,避免导致引用方法时 IDE 无提示问题(实际可调用)而加入类型注解
  14. els = WebDriverWait(self.driver, timeout).until(
  15. lambda driver: driver.find_elements(by=locator, value=value)) # type: WebElement
  16. for i in range(len(els)):
  17. self.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", els[i], _HIGH_LIGHT)
  18. return els
  19. except (AttributeError, Exception):
  20. self.screenshot_as_png()
  21. logger.info("\n 》》》====== DOM节点 %s \t 在 %s 秒内没找到 ====== 《《《 \n" % ((locator, value), timeout))
  22. def find_element_by_css_selector(self, css_selector, timeout=10):
  23. try:
  24. # 解决 IDE 无法判断 < el > 对象的类型,避免导致引用方法时 IDE 无提示问题(实际可调用)而加入类型注解
  25. el = WebDriverWait(self.driver, timeout).until(
  26. lambda driver: driver.find_element_by_css_selector(css_selector)) # type: WebElement
  27. self.driver.execute_script("arguments[0].setAttribute('style', arguments[1]);", el, _HIGH_LIGHT)
  28. return el
  29. except (AttributeError, Exception):
  30. self.screenshot_as_png()
  31. logger.info("\n 》》》====== DOM节点 %s \t 在 %s 秒内没找到 ====== 《《《 \n" % (css_selector, timeout))
  32. def move_to_element(self, web_element: WebElement):
  33. """
  34. :param web_element: WebElement
  35. :return: None
  36. """
  37. try:
  38. ActionChains(self.driver).move_to_element(web_element).perform()
  39. time.sleep(1)
  40. except (AttributeError, Exception):
  41. raise
  42. @staticmethod
  43. def item_select(web_element, text):
  44. """该方法只对标准下拉框<select> </select>有效"""
  45. s = Select(web_element)
  46. s.select_by_visible_text(text)
  47. def mouse_click(self, web_element):
  48. try:
  49. ActionChains(self.driver).click(web_element).perform()
  50. except (AttributeError, Exception):
  51. raise
  52. def scroll_to_target(self, web_element):
  53. """用于浏览器内滚动元素到可视窗口以定位元素"""
  54. try:
  55. (self.driver.execute_script("arguments[0].scrollIntoView();", web_element))
  56. time.sleep(1)
  57. except:
  58. raise
  59. def drag_and_drop(self, source_element, target_element):
  60. try:
  61. ActionChains(self.driver).drag_and_drop(source_element, target_element).perform()
  62. except:
  63. raise
  64. def drag_and_drop_by_offset(self, source_element, xoffset, yoffset):
  65. try:
  66. ActionChains(self.driver).drag_and_drop_by_offset(source_element, xoffset, yoffset).perform()
  67. except:
  68. raise
  69. def context_click(self, on_element):
  70. """鼠标右键单击
  71. """
  72. try:
  73. ActionChains(self.driver).context_click(on_element).perform()
  74. except:
  75. raise
  76. def double_click(self, on_element):
  77. """鼠标左键双击"""
  78. try:
  79. ActionChains(self.driver).double_click(on_element).perform()
  80. except:
  81. raise
  82. def get_size(self, web_element: WebElement) -> dict:
  83. """The size of the element.
  84. i.e {"height": size["height"], "width": size["width"]}
  85. """
  86. self.move_to_element(web_element)
  87. return web_element.size
  88. def get_rect(self, web_element: WebElement) -> dict:
  89. """A dictionary with the size and location of the element."""
  90. self.move_to_element(web_element)
  91. return web_element.rect
  92. def get_location(self, web_element: WebElement) -> dict:
  93. """The location of the element in the renderable canvas."""
  94. self.move_to_element(web_element)
  95. return web_element.location
  96. def get_value_of_css_property(self, web_element: WebElement, property_name):
  97. self.move_to_element(web_element)
  98. return web_element.value_of_css_property(property_name)
  99. def input_text(self, web_element: WebElement, text):
  100. self.move_to_element(web_element)
  101. return web_element.send_keys(text)
  102. def move_by_offset(self, xoffset, yoffset):
  103. return ActionChains(self.driver).move_by_offset(xoffset, yoffset)
  104. def click_and_hold(self, on_element=None):
  105. return ActionChains(self.driver).click_and_hold(on_element)
  106. def release(self, on_element=None):
  107. return ActionChains(self.driver).release(on_element)
  1. <a name="Xjot9"></a>
  2. #### 钩子-定义夹具
  3. > @pytest.fixture
  4. ```python
  5. #!/usr/local/bin/py3
  6. # -*- coding:utf-8 -*-
  7. """
  8. @File: conftest.py
  9. @Description: 工程的核心文件,定义了夹具和测试报告等。
  10. Pytest-hmtl的报表增强,其文件路径、文件名、方法名等名称均是hook实现,不要做更改。详情请通篇阅读
  11. https://pytest-html.readthedocs.io/en/latest/user_guide.html#enhancing-reports ,
  12. https://docs.qameta.io/allure-report/#_attachments_5
  13. @Author: hailong.chen
  14. @Date: 2020/12/26
  15. """
  16. import time
  17. from datetime import datetime
  18. import allure
  19. from py.xml import html
  20. import pytest
  21. from utils.common import WebDriverAgent
  22. def pytest_html_results_table_header(cells):
  23. cells.insert(1, html.th("Description"))
  24. cells.insert(3, html.th("Datetime", class_="sortable time", col="time"))
  25. cells.pop()
  26. def pytest_html_results_table_row(report, cells):
  27. cells.insert(1, html.td(report.description))
  28. cells.insert(3, html.td(datetime.now().strftime('%D %H:%M:%S'), class_="col-time"))
  29. cells.pop()
  30. @pytest.hookimpl(hookwrapper=True)
  31. def pytest_runtest_makereport(item, call):
  32. pytest_html = item.config.pluginmanager.getplugin("html")
  33. outcome = yield
  34. report = outcome.get_result()
  35. setattr(report, "duration_formatter", "%H:%M:%S.%f")
  36. report.description = str(item.function.__doc__)
  37. extra = getattr(report, "extra", [])
  38. if report.when == "call":
  39. xfail = hasattr(report, "wasxfail")
  40. if (report.skipped and xfail) or (report.failed and not xfail):
  41. # only add additional html on failure
  42. file_name = report.nodeid.replace("::", "_") + ".png"
  43. img = capture_screenshot_as_base64()
  44. if file_name:
  45. html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \
  46. 'onclick="window.open(this.src)" align="right"/></div>' % img
  47. extra.append(pytest_html.extras.html(html))
  48. # allure 获取截图文件
  49. with allure.step('执行出现失败时截图如下'):
  50. _png = get_screenshot_as_png()
  51. allure.attach(_png, '截图信息', allure.attachment_type.PNG)
  52. report.extra = extra
  53. def pytest_html_results_table_html(report, data):
  54. if report.passed:
  55. del data[:]
  56. data.append(html.div("No log output captured.", class_="empty log"))
  57. driver = None
  58. @pytest.fixture(scope='session', autouse=True)
  59. def browser(request):
  60. global driver
  61. driver = WebDriverAgent()
  62. #driver.get(url)
  63. time.sleep(2)
  64. # 此处是因为浏览器测试驱动出现了白屏,进行一次刷新
  65. driver.refresh()
  66. def end():
  67. driver.quit()
  68. request.addfinalizer(end)
  69. return driver # type: WebDriverAgent
  70. def capture_screenshot_as_base64():
  71. return driver.get_screenshot_as_base64()
  72. def get_screenshot_as_png():
  73. return driver.get_screenshot_as_png()
  74. def pytest_configure(config):
  75. """
  76. 通过 @pytest.mark 调用。用于测试用例管理
  77. e.g @pyetest.mark.bvt 即定义某用例级别为构建认证测试(金丝雀测试)
  78. """
  79. marker_list = ["bvt", "smoke"]
  80. for markers in marker_list:
  81. config.addinivalue_line("markers", markers)
  • 调用 fixture ```python

    !/usr/local/bin/py3

    -- coding:utf-8 --

“”” @File: test_login.py @Description: @Author: hailong.chen @Date: 2020/12/24 “”” import pytest

from page_metadata.website_dom import PageDom

@pytest.mark.usefixtures(‘browser’) class TestLogin: “””website登录业务”””

  1. def test_login(self, browser, acc='*******', password='******'):
  2. """正常登录"""
  3. browser.find_element(PageDom.LOGIN_ACC[0], PageDom.LOGIN_ACC[1]).send_keys(acc)
  4. browser.find_element(PageDom.LOGIN_PASSWORD[0], PageDom.LOGIN_PASSWORD[1]).send_keys(password)
  5. browser.find_element(PageDom.LOGIN_BTN[0], PageDom.LOGIN_BTN[1]).click()
  6. flag = browser.find_element(PageDom.LOGO_IMG[0], PageDom.LOGO_IMG[1])
  7. assert flag, "登录异常"
  1. ---
  2. <a name="YJrAR"></a>
  3. #### 钩子-添加命令与参数、添加自定义markers
  4. > pytest_configure(config)
  5. > pytest_addoption(parser)
  6. > pytest_config.getoption(request)
  7. **如果命令行调用自定义参数时不生效,通常是需要**
  8. > pytest -h 可查看
  9. 1. **先行切换到对应注册的 **`**conftest.py**`**的目录,然后调用注册的命令参数 **
  10. **如果命令行调用自定义标记时不生效,解决的办法有两种**
  11. > pytest --markers 可查看
  12. 1. **模块名以 test_*.py *_test.py 命名**
  13. 2. **先行切换到对应注册的 **`**conftest.py**`**的目录,然后调用注册的命令参数 **
  14. ```python
  15. def pytest_configure(config):
  16. """
  17. 注册配置信息,可用于测试用例调度管理。
  18. 基于测试发现的规则(cases/website目录下),通过命令行形如 `pytest -m bvt` 或 `pytest -m 'not bvt'` 调用
  19. 在 contest.py 所属的目录下,命令调用 `pytest --markers` 可查看有效的标记
  20. e.g @pyetest.mark.bvt 即标记某用例为"bvt"
  21. """
  22. config.addinivalue_line("markers", "bvt")
  23. config.addinivalue_line("markers", "smoke")
  24. def pytest_addoption(parser):
  25. """命令参数注册"""
  26. parser.addoption("--env_config", dest="NAME", choices=["default_config", "local_config"],
  27. default="local_config",
  28. help="--env_config参数的选项值只能是'default_config','local_config', 如`--env_config=local_config`")
  29. @pytest.fixture(scope="session")
  30. def env_config(request):
  31. """获取命令参数的值"""
  32. return request.config.getoption("--env_config")