UI自动化

 一、概述

1、什么是UI自动化

  UI即用户界面(user interface)的缩写,客户通过UI实现查看、操作等功能。UI自动化测试就是把人为驱动的测试转化为机器执行的一种过程,其重点在于持续集成。

2、UI自动化测试技术

  1)Qtp类似于脚本执行软件、通过对操作者鼠标、键盘的记录,生成测试脚本;之后执行录制好的测试脚本进行断言、生成测试报告。Qtp更加适用于C/S架构的软件。

    链接:Qtp自动测试工具(案例学习) - 思维焦点 - 博客园 (cnblogs.com)

  2)Robot Framework是一个基于关键字驱动的自动化测试框架。通过该框架,测试人员可使用python封装关键字,并在非代码环境下使用关键字构建可被执行的测试用例,适合B/S架构的web应用。

    链接:Robot Framework官方教程(一)入门 - 简书 (jianshu.com)

  3)Watir Watir( web Application Testing in Ruby) 是一个优秀的开源工具,用于开发基于Web 应用的自动化测试程序。它使用Ruby脚本语言,提供了轻量级的自动化测试程序框架和丰富的开发库。

    链接:开源Web自动化测试框架——Watir试用手记_Watir_领测软件测试网 (ltesting.net)

  4)Selenium是最广泛使用的开源Web UI(用户界面)自动化测试套件之一。它最初由杰森·哈金斯(Jason Huggins)于2004年开发。Selenium支持跨不同浏览器,平台和编程语言的自动化。Selenium通过使用特定于每种语言的驱动程序支持各种编程语言。Selenium支持的语言包括C#,Java,Perl,PHP,Python和Ruby。目前,Selenium Web驱动程序最受Python和C#欢迎。

  5)Appium主要应用于移动端测试,测试代码通过appium,实现对于移动端的操作,从而带到测试的目的。

  6)Win UI通过控制windows电脑,实现测试 

二、Selenium

1、Selenium实现流程

  1)元素定位→2)通过python编写测试脚本→3)python调用webDriver实现脚本执行→4)webDriver返回测试结果→5)python进行断言→6)python输出测试报告

  笔者主要从三个部分来对Selenium自动化测试进行描述

2、元素定位

  1)Xpath定位

  1、路径
    绝对路径 /html/body/div[2]/div[1]/div/div[4]/div/a[1]/img
    相对路径 //*[@id="titleUserIcon"]
  2、元素筛选
    "*", 通配符,表示任意的标签,任意的字符
    "[]", 下标法是从1开始,当标签在同一个父级的情况才能使用
    "@", 表示后边跟的是标签属性
    "and/or", 连接多条筛选语句
    "contains()", 包含 //div[contains(@title,"搜索")]
  3、xpath轴
    //div[@id="userinfo"]/a[1]/following-sibling::a 找弟弟 
    //div[@id="userinfo"]/a[1]/preceding-sibling::a 找哥哥 

   2)CSS 选择器

  参考链接:CSS 选择器 - CSS(层叠样式表) | MDN (mozilla.org)

1、基本符号代表的意思
  ".", 表示属性class
  ">", 表示id
  "+", 表示找弟弟,相邻的元素,不能找哥哥
  " ", 表示任意子元素
  "*", 通配符
  "^", 表示什么开头
