接上篇:webui自动化测试框架(1)——框架介绍
封装driver.py: driver的初始化,查找元素,点击元素,输入内容,切换iframe,切换Windows等 1.类,构造函数init里面适配浏览器,浏览器的窗口最大化,设置隐式等待,默认20秒。 2.定位元素进行封装find_element方法 参数ele_info 数据类型是字典的类型{‘type’:‘xpath’,‘value’:‘xxx’},type表示定位类型,value表示元素定 位的值 返回值是目标元素 3.get_by方法 主要是获取最终的定位类型和元素定位的值,即locator=(type,value) 4.click方法 采用之前封装的显示等待的条件 5.get 打开浏览器链接 6.quit 退出浏览器 7.page_source:获取页面源码 8.page_contains:测试用例层做断言用的,这里面采用显示等待的方式,lambda匿名函数如果包含返回True,否则返回False。
封装driver.py
浏览器
webUI自动化大多数公司用的是Chrome浏览器,但也不排除其他浏览器的使用,还有一种情况是在要求测试兼容性的时候,会用到不同的浏览器,因此需要对不同的浏览器进行适配(前提:不同浏览器都安装了对应的webdriver) 这里封装了Chrome、Firefox、IE、edge浏览器,可以根据实际情况自行往里添加相关浏览器 代码如下:
class InitDriver:
def __init__(self,browser:str):
self.logger = GetLogger().get_logger()
if browser.lower() == 'chrome':
self.driver = webdriver.Chrome()
elif browser.lower() == 'firefox':
self.driver = webdriver.Firefox()
elif browser.lower() == 'ie':
self.driver = webdriver.Ie()
elif browser.lower() == 'edge':
self.driver = webdriver.Edge()
else:
self.logger.error('不支持的浏览器类型:{}'.format(browser))
raise Exception('不支持的浏览器类型:{}'.format(browser))
self.driver.maximize_window()
self.driver.implicitly_wait(20)
元素信息
传入元素可作为目标元素,供后面的鼠标操作和定位时调用
def find_element(self,ele_info):
try:
locator = self.get_by(ele_info)
el = self.driver.find_element(*locator)
self.logger.info(f'查找{ele_info}元素成功')
return el
except Exception as e:
self.logger.error(f'查找{ele_info}元素失败,报错信息为{e}')
raise Exception(f'查找{ele_info}元素失败,报错信息为{e}')
网页地址
根据需求不同,每次登陆的网址也不同,这里对打开的目标网址也写了一个方法,后面可以直接调用
def get(self,url):
self.driver.get(url)
self.logger.info(f'登录{url}网站')
点击操作
自动化测试过程中无时无刻不涉及鼠标和键盘操作,除了元素定位以外,其他都是由键盘和鼠标操作来完成的。比如一个简单的搜索动作,首先通过键盘向搜索框键入内容(send_keys),然后通过鼠标点击搜索按钮(click)完成搜索 在selenium中,所有的键盘鼠标动作都来自于一个类ActionChains,先来导入它
from selenium.webdriver.common.action_chains import ActionChains
定位到元素,然后进行点击
def click(self,ele_info):
try:
wait=WebDriverWait(self.driver,20)
locator = self.get_by(ele_info)
wait.until(element_click_is_success(locator))
self.logger.info(f'元素{ele_info}点击成功')
except Exception as e:
self.logger.error(f'元素{ele_info}点击失败,报错信息为{e}')
raise Exception(f'元素{ele_info}点击失败,报错信息为{e}')
输入操作
def send_keys(self,ele_info,text):
try:
element = self.find_element(ele_info)
element.clear()
element.send_keys(text)
self.logger.info(f'元素{ele_info}输入文本内容{text}成功')
except Exception as e:
self.logger.error(f'{ele_info}输入失败,报错信息为{e}')
raise Exception(f'{ele_info}输入失败,报错信息为{e}')
页面源码(用于断言)
def page_source(self):
try:
self.logger.info('获取页面源码')
return self.driver.page_source
except Exception as e:
self.logger.error(f'获取页面源码失败,报错信息为{e}')
raise Exception(f'获取页面源码失败,报错信息为{e}')
断言:提示信息是否在页面源码中
def page_contains(self,text):
try:
wait = WebDriverWait(self.driver,20)
flag =wait.until(lambda d: text in d.page_source)
self.logger.info(f'页面源码中包含{text}文本')
except Exception as e:
flag = False
self.logger.warn(f'页面源码中不包含{text},报错信息不包含{e}')
return flag
退出浏览器
每个测试场景类测试完成后都需要推出浏览器,也就意味着每个case都要写一次退出,所以在这里也把推出写成了一个方法,后面通过调用就可以了
def quit(self):
try:
self.driver.quit()
self.logger.info('driver退出成功')
except Exception as e:
self.logger.error(f'driver退出失败,报错信息为{e}')
定位方式
这里只封装了几种基本元素定位方式: id、name、classname、tagname、css、xpath、linktext、plinktext 复数定位可以自行加上
def get_by(self,ele_info):
'''
主要是获取定位策略locator的值
ele_info数据类型是自己设计的,字典
ele_info = {'type':name/xpath/id/css/classname/tagname/linktext/plinktext,'value':值}
type = ele_info.get('type') # 定位策略
value = ele_info.get('value') # 值
driver.find_element(type,value).send_keys('123456789')
:return: 具体的locator
'''
type = ele_info.get('type')
value = ele_info.get('value')
if type == 'id':
locator = (By.ID, value)
elif type == 'name':
locator = (By.NAME, value)
elif type == 'classname':
locator = (By.CLASS_NAME, value)
elif type == 'tagname':
locator = (By.TAG_NAME, value)
elif type == 'css':
locator = (By.CSS_SELECTOR, value)
elif type == 'xpath':
locator = (By.XPATH, value)
elif type == 'linktext':
locator = (By.LINK_TEXT, value)
elif type == 'plinktext':
locator = (By.PARTIAL_LINK_TEXT, value)
else:
self.logger.error(('不支持的浏览器类型:{}'.format(type)))
raise Exception('不支持的浏览器类型:{}'.format(type))
self.logger.info(f'元素的定位策略是{locator}')
return locator
鼠标悬停
什么地方会用到鼠标悬停的动作呢? 比如选择省市区这类的下拉框或者选择框的的时候就会涉及到鼠标悬停的动作了。
def move_to_element(self,ele_info):
'''
鼠标悬浮
:return:
'''
try:
el=self.find_element(ele_info)
action=ActionChains(self.driver)
action.move_to_element(el).perform()
self.logger.info(f'鼠标悬浮元素{ele_info}成功')
except Exception as e:
self.logger.error(f'鼠标悬浮元素{ele_info}失败,报错信息为{e}')
raise Exception(f'鼠标悬浮元素{ele_info}失败,报错信息为{e}')
截图
这里主要是截取测试用例失败时的截图,也就是测试用例在哪里失败的就会在哪里截图
def get_screenshot_as_png(self):
try:
png = self.driver.get_screenshot_as_png()
self.logger.info('截图成功')
return png
except Exception as e:
self.logger.error(f'截图失败,报错信息为{e}')
raise Exception(f'截图失败,报错信息为{e}')
def get_screenshot_as_file(self,filepath):
try:
self.driver.get_screenshot_as_file(filepath)
self.logger.info('截图并保存成功')
except Exception as e:
self.logger.error(f'截图并保存失败,报错信息为{e}')
raise Exception(f'截图失败,报错信息为{e}')
切换窗口
def switch_window(self):
try:
handles = self.driver.window_handles
wait = WebDriverWait(self.driver,10)
wait.until(window_be_switch_success(handles[-1]))
self.logger.info('切换窗口成功')
except Exception as e:
self.logger.error(f'切换窗口失败,报错信息为{e}')
raise Exception(f'切换窗口失败,报错信息为{e}')
获取文本内容
获取文本内容主要适用于断言使用,判断获取的文本内容是否在页面源码中存在
def get_text(self,ele_info):
el = self.find_element(ele_info)
try:
text = el.text
self.logger.info(f'获取元素文本内容成功,内容是{text},类型是{type(text)}')
return text
except Exception as e:
self.logger.error(f'获取元素文本内容失败,报错信息为{e}')
raise Exception(f'获取元素文本内容失败,报错信息为{e}')
显示等待封装无法点击not clickable
class element_click_is_success:
def __init__(self,locator):
self.locator = locator
def __call__(self,driver):
try:
driver.find_element(*self.locator).click()
return True
except Exception:
return False
class window_be_switch_success:
def __init__(self,handle):
self.handle = handle
def __call__(self,driver):
try:
driver.switch_to.window(self.handle)
return True
except Exception:
return False
封装file_load.py
和接口自动化的那个文件一样,可以直接copy过来
def read_yaml(filename):
'''
读取yaml文件
pip install pyyaml
:return:
路径:绝对路径
相对路径:相对的是哪个文件??
相对的是你运行的那个文件
'../data/mtxshop.yml' 相对file_load.py
'./data/mtxshop.yml' 相对run.py
动态生成绝对路径
解决方案:
动态生成绝对路径
1.获取当前项目的绝对路径
2.跟读取的数据进行拼接
'''
with open(DIR_NAME+filename,'r',encoding='utf-8') as f:
content =yaml.load(f,Loader=yaml.FullLoader)
return content
def read_excel(filename,sheet_name):
'''
pip install pandas
pip install openpyxl
pip install xlrd
:param filename: 你要读取的文件的名字
:return:
'''
pd=pandas.read_excel(DIR_NAME+filename,sheet_name=sheet_name,
keep_default_na=False,
engine='openpyxl')
lines_count = pd.shape[0]
col_count = pd.columns.size
data=[]
for row in range(lines_count):
line = []
for col in range(col_count):
text=pd.iloc[row,col]
if col == 1:
text=json.loads(text)
line.append(text)
data.append(line)
return data
if __name__ == '__main__':
print(read_yaml('/pagefiles/buyer.yml'))
封装logger.py
和接口自动化的那个文件一样,可以直接copy过来
import logging.handlers
from setting import DIR_NAME
class GetLogger:
'''
当已经创建了logger对象的时候,那么之后就不在创建了,也就是只创建一次对象
'''
logger = None
@classmethod
def get_logger(cls):
if cls.logger is None:
cls.logger = logging.getLogger('apiautotest')
cls.logger.setLevel(logging.DEBUG)
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s (%(funcName)s:%(lineno)d] - %(message)s"
fm = logging.Formatter(fmt)
tf = logging.handlers.TimedRotatingFileHandler(filename=DIR_NAME +'/logs/requests.log',
when='H',
interval=1,
backupCount=3,
encoding='utf-8'
)
logging.basicConfig(level=logging.DEBUG,format=fmt)
tf.setFormatter(fm)
cls.logger.addHandler(tf)
return cls.logger
if __name__ == '__main__':
logger = GetLogger().get_logger()
print(id(logger))
logger1 = GetLogger().get_logger()
print(id(logger1))
logger.debug('调试')
logger.info('信息')
logger.warning('警告')
name = '123'
logger.error('这个变量是{}'.format(name))
logger.critical('致命的')
待补充,更新中。。。
|