【WebUIAuto】1.web端UI自动化

环境安装步骤

1.准备好python环境
2.准备selenium环境
3.下载浏览器对应版本的driver,点击下载chromedriver
4.driver配置环境变量
5.在python里导入对应依赖

selenium IDE 录制

seleniumIDE官网

点击进入扩展程序

selenium 用例编写

python-selenium文档
selenium常用等待

  • 直接等待
    time.sleep(3) 等待3秒
  • 隐式等待
    driver.implicitly_wait(5) 轮训查找(默认0.5)元素是否出现,如果没出现就抛出异常
  • 显示等待
from selenium import webdriver
from selenium.webdriver.common.by import By
class TestWebUi:
    def setup(self):
        self.driver = webdriver.Chrome()
        self.driver.get("http://www.baidu.com")
        # 隐式等待
        self.driver.implicitly_wait(5)
    def teardown(self):
        self.driver.close()

    def test_search(self):

        self.driver.find_element(By.ID,'kw').send_keys('腾讯课堂')
        self.driver.find_element(By.ID, 'su').click()
        self.driver.find_element(By.LINK_TEXT,'腾讯课堂【官方】_腾讯课堂【官方】腾讯课堂官网').click()
          #显示等待
        WebDriverWait(self.driver,10).until(expected_conditions.presence_of_all_elements_located((By.LINK_TEXT,'官方')))
        
web控件定位,操作
  • 操作
    输入: self.driver.find_element(By.ID,'kw').send_keys('腾讯课堂')
    点击: self.driver.find_element(By.ID, 'su').click()
  • 定位-xpath
    作用域(appium、selenim)
表达式 结果
/div/input[1] 取div子元素的第一个input元素
/div/input[last()] 取div子元素的最后个input元素
/div/input[last()-1] 取div子元素的倒数第二个input元素
/div/input[position()<3] 取div子元素的前两个book元素
//div[@class='classname'] 得到所有div元素,且这些元素拥有值为classaname的class属性
/div/input[price>35] 得到所有div元素下的所有input,且其中的price元素值大于35,且这些元素拥有值为classaname的class属性
nodename 选取此节点的所有子节点
/ 从根节点选取
// 匹配选择当前节点,不考虑其位置
. 选取当前节点
.. 选取当前节点父节点
@ 选取属性
  • 定位-css selector
    appium 原生元素不能用 css selector定位
表达式 结果 例子描述
.class .intro 选择 class = "intro"的所有元素
.id .idname 选择 id= "idname"的所有元素
* * 选择所有元素
element div 选择所有<div>元素
element,element div,p 选择所有<div>元素和所有<p>元素
element element div p 选择<div>元素内部的所有<p>元素
element>element div>p 选择父元素为<div>元素的所有<p>元素
element+element div+p 选择紧接在<div>元素之后的所有<p>元素
css select f方式
web控件交互

Actions

  • 官方文档
  • ActionChains:执行鼠标点击、双击、右键、拖拽事件
  • TouchActions: 模拟PC移动端、点击、滑动、拖拽、多点触控操作
  • 执行原理,调用actionChains方法,不会立即执行,而是讲所有的操作,按顺序放在一个队列里,调用perform()方法时,队列中的事件会依次执行
  • 基本用法
  1. 生成一个动作 action = ActionChains(driver)
  2. 动作添加方法1 action.方法1
  3. 调用perform()方法执行(action.perform())
  • 写法
  1. 链式写法 ActionChains(driver).move_to_element(element).click(element).perform()
  2. 分布写法
    action= ActionChains(driver)
    action.move_to_element(element)
    actions.click(element)
    actions.perform()

ActionChains示例