2、伪类
  div[class="van-slide ggc"]>div:last-child
  div[class="van-slide ggc"]>div:nth-child(3)

 3、webDriver原理

 整个的工作过程简要来说就是:IDE通过webDriver实例化一个Driver,通过实例化的Driver向Browser发送指令,并接收Browser返回的结果,之后将返回的结果再传输到IDE。

   其实早版本的selenium并不是通过webDriver来控制浏览器的,在selenium1.x的时候,selenium通过JS注入来控制浏览器。过程为Selenium RC启动一个Server,将操作Web元素的API调用转化为一段段Javascript,在Selenium内核启动浏览器之后注入这段Javascript。Javascript可以获取并调用DOM的任何元素,自如的进行操作,由此才实现了Selenium的目的:自动化Web操作。这种Javascript注入技术的缺点是速度不理想,而且稳定性大大依赖于Selenium内核对API翻译成的Javascript质量高低。

 

   于是开发了selenium2.x,即通过webDriver实现自动化Web操作。这个版本的selenium是利用浏览器原生的API,封装成一套更加面向对象的Selenium WebDriver API。直接操作浏览器页面里的元素,甚至操作浏览器本身(截屏,窗口大小,启动,关闭,安装插件,配置证书之类的)。由于使用的是浏览器原生的API,速度大大提高,而且调用的稳定性交给了浏览器厂商本身,显然是更加科学。然而带来的一些副作用就是,不同的浏览器厂商,对Web元素的操作和呈现多少会有一些差异,这就直接导致了Selenium WebDriver要分浏览器厂商不同,而提供不同的实现。

 4、WebDriver和Selenium配置

  1)WebDriver配置

  因为不同浏览器支持的WebDriver是不同的,因此需要根据测试的需求下载对应的WebDriver。然后将下载好的Driver放到环境变量配置到的文件夹,此处笔者使用Python完成UI自动化,因此将WebDriver放到了Python的环境变量中。

  下载链接:Selenium 与 浏览器驱动 - 介绍 (liushilive.github.io)

  2)Selenium安装

   pip install selenium -i https://mirrors.aliyun.com/pypi/simple/ 

5、Selenium常用方法

1、常用的包名/类名
  from selenium import webdriver
  from selenium.webdriver import ActionChains
  from selenium.webdriver.support.select import Select
  from selenium.webdriver.support.wait import WebDriverWait
  from selenium.webdriver.support import expected_conditions as EC
  from selenium.webdriver.common.by import By

2、webdriver

  webdriver主要用于实例化一个driver对象,不同的浏览器实例化的方式不同。
  Chrome_driver = webdriver.Chrome()
  Edge_driver = webdriver.Edge()
  Firefox_driver = webdriver.Firefox()

3、expected_conditions
  
  alert_is_present()                           # 判断alert是不是存在
  element_selection_state_to_be(element, is_selected)      # 判断某元素选中状态(True/False)是否与预期相同
  element_located_to_be_selected(locator)            # locator为一个元组(By.ID, "checkbox")
 
 element_to_be_selected(element)                # element为定位到的元素
  element_to_be_clickable(locator)               # 判断某元素是否是可访问,可启动
  frame_to_be_available_and_switch_to_it(locator)      # 判断frame是否可以切换过去
  invisibility_of_element_located(locator)          # 判断某个元素是不是不可访问或者不存在,True表示不可见
  new_window_is_opened(current_handles)            # 通过当前窗口句柄判断是否新增了窗口
  number_of_windows_to_be(num_windows)             # 判断当前窗口是不是指定数量
  presence_of_all_elements_located(locator)          # 判断定位元素的位置至少有一个元素存在页面中
  presence_of_element_located(locator)              # 判断一个元素存在页面中
  staleness_of(element)                       # 判断某个元素不再附加于DOM树中
  text_to_be_present_in_element(locator, text_)        # 判断给定文本是否出现在特定元素中
  text_to_be_present_in_element_value(locator, text_)    # 判断给定文本是否出现在特定元素的value中
  title_contains(title)                      # title是否包含特定文本
  title_is(title)                          # title是否是指定的title
  url_changes(url)                          # url判断网页是否更改了
  url_contains(url)                         # 判断网页是否包含给定字段
  url_matches(pattern)                       # 判断网址是否匹配给定格式
  url_to_be(url)                          # 判断网页是否为特定网页
  visibility_of_element_located(locator)            # 判断特定元素是否可见  
  visibility_of(element)                      # 判断特定元素是否可见
  visibility_of_all_elements_located(locator)          # 判断locator定位的所有元素都存在
  visibility_of_any_elements_located(locator)          # 判断locator定位的任意元素存在
