关键字驱动

关键字驱动的框架也称为表驱动的框架。在这里,我们有一个表,在其中描述了必须执行的方法的关键字或操作。除了把测试数据放到表(excel, yaml, python列表)维护, 需要操作的浏览器指令放到表当中维护

什么叫做数据驱动?

答:所谓数据驱动就是同一段代码,参数一样,但是函数内部做了处理,所以输入不同的数据(值),得到不同的结果。代码还是最开始写的代码并有什么改变,只是输入值不一样结果也不一样,代码内部有分支选择而已

什么叫做关键字驱动?

答:所谓关键字驱动就是将数据驱动里的数据改为关键字而已。我们数据驱动是程序读取数据进行执行,那么是哪些代码模块调取的数据呢?数据驱动和关键字驱动在测试里说的最多,那我们就以UI自动化中界面元素定位为例来说明,我改变定位器的值,后续的代码没有任何改变,但是得到的值完全不一样,通过改变关键字,从而改变数据的输入位置,这就叫做关键字驱动,数据驱动结果是驱动代码得到不同的数据,关键字驱动结果是驱动代码得到不同代码

关键字驱动的用法

封装basepage.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/22 12:48
# @Author  : shisuiyi
# @File    : basepage.py
# @Software: win10 Tensorflow1.13.1 python3.9
import time

import allure
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait


class BasePage:
    """储存浏览器,页面的通用操作"""

    def __init__(self, driver):
        self.driver = driver

    def goto(self, url):
        self.driver.get(url)

    def reload(self):
        """刷新"""
        self.driver.refresh()

    def wait_element_clickable(self, locator, timeout=10):
        """等待元素可以被点击"""
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=0.2)
        el = wait.until(EC.element_to_be_clickable(locator))
        return el

    def wait_element_visible(self, locator, timeout=10):
        """等待元素可见"""
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=0.2)
        el = wait.until(EC.visibility_of_element_located(locator))
        return el

    def wait_title_is(self, title, timeout=10):
        """等待 title 等于"""
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=0.2)
        # EC.title_is(title) 内置的显性等待,页面的标题是否等于
        return wait.until(EC.title_is(title))

    def wait_url_contains(self, url, timeout=10):
        """等待 url包含"""
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=0.2)
        return wait.until(EC.url_contains(url))

    def get_element(self, locator):
        """查找元素"""
        el = self.driver.find_element(*locator)
        return el

    def get_elements(self, locator):
        """查找多个元素"""
        elements = self.driver.find_elements(*locator)
        time.sleep(1)
        return elements

    def write(self, locator, value):
        """输入操作"""
        el = self.driver.find_element(*locator)
        el.send_keys(value)
        return self  # return self返回的是类的实例。

    def click(self, locator):
        """鼠标点击。方法2"""
        el = self.wait_element_clickable(locator)
        #  el = self.driver.find_element(*locator)
        action = ActionChains(self.driver)
        action.click(el).perform()

    def double_click(self, locator):
        el = self.wait_element_clickable(locator)
        #  el = self.driver.find_element(*locator)
        action = ActionChains(self.driver)
        action.double_click(el).perform()

    def context_click(self, locator):
        el = self.driver.find_element(*locator)
        action = ActionChains(self.driver)
        action.context_click(el).perform()

    def click_and_hold(self, locator):
        """长按"""
        try:
            el = self.driver.find_element(*locator)
            action = ActionChains(self.driver)
            action.click_and_hold(el).perform()
        except Exception as e:
            print('[ERROR]-长按页面元素{}失败,原因:{}'.format(locator, e))

    def clear_element(self, locator):
        """元素清空"""
        self.wait_element_clickable(locator).clear()
        return self

    def select(self, locator1, locator2):
        """下拉选择"""
        self.click(locator1)
        self.click(locator2)

    def enter(self):
        """回车"""
        action = ActionChains(self.driver)
        action.send_keys(Keys.ENTER).perform()

    def send_file(self, locator, file_path):
        """发送文件"""
        el = self.wait_element_visible(locator)
        el.send_keys(file_path)

    def move_to(self, locator):
        """鼠标悬停, locator = ('xpath', 'value')"""
        el = self.driver.find_element(*locator)
        action = ActionChains(self.driver)
        action.move_to_element(el).perform()

    def drag_and_drop(self, locator_start, locator_end):

        start_el = self.wait_element_clickable(locator_start)
        end_el = self.wait_element_clickable(locator_end)
        action = ActionChains(self.driver)
        action.drag_and_drop(start_el, end_el).perform()

    def switch_to_iframe(self, iframe_reference, timeout=30):
        """iframe切换"""
        # self.driver.switch_to.frame(iframe_reference)
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=0.2)
        wait.until(EC.frame_to_be_available_and_switch_to_it(iframe_reference))
        # frame_to_be_available_and_switch_to_it 此方法会判断iframe是否可用,并且会自动切换到iframe中。

    def allure_screenshot(self, name=None):
        """截图"""
        f = self.driver.get_screenshot_as_png()
        return allure.attach(f,
                             name=name,
                             attachment_type=allure.attachment_type.PNG)

    def script(self, src):
        """
        定义script方法,用于执行js脚本
        """
        self.driver.execute_script(src)

    def switch_window(self, n):
        """窗口切换"""
        # WebDriver对象有window_handles 属性,这是一个列表对象, 里面包括了当前浏览器里面所有的窗口句柄。
        self.driver.switch_to.window(self.driver.window_handles[n])

    def assert_element_attribute_equal(self, locator, attr_name, expected):
        """断言元素的text文本等于"""
        el = self.wait_element_visible(locator)
        actual = el.get_attribute(attr_name)
        print("文本", actual)
        print(expected)
        assert actual == expected

