Package: fasttest-selenium
框架介绍fasttest-selenium 是由Python语言开发的WEB端自动化框架，通过yaml编写相应的action即可调用Selenium API完成自动化测试。文档介绍idea能否同时运行多个不同的浏览器，解决兼容性测试问题通过插件快速输入关键字已完成功能selenium 90%的Action支持可拓展的公共方法和公共函数高度还原steps的报告输出待完成功能数据类型优化：支持自定义复杂数据结构结果报告优化：支持图片放大快速体验框架下载fasttest-selenium已经发布到Python PyPi平台，可通过以下两种方式完成安装
1.在终端输入下方命令进行安装：pip3installfasttest-selenium2.可前往代码仓库直接clone或下载源码。下载源码后需要将包导入到当前项目中项目依赖该框架在Python3.7版本下开发，部分功能依赖第三方模块，故需要安装以下模块PyYAML用于解析yaml测试用例文件pip3installPyYAMLseleniumWEB自动化框架pip3installseleniumcolorama日志高亮输出pip3installcolorama4.opencv-contrib-python图片对比识别pip3installopencv-contrib-python==3.4.2.16说明：opencv在3.4.2以上版本使用其算法需要授权确认，该版本最高支持Python3.7，如不需要图片对比能力，可安装最新版Python和opencv-contrib-python测试DemoDemo包含完整项目结构以及关键字测试用例，下载后可通过运行runtest.py启动测试执行项目结构项目树状图├── Common           // 自定义关键字库  
|   ├── common.yaml  // 自定义关键字文件
├── Resource         // 文件资源目录
|   ├── test.png     // 图片资源
├── Report           // 结果报告目录  
├── Scripts          // 脚本函数目录 
|   ├── __init__.py  // 包标识文件
|   ├── test.py      // 脚本文件
├── TestCase         // 测试用例目录
|   ├── test.yaml    // 测试用例文件
├── config.yaml      // 项目配置文件  
├── data.json        // 数据配置文件  
├── runtest.py       // 项目执行文件config 项目配置config.yaml包含了项目运行时必要参数，结构如下：driver固定参数，值为seleniumbrowser指定浏览器，包括chrome、firefox、safari、ie、operaisReset每条用例执行完后是否重置浏览器，推荐设置为TruesaveScreenshot执行步骤是否保存截图，如果为True将大大增加执行时间，推荐设置为False，只在执行异常时保存截图implicitlyWait隐式等待时间maxWindow窗口最大化desiredcaps启动参数配置，会根据browser拿到对应浏览器参数driver浏览器驱动，如驱动未添加环境变量中，可通过此参数指定驱动路径options浏览器启动参数，会自动添加到对应浏览器的optionstestcast用例执行路径，可指定某一条用例路径或者某一文件夹下面所有用例driver:seleniumbrowser:chromeisReset:TruesaveScreenshot:FalseimplicitlyWait:10maxWindow:Truedesiredcaps:-chrome:-driver:'/Users/xiongjunjie/Desktop/chromedriver'options:# 无头模式运行-'--headless'-'--dissble-gpu'-'--window-size=1920,1050'firefox:-driver:'/Users/xiongjunjie/Desktop/geckodriver'options:-'--headless'-'--dissble-gpu'-'--window-size=1920,1050'testcase:-TestCase/common/-TestCase/selenium/cookies.yamldata 数据配置data.json为数据配置，结构如下：variable全局变量配置，用例中可直接获取该配置下的数据resource资源路径配置，用例中可直接访问该相对路径下的文件资源keywords脚本函数配置，用例中可直接调用该配置下的函数{"variable":{"userid":"admin","password":13456},"resource":{"baidu_logo":"Resource/baidu.png"},"keywords":["keyDown","getText"]}例：直接调用keywords函数并传递userid的值和baidu_logo的路径-keyDown(${userid},${baidu_logo})runtest 执行入口runtest.py为整个项目执行入口，可直接执行该文件或在终端运行python3 runtest.py启动项目start方法有返回整体执行结果，方便接入持续集成或者生成自定义报告#!/usr/bin/env python3# -*- coding: utf-8 -*-fromfasttest_seleniumimport*if__name__=='__main__':project=Project()project.start()Common 自定义关键字Common目录用于存放自定义的关键字，类似于PO模式中的Page，可以将用例中公共逻辑抽离出来保存到这里，它需要一个关键字来表达它的作用，所以我们需要为它指定一个唯一的名字，以便于我们在用例中可调用。关键字结构：common_action关键字名称，性感且唯一description关键字描述，简短有力input传入参数，一个不漏output返回参数，暂未实现steps执行步骤关键字定义：common_action:description:公共方法input:[key,value]output:[]steps:-click(${value})-sleep(5)common_action1:description:公共方法1input:[key,value]output:[]steps:-click(${value})-sleep(5)调用方式：在用例中call + 关键字名称为调用该关键字的语法，参数个数应和input相对应-call common_action('key','测试数据')Scripts 脚本函数Scripts为Python脚本目录，在 [data.json]下keywords中注册方法名后，就可直接在用例中调用脚本
如现有关键字无法完成的特殊操作，可以导入框架中driver模块，通过driver调用底层API函数定义：#!/usr/bin/env python3# -*- coding: utf-8 -*-fromfasttest_selenium.driverimport*defgetText():'''调用selenium api获取当前页面标题'''log_info(driver.title)returndriver.titledefgetTest(test):'''普通函数'''log_info(test)调用方式：如果需要拿到函数返回的数据，可以直接用变量保存，如不需要，可以直接调用函数-${title} = getText()# 获取返回值-getTest('测试数据')# 普通函数Resource 文件资源Resource目录下的文件资源在data.json下resource中指定文件名称和相对路径后，可以直接在用例中访问该资源文件-matchImage(${baesImage}, ${baidu_logo})TestCase 测试用例TestCase用于存放测试用例，测试用例为YAML文件，如果对YAML语法不熟悉可参考菜鸟教程，用例结构如下：module用例所属业务模块，同一业务下用例的module应保持一致，在结果报告中它们将展示在同一个分组下面skip是否跳过该用例，默认为falsedescription用例描述steps用例步骤module:seleniumskip:falsedescription:action相关steps:-openUrl('https://www.baidu.com')# 检查元素-check('id=su')# 单击元素-click('id=su')# 右击元素-contextClick('id=su')# 双击元素-doubleClick('class=hot-refresh-text')# 按住鼠标左键-holdClick('class=hot-refresh-text')Report 结果报告Report为测试结果目录，用例执行完毕后会自动生成高度还原测试用例步骤的测试报告，可点击具体步骤
查看截图和错误信息关键字测试用例中的关键字包含4中场景，项目结构中已经介绍了自定义关键字Common和Scripts，接下来两种是固定关键字讲在前面在介绍关键字之前，先介绍下用例中经常用到的两个点关于变量的定义，占位符${} + 变量名test来表示这是一个变量，获取该变量同样如此关于赋值：它可以直接被赋值或者其他关键字返回值给变量关于取值：一条用例中${test}会从多个地方尝试获取值，所以具有一定优先级最高优先级：如果在for循环中，会优先获取for循环迭代值第二优先级：如果for循环中无法获取该变量，会尝试从当前用例中获取变量值第三优先级：如果当前用例中未定义该变量，会判断是否在Common库中input有定义第四优先级：如果以上三种均未获取该变量，会尝试从data.json下variable、resource获取变量值元素定位方法：selenium有八种元素定位方式，为了简化步骤描述，用例中按照type=element的方式即可定位到相关元素元素定位方式：id、name、class、tag_name、link_text、partial_link_text、xpath、css_selector例：点击百度按钮 click("id=su")Selenium ActionBrowser打开网页(openUrl)openUrl打开一个网页，参数:URL-${url} = 'https://www.baidu.com'-openUrl(${url})# 或者-openUrl('https://www.baidu.com')对应Selenium API ：driver=webdriver.Chrome()driver.get(url)关闭标签页或窗口(close)close关闭当前标签页或窗口-openUrl('https://www.baidu.com')-close()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.close()后退(back)back退回到上一页-back()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.find_element_by_xpath('//*[@id="s-top-left"]/a[1]').click()driver.back()前进(forward)forward前进到下一页-forward()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.find_element_by_xpath('//*[@id="s-top-left"]/a[1]').click()driver.back()driver.forward()刷新(refresh)refresh刷新当前页面-refresh()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.refresh()窗口最大化(maxWindow)maxWindow窗口最大化-maxWindow()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.maximize_window()窗口最小化(minWindow)minWindow窗口最小化-minWindow()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.minimize_window()窗口全屏(fullscreenWindow)fullscreenWindow窗口全屏-fullscreenWindow()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.fullscreen_window()设置窗口大小(setWindowSize)setWindowSize设置窗口大小，参数：width，height-setWindowSize(3000,2000)对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.set_window_size(3000,2000)设置窗口坐标(setWindowPosition)setWindowPosition设置窗口起始坐标，参数：x，y-setWindowPosition(10,20)对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.set_window_position(10,20)获取窗口大小($.getWindowSize)$.getWindowSize获取窗口大小-${w_size} = $.getWindowSize()对应Selenium API：driver=webdriver.Chrome()driver.get(url)w_size=driver.get_window_size()获取窗口坐标($.getWindowPosition)$.getWindowPosition获取窗口起始坐标-${w_position} = $.getWindowPosition()对应Selenium API：driver=webdriver.Chrome()driver.get(url)w_position=driver.get_window_position()获取当前窗口句柄($.getCurrentWindowHandle)$.getCurrentWindowHandle获取当前窗口句柄-${handle} = $.getCurrentWindowHandle()对应Selenium API：driver=webdriver.Chrome()driver.get(url)handle=driver.current_window_handle获取所有窗口句柄($.getWindowHandles)$.getWindowHandles获取所有窗口句柄-${handles} = $.getWindowHandles()对应Selenium API：driver=webdriver.Chrome()driver.get(url)handles=driver.window_handles切换窗口句柄(switchToWindow)switchToWindow切换窗口句柄，参数:handle获取所有的窗口句柄是无序的，建议用for循环切换窗口句柄-${all_handle} = $.getWindowHandles()-for ${handle} in ${all_handle}:# 切换窗口句柄-switchToWindow(${handle})-${title} = $.getTitle()-if ${title} == '百度新闻——海量中文资讯平台':-break对应Selenium API：driver=webdriver.Chrome()driver.get(url)handles=driver.window_handlesforhandleinhandles:# 切换窗口句柄switch_to.window(handle)title=driver.titleiftitle=='百度新闻——海量中文资讯平台':break获取窗口标题($.getTitle)$.getTitle获取窗口标题-${title} = $.getTitle()对应Selenium API：driver=webdriver.Chrome()driver.get(url)title=driver.title获取窗口URL($.getCurrentUrl)$.getCurrentUrl获取窗口URL-${title} = $.getCurrentUrl()对应Selenium API：driver=webdriver.Chrome()driver.get(url)title=driver.current_url获取浏览器名称($.getName)$.getName获取浏览器名称-${name} = $.getName()对应Selenium API：driver=webdriver.Chrome()driver.get(url)title=driver.nameAction单击(click)click鼠标左键单击，参数:element-click('id=su')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_id('su')element.click()右击(contextClick)contextClick鼠标右键单击，参数:element-contextClick('id=su')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_id('su')ActionChains(driver).context_click(element).perform()双击(doubleClick)doubleClick鼠标左键双击，参数:element-doubleClick('id=su')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_id('su')ActionChains(driver).double_click(element).perform()长按(holdClick)holdClick长按鼠标左键不放，参数:element-holdClick('id=su')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_id('su')ActionChains(driver).click_and_hold(element).perform()拖放元素到另一个元素位置(dragDrop)dragDrop拖放元素到另一个元素位置，参数:element，target-dragDrop('xpath=//*[@id="sbox"]/tbody/tr/td[1]/div[1]/a/img','id=s_btn_wr')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_xpath('//*[@id="sbox"]/tbody/tr/td[1]/div[1]/a/img')target=find_element_by_id('s_btn_wr')ActionChains(driver).drag_and_drop(element,target).perform()拖放元素到某个位置(dragDropByOffset)dragDropByOffset拖放元素到某个位置，参数:element，x，y-dragDropByOffset('xpath=//*[@id="sbox"]/tbody/tr/td[1]/div[1]/a/img',10,10)对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_xpath('//*[@id="sbox"]/tbody/tr/td[1]/div[1]/a/img')ActionChains(driver).drag_and_drop_by_offset(element,10,10).perform()鼠标移动到某个位置(moveByOffset)moveByOffset鼠标从当前位置移动到某个坐标，参数：x，y-moveByOffset(10,10)对应Selenium API：driver=webdriver.Chrome()driver.get(url)ActionChains(driver).move_by_offset(10,10).perform()鼠标移动到某个元素(moveToElement)moveToElement鼠标移动到某个元素上，参数:element-moveToElement('xpath=//*[@id="hotsearch-content-wrapper"]/li[1]/a/span[2]')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_xpath('//*[@id="hotsearch-content-wrapper"]/li[1]/a/span[2]')ActionChains(driver).move_to_element(element).perform()鼠标_移动到距某个元素多少距离的位置_(moveToElementWithOffset)moveToElementWithOffset鼠标_移动到距某个元素(左上角坐标)多少距离的位置_，参数:element，x，y-moveToElementWithOffset('id=su',10,20)对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_id('su')ActionChains(driver).move_to_element_with_offset(element,10,20).perform()输入(sendKeys)sendKeys输入文案，参数：element，Keys(可选)，text-sendKeys('name=wd','测试文案')# 普通输入# 复制粘贴-sendKeys('name=wd','Keys.CONTROL', 'c')# control + c-sendKeys('name=wd','Keys.CONTROL', 'v')# control + v对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_name('wd')element.send_keys('测试文案')# 复制粘贴element.send_keys(Keys.CONTROL,'c')element.send_keys(Keys.CONTROL,'v')清除(clear)clear清除输入框文案、元素选中状态-clear('name=wd')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_name('wd')element.clear()执行JS(executeScript)executeScript执行JS代码，参数:script-${js} = "window.open('http://www.taobao.com')"-executeScript(${js})对应Selenium API：driver=webdriver.Chrome()driver.get(url)js="window.open('http://www.taobao.com')"driver.execute_script(js)截图($.saveScreenshot)$.saveScreenshot对屏幕或者元素截图，参数:element(可选，如果为空则截全屏)，name# 对元素截图，返回图片路径-${path} = $.saveScreenshot('id=su', 'biadu.png')# 全屏截图，返回图片路径-${path} = $.saveScreenshot('biadu.png')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element=find_element_by_id('su')# 元素截图element.screenshot(path)# 全屏截图driver.save_screenshot(path)Element获取单个元素($.getElement)$.getElement获取单个元素，参数:element# 获取单个元素 id|name|class|tag_name|link_text|partial_link_text|xpath|css_selector-${element_id} = $.getElement('id=su')-${element_name} = $.getElement('name=f')-${element_class} = $.getElement('class=s_ipt')-${element_tag_name} = $.getElement('tag_name=form')-${element_link_text} = $.getElement('link_text=新闻')-${element_partial_link_text} = $.getElement('partial_link_text=新')-${element_xpath} = $.getElement('xpath=//*[@id="su"]')-${element_css_selector} = $.getElement('css_selector=[name="wd"]')对应Selenium API：driver=webdriver.Chrome()driver.get(url)'''by = Dict({'id': By.ID,'name': By.NAME,'xpath': By.XPATH,'class': By.CLASS_NAME,'tag_name': By.TAG_NAME,'link_text': By.LINK_TEXT,'css_selector': By.CSS_SELECTOR,'partial_link_text': By.PARTIAL_LINK_TEXT,})'''element_id=find_element(By.ID,'su')获取多个元素($.getElement)$.getElement获取单个元素，参数:element# 获取多个元素 id|name|class|tag_name|link_text|partial_link_text|xpath|css_selector-${elements_class} = $.getElements('class=title-content-title')对应Selenium API：driver=webdriver.Chrome()driver.get(url)elements_class=find_elements(By.CLASS_NAME,'title-content-title')判断元素是否选中($.isSelected)$.isSelected判断元素是否选中，参数:element-${element_id} = $.getElement('id=su')-${is_selected} = $.isSelected(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')is_selected=element_id.is_selected()判断元素是否显示($.isDisplayed)$.isDisplayed判断元素是否显示，参数:element-${element_id} = $.getElement('id=su')-${is_displayed} = $.isDisplayed(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')is_displayed=.is_displayed()判断元素是否可用($.isEnabled)$.isEnabled判断元素是否可用，参数:element-${element_id} = $.getElement('id=su')-${is_enabled} = $.isEnabled(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')is_enabled=element_id.is_enabled()获取元素大小($.getSize)$.getSize判断元素是否选中，参数:element-${element_id} = $.getElement('id=su')-${size} = $.getSize(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')size=element_id.size获取元素坐标($.getLocation)$.getLocation获取元素坐标，参数:element-${element_id} = $.getElement('id=su')-${location} = $.getLocation(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')location=element_id.location获取元素位置大小($.getRect)$.getRect获取元素位置大小，参数:element-${element_id} = $.getElement('id=su')-${rect} = $.getRect(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')rect=element_id.rect获取元素标签($.getTagName)$.getTagName获取元素标签，参数:element-${element_id} = $.getElement('id=su')-${tag} = $.getTagName(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')tag=element_id.tag_name获取元素文案($.getText)$.getText获取元素位置文案，参数:element-${element_id} = $.getElement('id=su')-${text} = $.getText(${element_id})对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')text=element_id.text获取元素属性($.getAttribute)$.getAttribute获取元素位置属性，参数:element，attribute-${element_id} = $.getElement('id=su')-${attribute} = $.getAttribute(${element_id}, 'value')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')attribute=element.get_attribute('value')获取元素CSS($.getCssProperty)$.getCssProperty获取元素CSS，参数:element，css-${element_id} = $.getElement('id=su')-${css} = $.getCssProperty(${element_id}, 'height')对应Selenium API：driver=webdriver.Chrome()driver.get(url)element_id=find_element(By.ID,'su')css=element_id.value_of_css_property('height')Cookie获取所有Cookie($.getCookies)$.getCookies获取所有Cookie-${cookies} = $.getCookies()对应Selenium API：driver=webdriver.Chrome()driver.get(url)cookies=driver.get_cookies()获取指定Cookie($.getCookie)$.getCookie获取指定Cookie，参数:name-${cookie} = $.getCookie('BAIDUID')对应Selenium API：driver=webdriver.Chrome()driver.get(url)cookie=driver.get_cookie('BAIDUID')删除指定Cookie(deleteCookie)deleteCookie删除指定Cookie，参数:name-deleteCookie('BAIDUID')对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.delete_cookie('BAIDUID')删除所有Cookie(deleteAllCookies)deleteAllCookies删除所有Cookie-deleteAllCookies()对应Selenium API：driver=webdriver.Chrome()driver.get(url)driver.delete_all_cookies()添加指定Cookie(addCookie)addCookie添加指定Cookie，参数:cookie-${add_cookie} = {'name':'ADDCOOKIE','value':'123adc'}-addCookie(${add_cookie})对应Selenium API：driver=webdriver.Chrome()driver.get(url)add_cookie={'name':'ADDCOOKIE','value':'123adc'}driver.add_cookie(add_cookie)Other Action变量(${variables})关于变量已经在开头介绍过，这里演示变量几种写法# 直接赋值-${t1} = 1-${t2} = '2'-${t3} = "3"-${t4} = True-${t5} = False-${t6} = None-${t7} = [1,2,3]-${t8} = {'key':'value'}# 调用方法返回-${elements} = $.getElements('class=title-content-title')如果变量值为列表或字典，可以指定index或者key来获取其中某一个元素的值-keyDown(${7}[0], ${t8}['key'])设置全局变量(setVar)**setVar设置全局变量，该变量在整个测试执行结束后才会销毁，参数：key，value-setVar('key','测试')获取全局变量($.getVar)**$.getVar获取全局变量，参数：key-${test} = $.getVar('key')获取长度($.getLen)$.getLen可以获取其他变量的长度，这对于循环是有帮助的，参数：variables-${t7} = [1,2,3]-${len} = $.getLen(${t7})睡眠(sleep)sleep在当前步骤等待指定时间，参数：s-sleep(5)# 等待5s断言(assert)assert用例中可以直接断言来检查执行是否符合预期效果，参数：条件表达式-assert 5 > 4-${text} = '测试文案'-assert '文案' == ${text}-assert '文案' in '测试文案'执行表达式($.id)$.id用于执行一个表达式，参数：表达式-${index} = $.id(1+1)# 2-${str} = $.id('测试'+'文案')# 测试文案条件语句(if、elif、else)if、elif、else根据条件表达式来决定执行代码，参数：条件表达式-${index} = $.id(1+1)# 2-if ${index} == 2:-${test} = 1elif ${index} > 2:-${test} = 2else:-${test} = 3循环语句(while)while用于循环执行程序， 参数：条件表达式break在某一条件成立时，可以跳出循环-${list} = [1,2,3,4]-${len} = $.getLen(${list})-while ${len}:-${len} = $.id(${len}-1)-if ${list}[${len}] == 2:-break循环语句(for)for循环可以遍历任何序列的项目，如一个列表或者一个字符串， 参数：可迭代对象break在某一条件成立时，可以跳出循环# 打开URL-openUrl('https://www.baidu.com')# 打开一个新窗口-click('xpath=//*[@id="s-top-left"]/a[1]')# 获取所有窗口句柄-${all_handle} = $.getWindowHandles()# for 循环-for ${handle} in ${all_handle}:# 切换窗口句柄-switchToWindow(${handle})-${title} = $.getTitle()-if ${title} == '百度新闻——海量中文资讯平台':-breakEnd