4、实例化后的driver常见用法
  driver = webdriver.Chrome()  # 实例化一个driver
  driver.get(url)         # 打开指定网址
  driver.refresh()        # 刷新页面
  driver.title          # 获取标题
  driver.page_source       # 获取当前页面渲染之后的源代码
  driver.current_url       # 获取当前的url
  driver.window_handles     # 获取当前窗口句柄,以列表形式返回
  driver.current_window_handle  # 获取当前所在窗口句柄
  driver.close()          # 关闭浏览器当前窗口
  driver.quit()          # 关闭浏览器所有窗口并且退出

5、Select

  def select_method(self):
    self.driver.get("https://www.woniuxy.com/live")
    # 找元素,使用变量接收元素
    select_element = self.driver.find_element_by_id("searchlivebymajor")
    my_select = Select(select_element) # 传入参数,是web元素,得是select元素,select标签
    my_select.select_by_visible_text("Java测试开发") # 通过可以看的见文本,选择一项

6、切换window/frame      
  def switch_window(self): # 切换window     
    self.driver.get("http://www.woniuxy.com/") # 打开一个url     
    element = self.driver.find_element_by_link_text("企业内训")     
    element.click()     
    currents
= self.driver.window_handles # 是个列表     
    self.driver.switch_to.window(currents[1])  # 使用下标法选择窗口的句柄,切换到新的句柄,新的位置     
    shizi_element = self.driver.find_element_by_link_text("师资介绍")     
    print(shizi_element) 7、切换ifram   
  
  def
switch_iframe(self):     self.driver.get("https://www.woniuxy.com/train/teacher.html")     time.sleep(15)     # 找第一个iframe     iframe1 = self.driver.find_element_by_id("iframe_company_mini")     # 切换到iframe1     self.driver.switch_to.frame(iframe1)     iframe2 = self.driver.find_element_by_class_name("ke-edit-iframe")     # 切换到iframe2     self.driver.switch_to.frame(iframe2)     # 找到聊天的输入框     input_box = self.driver.find_element_by_xpath("/html/body")     # 输入发送的内容     input_box.send_keys("this is a testing")     # 逐层向上返回     self.driver.switch_to.parent_frame()# 返回父级iframe     # 直接返回到最外层,也就是刚开始的地方     self.driver.switch_to.default_content() # 返回最初的地方,重新来过

6、ActionChaines(动作链)

1、动作链常用动作
  click(on_element=None)           单击鼠标左键
  click_and_hold(on_element=None)      点击鼠标左键,不松开
  context_click(on_element=None)      点击鼠标右键
  double_click(on_element=None)       双击鼠标左键
  drag_and_drop(source, target)       拖拽到某个元素然后松开
  drag_and_drop_by_offset(source, xoffset, yoffset)       拖拽到某个坐标然后松开
  key_down(value, element=None)                  按下某个键盘上的键
  key_up(value, element=None)                   松开某个键
  move_by_offset(xoffset, yoffset)                鼠标从当前位置移动到某个坐标
  move_to_element(to_element)                   鼠标移动到某个元素
  move_to_element_with_offset(to_element, xoffset, yoffset) 移动到距某个元素(左上角坐标)多少距离的位置
  perform()           执行链中的所有动作
  release(on_element=None)  在某个元素位置松开鼠标左键
  send_keys(*keys_to_send)  发送某个键到当前焦点的元素
  send_keys_to_element(element, *keys_to_send)  发送某个键到指定元素
2、动作链基本操作
  
  driver = webdriver.Chrome()
  actions = ActionChains(driver)  # 实例化一个动作链
  # 执行动作链动作点击→鼠标移动到指定元素→释放鼠标→执行
  action.click(on_element=None).move_to_element(to_element).release(on_element=None).perform()
  # 分开执行
  action.click(on_element=None)
  action.move_to_element(to_element)
  action.release(on_element=None)
  action.perform()