准备yaml文件

keywords_login.yaml

-
  # 访问 url
  action: goto
  params:
    url: "https://v4.ketangpai.com/User/login.html"
-
  # 输入用户名
  action: write
  params:
    locator: ['css selector', 'input[name="account"]']
    value: "18******63"
-
  # 输入密码
  action: write
  params:
    locator: ['css selector', 'input[name="pass"]']
    value: "***521583****"
-
  # 点击登录
  action: click
  params:
    locator: ['css selector', '.btn-btn']
-
  # 断言
  action: assert_element_attribute_equal
  params:
    locator: ['css selector', '.avatar']
    attr_name: "title"
    expected: "师语"

封装数据解析类

data_handler.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/2/10 20:35
# @Author  : shisuiyi
# @File    : data_handler.py
# @Software: win10 Tensorflow1.13.1 python3.9
from configparser import ConfigParser
import yaml


class Config:
    def __init__(self, filename, encoding='utf-8'):
        self.filename = filename
        self.encoding = encoding
        self.suffix = self.filename.split('.')[-1]
        if self.suffix not in ['yaml', 'yml', 'cnf', 'conf', 'ini']:
            raise ValueError('不能识别的配置文件后缀:{}'.format(self.suffix))

    def parse_ini(self):
        """
        解析ini
        :return:
        """
        conf = ConfigParser()
        conf.read(self.filename)
        data = {}
        for section in conf.sections():
            data[section] = dict(conf.items(section))
        return data

    def parse_yaml(self):
        """
        解析yaml
        :return:
        """
        with open(self.filename, 'r', encoding=self.encoding) as f:
            data = yaml.load(f, Loader=yaml.FullLoader)
        return data

    def parse(self):
        """
        解析配置文件
        :return:
        """
        if self.suffix in ['yaml', 'yml']:
            return self.parse_yaml()
        else:
            return self.parse_ini()


if __name__ == '__main__':
    cm = Config(r'D:\Lemon\py45\webauto\data\keywords_login.yaml')
    res = cm.parse()
    print(res)

__init__.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/20 20:34
# @Author  : shisuiyi
# @File    : __init__.py
# @Software: win10 Tensorflow1.13.1 python3.9
from common.data_handler import Config

cm = Config(r'D:\Lemon\py45\webauto\data\keywords_login.yaml')

封装通用的测试方法

test_keywords.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/2/10 20:25
# @Author  : shisuiyi
# @File    : test_keywords.py
# @Software: win10 Tensorflow1.13.1 python3.9
from common import cm
from common.basepage import BasePage


def test_keywords(driver):
    steps = cm.parse()
    for step in steps:
        # step  {'action': 'goto', 'params': {'url': 'https://v4.ketangpai.com/User/login.html'}}
        method_name = step['action']  #
        params = step['params']  # {'url': 'https://v4.ketangpai.com/User/login.html'}
        # 通过方法的名称获取 basepage 对象中的同名方法,
        page = BasePage(driver)
        method = getattr(page, method_name)
        # 调用方法  method(url='')
        method(**params)

总结:

测试方法基本不用变化,只需要变化yaml中的数据即可。

yaml中的action代表需要调用的函数,存储在basepage中,缺什么就再进行封装。

有点类似接口的数据驱动思想,测试函数通用,只需要修改excel(测试数据)和basecase(调用方法)