关键字驱动
关键字驱动的框架也称为表驱动的框架。在这里,我们有一个表,在其中描述了必须执行的方法的关键字或操作。除了把测试数据放到表(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(调用方法)
评论