3、例:通过ActionChains实现drag_and_drop()动作
  def drag_drop(self):
        self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
        iframe = self.driver.find_element_by_id("iframeResult")
        self.driver.switch_to.frame(iframe)
        # 源元素 - 开始
        source_element = self.driver.find_element_by_id("draggable")
        # 目标元素 - 结束
        target_element = self.driver.find_element_by_id("droppable")
        # 定义动作链
        actions = ActionChains(self.driver) # 实例化一个动作链,参数是driver,意味着driver要做动作链中动作
        # 定义动作链中的动作
        actions.drag_and_drop(source_element,target_element)
        # 执行动作
        actions.perform()
        self.driver.switch_to.alert.accept() # 切换到alert,点击确定

7、元素等待

1、 强制等待(让整个执行程序等待50s)
  time.sleep(50)

2、隐式等待(让driver等待10s)
  driver.implicitly_wait(10)

3、显示等待(刷新频率为1次/s,超过10s没有发现就为超时)
  my_wait = WebDriverWait(driver, timeout=10, poll_frequency=1)  # 实例化一个显示等待
  my_wait.until(EC.visibility_of_element_located(locator))     # 知道指定locator出现,停止等待 

 8、Cookie/Session

 # 采用driver.get_cookie(),获取当前生成的sessionid,采用driver.get_cookies()获取浏览器存储的所有sessionid,
# 前者是字典存储着有关sessionid的多个键值对,后者是列表嵌套字典,包含多个cookie的信息。

def get_session():
    driver = webdriver.Chrome() driver.get(
url) driver.find_element_by_name("userName").send_keys("username") driver.find_element_by_name("userPass").send_keys("password") driver.find_element_by_class_name("btn.btn-primary.btn-block.btn-save").click() s_id = driver.get_cookie("JSESSIONID") # 将获取到的sessionid写入文件中
    with open("cookie",mode="w") as wf: contents = json.dumps(s_id) wf.write(contents)
 def driver_add_session():
      # 注意,添加之前需要先打开浏览器和url
      driver = webdriver.Chrome()
    driver.get(url) driver.delete_all_cookies() # 删除所有的原来cookies with open("cookie") as rf: session_dict = json.load(rf) # 字符串转字典 self.driver.add_cookie(session_dict) # 注意session必须是一个字典格式 self.driver.get(url/login)         # 直接登录成功

9、通过JS解决问题

# 在Selenium中有一个专门执行JS代码的API,execute_script(js),通过return 可以返回执行结果的返回值
1、滚动条回到底部
    js="document.getElementById('id').scrollTop=0"
    driver.execute_script(js)
    js1 = "document.documentElement.scrollTop=0"
    driver.execute_script(js)
2、滚动条划到底部
    js="document.getElementById('id').scrollTop=10000"
    driver.execute_script(js)
    js1 = "document.documentElement.scrollTop=10000"
    driver.execute_script(js)
3、滚动条拉到指定位置
    target = driver.find_element_by_id("xxx")
    driver.excute("arguments[0].scrollIntoView();", target)
4、scrollTo函数
    # --scrollHeight 获取对象的滚动高度。 
    # --scrollLeft 设置或获取位于对象左边界和窗口中目前可见内容的最左            端之间的距离。 
    # --scrollTop 设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离。 
    # --scrollWidth 获取对象的滚动宽度。
    #滚动到底部
    js = "window.scrollTo(0,document.body.scrollHeight)"
    driver.execute_script(js)
    # 滚动到顶部
    js = "window.scrollTo(0,0)"
    driver.execute_script(js)

三、Appium

