前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3.UI自动化测试框架搭建-封装元素操作基类

3.UI自动化测试框架搭建-封装元素操作基类

作者头像
zx钟
发布2022-03-29 16:37:52
5850
发布2022-03-29 16:37:52
举报
文章被收录于专栏:测试游记测试游记

ElementOperator基类封装(部分)

完整代码见:

https://github.com/zx490336534/selenium-po/blob/master/selenium_po/elementoperator.py

init方法

在初始化的时候,完成Yaml文件的读取与解析

file_name对应的是Yaml文件中的pageName

代码语言:javascript
复制
class ElementOperator:
    def __init__(self, path=None, file_name=None):
        """

        :param path: 元素定位yaml文件
        :param file_name: 页面名
        """
        self.path = path
        self.file_name = file_name or os.path.splitext(os.path.split(path)[-1])[0]
        self.data_dict = self._parse_yaml()  # 读取Yaml文件内容
        self._locator_map = self.read_yaml()  # 页面元素定位解析

使用os模块切割文件夹路径,也就是如果没有传入file_name的时候默认pageName就是这个文件名

代码语言:javascript
复制
>>> path = r"/Users/zhongxin/gitproject/wytest/report/demo/home.yaml"

>>> os.path.split(path)
('/Users/zhongxin/gitproject/wytest/report/demo', 'home.yaml')

>>> os.path.split(path)[-1]
'home.yaml'

>>> os.path.splitext(os.path.split(path)[-1])
('home', '.yaml')

>>> os.path.splitext(os.path.split(path)[-1])[0]
'home'

_parse_yaml方法

读取yaml文件,如果出现问题那就返回一个空的字典

因为该类主要功能不是操作yaml,所以在函数前面增加了一个下划线_。表示它名义上是个私有的函数

代码语言:javascript
复制
def _parse_yaml(self):
    """
    读取Yaml文件内容
    :return:
    """
    data_dict = {}
    try:
        with open(self.path, 'r+', encoding='utf-8') as f:
            data_dict = yaml.load(f, Loader=yaml.FullLoader) or {}
    except Exception as e:
        raise Exception(e)
    finally:
        return data_dict

read_yaml方法

使用硬编码的方式去读取指定格式的yaml文件中的元素定位语句,并使用Locator将他们实例化后存入locator_map这个字典中

代码语言:javascript
复制
def read_yaml(self):
    """
    页面元素定位解析
    :return:
    """
    pages_list = self.data_dict["pages"]
    locator_map = dict()
    for page in pages_list:
        page_name = page["page"]["pageName"]
        page_desc = page["page"]["desc"]
        locator_map[page_name] = dict()
        locators_list = page["page"]["locators"]
        for locator in locators_list:
            by_type = locator["type"]
            element = locator["value"]
            wait_sec = int(locator.get("timeout", 3))
            locator_name = locator["name"]
            desc = f"{page_desc}_{locator['desc']}"
            tmp = Locator(element, wait_sec, by_type, locator_name, desc)
            locator_map[page_name][locator_name] = tmp
    return locator_map

实现get_locator方法

从yaml解析并转换后的字典中拿到指定的元素定位Locator对象

代码语言:javascript
复制
def get_locator(self, locator_name):
    locator = self._locator_map.get(self.file_name)
    if locator:
        locator = locator.get(locator_name)
    return locator

实现getattr方法

为了通过「实例名称.属性名」的方式来拿到元素定位信息,需要实现__getatter__魔术方法

__getatter__** 函数:如果在实例以及对应的类中**查找属性失败, 那么会调用到类的__getatter__函数

为了防止重复调用出现「Fatal Python error: Cannot recover from stack overflow. Python runtime state: initialized」问题,在首行判断一下_locator_mapfile_name有没有存在dir(self)

代码语言:javascript
复制
def __getattr__(self, item):
    if "_locator_map" in dir(self) and "file_name" in dir(self):
        if item in self._locator_map[self.file_name]:
            locator = self.get_locator(item)
            if locator:
                return locator
            else:
                return self[item]

实现find_element方法

代码语言:javascript
复制
def find_element(self, locator):
    self.wait_for(locator.wait_sec)
    web_ele = self._get_element(locator)
    return web_ele
