UI 自动化测试一直是老生常谈的话题,
作为处于"金字塔顶端"的自动化手段,用的不好就是一场灾难,用的好也无法雪中送炭。
在本篇文章中,我将记录一下我"第二次"设计 UI 测试的心得体会。
POM(页面对象模型)相信大家已经非常熟悉了,核心思想就是每个页面对应一个类,在每个类中放置页面中的定位信息以及相关操作函数。
然而在 POM 的基础上,我们其实可以将每个页面的操作抽象成字典映射。这样一来,所有的用例执行对应的操作会变得非常清晰。
self.actions_map = {
    '点击职位管理标签': self._click_job_position_management_tab,
    '点击新增职位按钮': self._click_add_job_position_button,
    '输入职位信息': self._input_job_position_info,
}
 
在实战中,也相信大家发现不少页面带有重复的元素,比如标签栏,导航栏等通用组件。
针对这个情况,我们可以设计一个页面基础类,储存所有页面通用的 基础操作 与 通用定位,所有独立的页面类都可以继承这个基础类,从而使得代码架构变得更加整洁,有效减少重复代码。
class PageObjectModel:
    LOCATORS = {
        'el-message--success': {'css selector': '.el-message--success'},
        'bread_crumb_nav': {'css selector': 'div[role=\'navigation\']'},
    }
    def __init__(self, browser=None):
        self.browser = browser
        self.actions_map = {}
        self.base_actions_map = {
            '检查并激活主页面包屑导航': self._check_and_activate_tab_bar,
            '切换至新开窗口': self._switch_to_new_window,
            '关闭当前窗口': self._close_current_window,
            '关闭新开窗口': self._close_new_window,
        }
    def operate(self, action, **kwargs):
        if action in self.actions_map.keys():
            self.actions_map[action](**kwargs)
        elif action in self.base_actions_map.keys():
            self.base_actions_map[action](**kwargs)
        else:
            preporter.warn(msg=f'action: 【{action}】 不在 action_map 内!')
 
这里的设计关键有两点:
初始化 base_actions_map,也就是我们的基础操作字典,我们要在这里对所有的测试执行步骤和对应的执行函数进行映射。
实现 operate 方法,所有的用例执行操作步骤都将通过 operate 方法 优先 在本身的操作字典中寻找映射函数。
基础类设计完毕~。
假设我们现在需要对某个页面做 UI 自动化,我们的只需要继承刚刚实现 PageObjectModel,
然后构造 actions_map 就可以了~
class JobPositionManagement(PageObjectModel):
    LOCATORS = {
        # 职位管理列表页元素
        'job_position_management_page': {'tag name': 'li', 'text': '职位管理'},
    }
    def __init__(self, browser=None):
        super(JobPositionManagement, self).__init__(browser)
        self.actions_map = {
            '点击职位管理标签': self._click_job_position_management_tab,
        }
    def _click_job_position_management_tab(self, browser=None):
        _browser = self.browser if self.browser else browser
    _browser.v_click(locate_rule=self.LOCATORS['job_position_management_page'])
 
最后在实际测试调用中,代码会变得非常整洁清晰,而这也是程序设计的魅力。
@HistoricalLogsDeletion
   @Test(enabled=False, tags=["职位管理", "回归测试", "冒烟测试"], description="验证点击【生成JD】后,JD输入框是否生成数据", timeout=30)
   def test_06(self):
       self.job_position_management.operate(action='生成JD')
       self.job_position_management.operate(action='等待页面加载完毕')
       self.job_position_management.operate(action='验证JD输入框内是否存在数据')
 虽然大部分公司都无法成功在 UI 自动化测试中获得正收益,但测试技术的发展永无止境~