1、概述

   Appium 是一个开源工具,用于自动化 iOS 手机、 Android 手机和 Windows 桌面平台上的原生、移动 Web 和混合应用。「原生应用」指那些用 iOS、Android 或者 Windows SDKs 编写的应用。「移动 Web 应用」是用移动端浏览器访问的应用( Appium 支持 iOS 上的 Safari 、Chrome 和 Android 上的内置浏览器)。「混合应用」带有一个「webview」的包装器——用来和 Web 内容交互的原生控件。类似于 Apache Cordova 项目,创建一个混合应用使得用 Web 技术开发然后打包进原生包装器创建一个混合应用变得容易了。重要的是,Appium 是跨平台的:它允许你用同样的API 对多平台(iOS、Android、Windows)写测试。做到在 iOS、Android 和 Windows 测试套件之间复用代码。

2、原理

  Appium通信原理:Client端发送自动化指令给Appium server,Appium Server接收到client发送的指令后,转换为移动端能够识别的指令,然后发送给移动端设备,并对移动端设备进行操作。 

  1) Client端:

  一般来说就是运行代码的机器,即我们是用Java语言编写的代码,也可以用其他Selenium支持Python,ruby,C#等语言来编写,Appium提供的Appium-client API是Appium通过扩展Selenium的Webdriver协议而来的,我们编写代码的时只要实现Webdriver标准协议即可。

  2)Appium Server:

  Appium Server功能是监听接口,接收client端发送的command,然后将command转为移动端能够识别的command,然后发送给移动设备进行操作,再等待移动设备返回来的操作结果,将操作结果发送给client端。 Appium server是可以放在client端,也可以放在云端。 Appium server 默认的端口号是4723,用于Appium server监听client端的发送来的请求。

  3) Android设备

  Android端,Appium基于Webdriver协议,利用Bootstrap.jar,最后通过调用UIautomatior命令,实现APP的自动化测试(Android4.2以前的版本是用Instrumentation框架,通过绑定另外一个独立的selendroid项目来实现),Android4.2以后的版本是用UIautomator)。UIAutomator测试框架是Android SDK自带的APP UI自动化测试Java库,另外UIAutomator对H5支持有限,Appium引入了Chromedriver以及Safaridriver来实现H5的自动化。

   参考链接:Appium工作原理 - 简书 (jianshu.com)

3、Appium的安装及相关环境的配置

# 1、Appium安装之前需要安装JDK环境,因为笔者已经安装,所以不赘述了。
# 2、Appium安装可以在官网下载,详见链接
# 3、python安装Appium库
pip install Appium-Python-Client -i https://mirrors.aliyun.com/pypi/simple/

4、Appium基本操作

  Appium结合python完成移动端UI自动化过程为:(1)启动被测的设备和相关的app应用(2)连接设备到client(通过USB数据线或者无线网络连接,即使是使用无线连接也需要首先通过数据线连接),查看设备的连接信息和要测app的包名及主进程名。(3)启动Appium输入相关参数进行连接,连接之后可以进行相关自动化测试操作。

  1)Appium界面

 2)手机端设置

  在进行appium连接的时候需要查看一下手机端的USB调试设置,打开允许通过USB进行点击操作、允许通过USB安装应用、USB默认设置中勾选MIDI模式。

3)Appium库安装,及通过python操作appium

  Appium可以使用selenium中的内容来实现元素定位,同时还有自己的定位方式:driver.find_element_by_android_uiautomator()

# 1、python中安装Appium库  
pip install Appium-Python-Client -i https://mirrors.aliyun.com/pypi/simple/