实现wait_for方法

implicitly_wait:隐式等待 当使用了隐式等待执行测试的时候,如果 WebDriver没有在 DOM中找到元素,将继续等待,超出设定时间后则抛出找不到元素的异常。也就是说当查找元素或元素并没有立即出现的时候,隐式等待将等待一段时间再查找 DOM,默认的时间是0。一旦设置了隐式等待,则它存在整个 WebDriver 对象实例的声明周期中,隐式的等到会让一个正常响应的应用的测试变慢,它将会在寻找每个元素的时候都进行等待,这样会增加整个测试执行的时间。

代码语言:javascript
复制
def wait_for(self, wait_sec):
    self.driver.implicitly_wait(wait_sec)
实现_get_element方法

由于隐式等待部分版本的driver可能会不支持,容易出现不太稳定的问题,所以采用显式等待的方式,并加下亿点点细节

  1. 每隔0.5秒查找一次元素,直到时间超过等待时间,然后raise抛出异常
  2. 查找到元素后使用height_light对元素进行高亮标记
代码语言:javascript
复制
def _get_element(self, locator):
    start_time = time.time()
    while True:
        try:
            value_text = locator.desc
        except:
            value_text = ""
        try:
            locator_t = self._get_locator_tuple(locator)
            web_element = self.driver.find_element(*locator_t)
            try:
                self.height_light(web_element)
            except Exception:
                pass
            return web_element
        except NoSuchElementException as n:
            time.sleep(0.5)
            if time.time() - start_time >= self.time_out:
                raise NoSuchElementException(f"{self.time_out}秒后仍没有找到元素「{value_text}」:{n}")
        except WebDriverException as w:
            time.sleep(0.5)
            if time.time() - start_time >= self.time_out:
                raise WebDriverException(f"{self.time_out}秒后浏览器仍异常「{value_text}」:{w}")
        except Exception as e:
            raise Exception(f"查找元素异常:{e}")
实现height_light代码高亮

效果如下

代码语言:javascript
复制
def height_light(self, element):
    """
    元素高亮
    :param element:
    :return:
    """
    self.driver.execute_script("arguments[0].setAttribute('style',arguments[1]);",
                               element, "border:2px solid red;")

结合Allure进行元素具体操作的函数编写

https://docs.qameta.io/allure/#_steps_5

在Allure的官方文档中可以看到,使用pytest+allure可以在函数的头部获取入参的内容

代码语言:javascript
复制
import allure

@allure.step('Step with placeholders in the title, positional: "{0}", keyword: "{key}"')
def step_with_title_placeholders(arg1, key=None):
    pass

根据这个语法,我们将元素的操作步骤写到函数的头部

比如我现在要判断一个元素是否存在

代码语言:javascript
复制
@allure.step("查看「{locator}」是否存在")
def has_element(self, locator):
    ret = False
    try:
        self.find_element(locator)
        ret = True
    except Exception:
        pass
    return ret

具体效果:

代码语言:javascript
复制
with allure.step("查看登录名是否存在"):
    assert login.has_element(login.user_info_name), '登录名不存在'

同理,封装一下send_keysclick 等常用元素操作方法

代码语言:javascript
复制
@allure.step("往「{locator}」输入「{msg}」")
def input(self, locator, msg, clear=True):
    ele = self.find_element(locator)
    if clear:
        ele.clear()
    try:
        ele.send_keys(msg)
    except Exception as e:
        logger.error(f"往「{locator}」输入「{msg}」失败:{e}"

其他封装及完整代码见

https://github.com/zx490336534/selenium-po/blob/master/selenium_po/elementoperator.py

小结

到此,UI框架的元素操作部分已经完成了

不过为了让它从demo阶段变成一个更好用的框架,还有亿点点细节需要补充

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 测试游记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ElementOperator基类封装(部分)
    • init方法
      • _parse_yaml方法
        • read_yaml方法
          • 实现get_locator方法
            • 实现getattr方法
              • 实现find_element方法
                • 实现wait_for方法
                • 实现_get_element方法
                • 实现height_light代码高亮
            • 结合Allure进行元素具体操作的函数编写
            • 小结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档