class TestActionChains():
    def setup(self):
        self.driver = webdriver.Chrome()
        self.driver.get("http://sahitest.com/demo/clicks.htm")
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.close()

    def test_case_click(self):
        element_click = self.driver.find_element(By.XPATH,'//input[@value="click me"]')
        element_dbclick = self.driver.find_element(By.XPATH,'//input[@value="dbl click me"]')
        element_rclick = self.driver.find_element(By.XPATH,'//input[@value="right click me"]')
        action = ActionChains(self.driver)
        action.click(element_click)
        action.double_click(element_dbclick)# 双击操作
        action.context_click(element_rclick)# 右击操作
        action.perform()
        sleep(3)
    def test_move_to(self):
        # 光标移动到某个元素
        self.driver.get("http://www.baidu.com")
        element = self.driver.find_element(By.ID,'s-usersetting-top')
        action = ActionChains(self.driver)
        action.move_to_element(element)
        action.perform()
        sleep(2)
    def test_dragdrog(self):
        self.driver.get("http://sahitest.com/demo/dragDropMooTools.htm")
        ele1= self.driver.find_element(By.ID,'dragger')
        ele2= self.driver.find_element(By.XPATH,"/html/body/div[2]")
        action = ActionChains(self.driver)
        action.drag_and_drop(ele1,ele2).perform()
        action.click_and_hold(ele1).release(ele2).perform()
        action.click_and_hold(ele1).move_to_element(ele2).perform()
        sleep(3)
    def test_keys(self):
        # 模拟键盘操作
        self.driver.get("http://sahitest.com/demo/label.htm")
        ele = self.driver.find_element(By.XPATH,"/html/body/label[1]/input")
        ele.click()
        action = ActionChains(self.driver)
        action.send_keys("username").pause(1)# 添加一秒等待
        action.send_keys(Keys.SPACE).pause(1)
        action.send_keys("张三").pause(1)
        action.send_keys(Keys.BACK_SPACE).perform()
        sleep(3)

TouchAction
官方文档
TouchAction 示例滑动页面

class TestTouchAction():
    def setup(self):
        option = webdriver.ChromeOptions()
        option.add_experimental_option('w3c', False)
        self.driver = webdriver.Chrome(options=option)
        self.driver.get("http://baidu.com")
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.close()

    def test_scrollbottom(self):
        ele_se = self.driver.find_element(By.ID, 'kw')
        ele_se.send_keys('selenium测试')
        ele_c =  self.driver.find_element(By.ID, 'su')

        action = TouchActions(self.driver)
        action.tap(ele_c)
        action.perform()
        # 滑动页面
        action.scroll_from_element(ele_se, 0, 1200).perform()
        sleep(3)
表单
  • 表单定义
    1. 表单是一个包含表单元素的区域
    2. 表单元素是允许用户在表单中输入信息的元素
    3. 表单使用标签<form>定义
    4. 操作表单元素步骤
      a. 首先要定位到表单元素
      b. 然后操作元素(点击、输入、清空)
class TestFrom:
    def setup(self):
        self.driver = webdriver.Chrome()
        self.driver.get("http://testerhome.com/account/sign_in")
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.close()

    def test_form(self):
        self.driver.find_element(By.ID, 'user_login').send_keys('abcdee')
        self.driver.find_element(By.ID, 'user_password').send_keys('12312312')
        self.driver.find_element(By.XPATH, '//*[text()[contains(.,"记住")]]').click()
        self.driver.find_element(By.XPATH, '//*[@id="new_user"]/div[4]/input').click()
        sleep(3)

多窗口,frame处理

  • selenium 处理多窗口
  1. 多个窗口识别
    a. 获取当前窗口句柄:driver。current_window_handle
    b. 在获取所有窗口句柄 driver。window_handles
    c. 判断是否想要操作的窗口,如果是就可以对窗口进行操作,如果不是跳转到另外一个窗口,对另一个窗口操作 driver.switch_to_window

  2. 多个窗口切换

from time import sleep

from selenium.webdriver.common.by import By

from web_ui.base import Base
class TestWindows(Base):
    def test_windows(self):
        self.driver.get("http://www.baidu.com")
        self.driver.find_element(By.LINK_TEXT, '登录').click()

        self.driver.find_element(By.XPATH, '//*[text()[contains(.,"立即注册")]]').click()
        # 获取当前所有窗口
        windows = self.driver.window_handles
        print(windows)
        # 切换到最最新打开的窗口
        self.driver.switch_to_window(windows[-1])
        self.driver.find_element(By.ID,'TANGRAM__PSP_4__userName').send_keys('username')
        self.driver.find_element(By.ID,'TANGRAM__PSP_4__phone').send_keys('13200001234')
        sleep(2)
        #切换到 最开始的窗口
        self.driver.switch_to_window(windows[0])

        self.driver.find_element(By.ID,'TANGRAM__PSP_11__footerULoginBtn').click()
        self.driver.find_element(By.ID, 'TANGRAM__PSP_11__userName').send_keys('abcdef')
        sleep(2)

  • frame 分类,frameset、frame、iframe
    frameset 不会影响正常定位,可以用id、name 定位
    frame和iframe 需要特殊操作

  • selenium处理 frame

  1. 多个frame识别
    有两种,嵌套的、非嵌套
  2. 多个frame之前切换
    driver.switch_to.frame() # 根据ID或者index切换frame
    driver.switch_to_default_content() # 切换到默认frame
    driver.switch_to.parent_frame() # 切换到父级frame
    未嵌套iframe
    driver.switch_to_frame('frame id')
    driver.switch_to_frame('frame-index')无id用索引处理,从0开始
   def test_frame(self):
        self.driver.get('http://runoob.com/try/try.php?filename=jqueryui-api-droppable')
        # 根据 id切换frame
        self.driver.switch_to_frame('iframeResult')
        print(self.driver.find_element(By.ID, 'draggable').text)
        #切换到 父级frame
        self.driver.switch_to.parent_frame()
        # 切换到默认frame
        self.driver.switch_to.default_content()
        print(self.driver.find_element(By.ID, 'submitBTN').text)