#
2、通过python连接移动端设备 from appium import webdriverclass AppDriver: def __init__(self): self.driver = self.get_driver def get_driver(self): desired_capabilities = { "platformName": "Android", "platformVersion": "10", "deviceName": "redmi", "udid": "b431bcd7" } driver = webdriver.Remote("http://172.16.5.54:4723/wd/hub", desired_capabilities) return driver # 打开手机中的博客园 def open_blog(self): self.get_driver().activate_app("com.cnblogs.xamarinandroid") # 3、driver.find_element_by_android_uiautomator()定位, 下面的内容为()中的参数 #匹配全部text文字: 'new UiSelector().text("手机号")'#包含text文字 : 'new UiSelector().textContains("机")'#以text什么开始: 'new UiSelector().textStartsWith("手")'#正则匹配:text 'new UiSelector().textMatches("^手.*")'#className: 'new UiSelector().className("android.widget.TextView")'# classNameMatches: 'new UiSelector().classNameMatches("^android.widget.*")'# resource-id、resourceIdMatches: 'new UiSelector().resourceId("com.syqy.wecash:id/et_content")'# description: 'new UiSelector().description("S 日历")'
# 匹配以什么开始
'new UiSelector().descriptionStartsWith("日历")'
# 通过正则进行匹配 'new UiSelector().descriptionMatches(".*历$")'

# 4、uiaumator滑动
# 找到第一个可滚动元素,然后找到带有文本“ Tabs”的TextView。“标签”元素将滚动到视图中 self.driver.find_element_by_android_uiautomator('new UiScrollable(new UiSelector().scrollable(true).instance(0)).getChildByText(new UiSelector().className("android.widget.TextView"), "Tabs")') # scrollIntoView返回滚动到视图中的元素,scrollIntoView允许滚动到任何UiSelector,元素以text中的为准 self.driver.find_element_by_android_uiautomator('new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("WebView").instance(0));') # 5、多点触控 import time from appium import webdriver from appium.webdriver.common.touch_action import TouchAction from appium.webdriver.common.multi_action import MultiAction desired_caps = {} desired_caps['platformName'] = 'Android' #平台名称 desired_caps['deviceName'] = "aa" #设备名称 desired_caps['platformVersion'] = "9" #平台版本 desired_caps['appPackage'] = "com.google.android.apps.messaging" #包名 desired_caps['appActivity'] = "com.google.android.apps.messaging.ui.ConversationListActivity" #包入口 desired_caps['noReset'] = 'true' driver = webdriver .Remote('http://localhost:4723/wd/hub', desired_caps) #启动服务/通过4723端口来建立一个会话 def scr(): action1 = TouchAction(driver) #第一个手势 action2 = TouchAction(driver) #第二个手势 action3 = TouchAction(driver) #第三个手势 zoom_action = MultiAction(driver) action1.press(x = 300, y = 600).move_to(x = 300, y = 750).wait(200).release() action2.press(x = 400, y = 600).move_to(x = 400, y = 750).wait(200).release() action3.press(x = 500, y = 600).move_to(x = 500, y = 750).wait(200).release() zoom_action.add(action1,action2,action3) #加载 time.sleep(2) print("第%s次截图"%(i)) zoom_action.perform() #执行
if __name__ == '__main__': for i in range(100): #执行100次 scr()

 四、win ui

  使用win ui 要用到相关的插件应用:

使用uiautomation模块,相关的应用为:dotnet,pywin32,pywinauto

  使用一个栗子展示相关的操作(非重点)

import uiautomation
import os
import subprocess    # 通过subprocess创建一个线程
class TestNotepad:
    def __init__(self):
        subprocess.Popen("notepad.exe")
def notepad(self): # 找到Notepade - Class Name notepad_window = uiautomation.WindowControl(ClassName="Notepad") # 找editbox,输入内容 edit = notepad_window.EditControl() edit.SendKeys("祝大家国庆快乐") edit.SendKeys("\n") notepad_window.MenuItemControl(Name="文件(F)").Click() notepad_window.MenuItemControl(SubName="另存为(A)...").Click() save_window = notepad_window.WindowControl(Name="另存为") tree_item = save_window.TreeItemControl(Name="桌面") # 找到了第一个属性是树状的桌面 sub_tree_item = tree_item.TreeItemControl(Name="桌面") sub_tree_item.Click()

if __name__ == '__main__': mytest = TestNotepad() mytest.notepad()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

expected_conditions
posted @ 2021-10-19 10:32  顾子柒  阅读(2109)  评论(0)    收藏  举报