Test Step:是一个小的测试步骤的描述或者测试对象的一个操作说明。
Test Object:是指页面对象或元素,就像用户名、密码,
Action指页面操作的动作,打开浏览器,点击一个按钮,文本框输入一串文本等。
Test Data: 是任何对象操作时所需要的值,就像用户名、密码进行输入时的输入内容。
其实我们做关键字的驱动的思想,就是把编码从测试用例和测试步骤中分离出来,这样对于不会编码的人员更容易理解自动化,从而让手工测试人员也可以编写自动脚本。(这并不意味这不需要自动化测试人员,对于自动化框架的构建,自动化代码的更新,结构调整等都需要一个技术性的人员)对于测试小的项目的团队,可以有两个手工测试人员和一个自动化测试人员。
封装所有的动作的几类:action_page.py
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver
import logging
import time
import os
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(filename)s - %(levelname)s - %(message)s",
datefmt='%a, %Y - %m - %d %H: %M: %S',
)
class ActionPage(object):
def __init__(self):
self.driver = None
if self.driver is None:
self.driver = webdriver.Chrome()
def open_browser(self, browser: str):
browser = str(browser).title()
if browser == 'Chrome':
self.driver = webdriver.Chrome()
elif browser == 'Firefox':
self.driver = webdriver.Firefox()
else:
self.driver = 'type error'
return self.driver
def open_url(self, url: str):
"""
获取url地址
使窗口最大化
设置隐性等待30秒
:param url:
:return:
"""
self.driver.get(url)
logging.info("Open url: {}".format(url))
self.driver.maximize_window()
logging.info("Maximizes the current window")
self.driver.implicitly_wait(30)
def quit(self):
logging.info("Quit the browser!")
self.driver.quit()
def close(self):
self.sleep(2)
logging.info("Closing and quit the browser.")
self.driver.close()
def sleep(self, seconds):
logging.info("Sleep for {} seconds".format(seconds))
time.sleep(seconds)
def is_displayed(self, element):
"""
判断元素是否存在
:param element: 元素对象(WebElement对象)
:return:
"""
value = element.is_displayed()
return value
def locate_element(self, loc):
"""
定位元素方法
:param loc: 定位器
:return:
"""
locator = loc
try:
# 注意:以下入参本身是元组,不需要加*
element = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(locator))
return element
except NoSuchElementException:
logging.error("Page not found the element: {}".format(locator))
except Exception as e:
logging.error("Locate Failed,Error:{}".format(e))
return self.driver.find_element(locator)
def send_value(self, loc: tuple, value: str, clear=True):
"""
输入值
:param loc: 定位器
:param value: 输入数据
:param clear: 是否清除
:return:
"""
element = self.locate_element(loc)
if clear:
element.clear()
try:
self.sleep(0.5)
element.send_keys(value)
logging.info("Input data: {}".format(value))
except Exception as e:
logging.error("Faild to input: {},Error: {}".format(value, e))
def click(self, loc):
"""
点击元素
:param loc: 定位器
:return:
"""
element = self.locate_element(loc)
try:
element.click()
logging.info("The element \' {} \' was clicked.".format(element.get_attribute('value')))
except Exception as e:
display = self.is_displayed(element)
if display is True:
self.sleep(3)
element.click()
logging.info('The element was clicked')
else:
self.save_image()
logging.error('Failed to click the element, Error: {}'.format(e))
def save_image(self):
"""
截图
:return:
"""
date = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
screen_name = os.path.join("/screenshots", '{}.png'.format(date))
try:
self.driver.get_screenshot_as_file(screen_name)
logging.info("Had take screenshot and save to folder : /screenshots")
except NameError as e:
logging.error("Failed to take screenshot! Error: {}".format(e))
def switch_frame(self, loc):
"""
切入 frame
:param loc: 定位器
:return:
"""
try:
frame = self.locate_element(loc)
self.driver.switch_to.frame(frame)
logging.info("Successful to switch to frame!")
except Exception as e:
logging.error("Failed to switch to frame: {}, Error: {}".format(loc, e))
def quit_frame(self):
"""
退出当前frame
:return:
"""
self.driver.switch_to.default_content()
logging.info("Successful to quit the current frame!")
def get_attribute(self, loc, attribute):
"""
获取元素属性
:param loc: 定位器
:param attribute: 属性名称
:return:
"""
try:
element = self.locate_element(loc)
return element.get_attribute(attribute)
except Exception as e:
logging.info("Failed to get attribute with %s" % e)
self.save_image()
raise
def get_title(self):
"""
获取title
:return:
"""
title = self.driver.title
logging.info('Current page title is:%s' % title)
return title
def get_text(self, loc):
"""
获取元素对象文本
:param loc: 定位器
:return:
"""
try:
text = self.locate_element(loc).text
logging.info("Get the text: {}".format(text))
return text
except Exception as e:
logging.error("Faild to Get the text: {} Error: {}".format(loc, e))
def forward(self):
"""
浏览器前进
:return:
"""
self.driver.forward()
logging.info("Click forward on current page.")
def back(self):
"""
浏览器后退
:return:
"""
self.driver.back()
logging.info("Click back on current page.")
def execute_js(self, script):
"""
执行 script 语句
:param script: JS 语句
:return:
"""
try:
self.driver.execute_script(script)
logging.info("Successful to execute JS statements: {}" % script)
except Exception as e:
logging.error("Faild to execute JS statements, Error: {}".format(e))
def assert_title(self, title):
"""
断言title是否匹配
:param title:
:return:
"""
if title != self.driver.title:
logging.error("Assert Failed: {} is equal driver.title".format(title))
return 0
logging.info("Assert Passed: {} is not equal driver.title".format(title))
return 1
def assert_in_page_source(self, data):
"""
断言数据是否存在page_source资源中
:param data:
:return:
"""
if data not in self.driver.page_source:
logging.error("Assert Failed: {} not in page_source".format(data))
return 0
logging.info("Assert Passed: {} in page_source".format(data))
return 1
if __name__ == '__main__':
ap = ActionPage()
ap.open_url('https://www.baidu.com')
locator = ('id', 'kw')
ap.send_value(loc=locator, value='刘亦菲')
locator2 = ('id', 'su')
ap.click(locator2)
ap.assert_in_page_source('刘亦菲')
ap.close()
run.py
from openpyxl import load_workbook
from datetime import datetime
from action.action_page import ActionPage
import traceback
import os
def run_test(filename):
wb = load_workbook(filename)
# 获取sheet_names
for sheetname in wb.sheetnames:
ws = wb[sheetname]
run = Run(ActionPage())
# 获取最大行数
ws_rows_max = ws.max_row
# sheet.rows为生成器, 里面是每一行的数据,每一行又由一个tuple包裹
idx = 2
while idx <= ws_rows_max:
# 关键字
keyword = ws.cell(row=idx, column=3).value
# 定位方式
by = ws.cell(row=idx, column=4).value
# 元素定位表达式
loc = ws.cell(row=idx, column=5).value
# 构造定位器
if by and loc:
locator = (by, loc)
else:
locator = None
# 操作值
data = ws.cell(row=idx, column=6).value
if not data:
data = None
try:
# 根据传入的方法名来确定每一步执行哪一个方法
status = run.run_method(keyword, locator, data)
# 获取当前系统时间,作为测试用例执行时间
curr_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
ws.cell(row=idx, column=7).value = curr_time
# 断言
if str(keyword).startswith('assert'):
if status == 1:
# 如果执行无误则将执行结果写入Pass
ws.cell(row=idx, column=8).value = 'Passed'
elif status == 0:
# 如果执行错误则将执行结果写入Failed
ws.cell(row=idx, column=8).value = 'Failed'
wb.save(filename)
else:
# 如果执行无误则将执行结果写入Pass
ws.cell(row=idx, column=8).value = 'Pass'
wb.save(filename)
except Exception:
# # 出现错误时截图保存到表格:只用到一个PIL模块,但PIL是Python2专有的,没有Python3版本。Python3应安装pillow,用的时候和PIL是一样的。
# im = ImageGrab.grab()
# image_path = f'screenshot/{idx-1}.png'
#
# im.save(image_path)
# ws.cell(row=idx, column=10).value = image_path
# 如果执行出现异常,测试结果写入 异常,同时将异常信息写入到表格中
ws.cell(row=idx, column=8).value = '异常'
# 写入异常信息
ws.cell(row=idx, column=9).value = traceback.format_exc()
idx += 1
wb.save(filename)
class Run:
def __init__(self, obj):
self.obj = obj
def run_method(self, method: str, locator=None, send_value=None):
"""
反射机制
:param method: 方法名称
:param locator: 定位器
:param send_value: 传入数据
:return:
"""
if hasattr(ActionPage, method):
action_func = getattr(self.obj, method)
if send_value:
if locator:
return action_func(locator, send_value)
else:
return action_func(send_value)
else:
if locator:
return action_func(locator)
else:
return action_func()
else:
print("ActionPage not has attribute: {}".format(method))
if __name__ == '__main__':
BASE_DIR = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
DATA_DIR = os.path.join(BASE_DIR, "data")
xlsx_path = os.path.join(DATA_DIR, "data.xlsx")
print(xlsx_path)
run_test(xlsx_path)
# run = Run(ActionPage())
# run.run_method('open_url', send_value="https://www.baidu.com")
# run.run_method('send_value', ('id', 'kw'), "刘亦菲")
# run.run_method('click', ('id', 'su'))
# run.run_method('close')
EXCEL文件:data.xlsx
序号 | 操作步骤 | 关键词 | 定位方式 | 定位元素 | 传入数据 | 测试执行时间 | 测试结果 | 错误信息 |
---|---|---|---|---|---|---|---|---|
1 | 打开百度 | open_url | https://www.baidu.com | |||||
2 | 定位输入框,输入:刘亦菲 | send_value | id | kw | 林俊杰 | |||
3 | 点击百度一下 | click | id | su | ||||
4 | 断言数据存在于源码中 | assert_in_page_source | 林俊杰 | |||||
5 | 关闭页面 | close |