多浏览器处理
from  selenium import  webdriver
import os
class Base():
    def setup(self):
        browser = os.getenv('browser')
        if browser == 'firefox':
            self.driver = webdriver.firefox()
        elif browser == 'headless':
            self.driver = webdriver.PhantomJS()
        else:
            self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(5)
        self.driver.maximize_window()
    def teardown(self):
        self.driver.quit()
执行java script
    def test_javascript(self):
        # 通过js滑动到页面底部
        self.driver.get("http://www.baidu.com")
        self.driver.find_element(By.ID,'kw').send_keys('selenium测试')
        # 通过 js 获得元素
        ele = self.driver.execute_script("return document.getElementById('su')")
        ele.click()
        self.driver.execute_script("document.documentElement.scrollTop=100")
        sleep(2)
        # sleep(1)
        print(self.driver.execute_script('return document.title;return JSON.stringify(performance.timing)'))
        for code in [
            'return document.title','return JSON.stringify(performance.timing)'
        ]:
            print(self.driver.execute_script(code))
  • 修改日期
    def test_wirte_time(self):
        self.driver.get('http://12306.cn/index')
        # 执行js让输入框可编辑
        time_ele = self.driver.execute_script('a=document.getElementById("train_date");a.removeAttribute("readonly")')
        sleep(3)
        # 写入值
        self.driver.execute_script('a=document.getElementById("train_date");a.value = ("2021-08-09")')
        #  清楚元素的,readonly属性
        self.driver.execute_script("arguments[0].removeAttribute('readonly')",findElement(self.driver,'xm_cpsj'))
        self.driver.execute_script(f"$arguments[0].attr('value','{cpsj}');",findElement(self.driver,'xm_cpsj'))

        sleep(2)
        # 打印元素  value
        print(self.driver.execute_script("return document.getElementById('train_date').value"))
  • 文件上传弹窗处理
    # 上传图片
    def test_upload(self):
        self.driver.get("http://www.baidu.com")
        self.driver.find_element(By.CLASS_NAME, 'soutu-btn').click()
        sleep(2)
        self.driver.find_element(By.CLASS_NAME, 'upload-pic').send_keys('x.jpg')
        sleep(3)
    def test_alter(self):
        self.driver.get('http://runoob.com/try/try.php?filename=jqueryui-api-droppable')
        self.driver.switch_to_frame('iframeResult')
        drag = self.driver.find_element(By.ID,'draggable')
        drop = self.driver.find_element(By.ID, 'droppable')
        action = ActionChains(self.driver)
        action.drag_and_drop(drag,drop).perform()
        sleep(2)
        print('点击 alter 确认')
        self.driver.switch_to_alert().accept()
        self.driver.switch_to.default_content()
        self.driver.find_element(By.ID,'submitBTN').click()
        sleep(3)

PageObject设计模式 PageObject原理及六大原则

  • 公共方法代表页面提供的功能
  • 不暴露页面元素信息
  • 不在页面内断言(在业务层做断言)
  • 方法应该返回别的 PageObject或用于断言的数据
  • 不要给整个页面建模
  • 同样的行为不同的结果,应建模成不同的方法
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容

  • 摘要: 之前用Selenium做UI自动化测试从初学到熟练碰到过很多问题,这里就不一一细说了,所以把最基本的操作都...
    Vicky_习惯做唯一阅读 11,386评论 1 23
  • 一、自动化测试所属分类(站在代码可见度角度分类) 1. 黑盒测试(功能测试)2. 灰盒测试(接口测试)3. 白盒测...
    MAVIS_42bf阅读 905评论 0 0
  • 参考链接: web自动化测试教案:http://www.cnblogs.com/zidonghua/p/74300...
    永杰gg阅读 1,056评论 0 0
  • 一、概念 互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous in...
    __65a0阅读 823评论 0 4
  • 完善融宝UI自动化框架 继续总结自动化测试API "29.模拟键盘单个按键/组合按键操作," # import t...
    公子小白123阅读 1,057评论 0 1