自动化测试概览

什么是自动化测试?

把人对软件的测试行为转化为由机器执行测试行为的一种实践,
自动化测试用例的维护成本高于其节省的测试成本时,
自动化测试就失去价值与意义,在这样的项目中推进自动化测试就会得不偿失.

自动化测试的优点?

可以替代大量的手工机械重复性操作.
自动化测试可以大幅提升回归测试的效率,非常适合敏捷开发过程;
可以高效实现某些手工测试无法完成或者代价巨大的测试类型.
可以保证每次测试执行的操作以及验证的一致性和可重复性,避免人为的遗漏或疏忽。

自动化测试的缺点?

并不能取代手工测试,只能替代手工测试中执行频率高、机械化的重复步骤。不要奢望所有的测试都自动化,否则一定会得不偿失。

自动测试远比手动测试脆弱,无法应对被测系统的变化,自动化测试用例的维护成本一直居高不下。对于执行过程中出现的明显错误和意外事件,自动化测试没有任何处理能力。

自动化测试用例的开发工作量远大于单次的手工测试.

手工测试发现的缺陷数量通常比自动化测试要更多,并且自动化测试仅仅能发现回归测试范围的缺陷。

测试的效率很大程度上依赖自动化测试用例的设计以及实现质量,不稳定的自动化测试用例实现比没有自动化更糟糕。

实行自动化测试的初期,用例开发效率通常都很低,大量初期开发的用例通常会在整个自动化测试体系成熟,和测试工程师全面掌握测试工具后,需要重构。

业务测试专家和自动化测试专家通常是两批人,前者懂业务不懂自动化技术,后者懂自动化技术但不懂业务,只有二者紧密合作,才能高效开展自动化测试。

自动化测试开发人员必须具备一定的编程能力,这对传统的手工测试工程师会是一个挑战。

什么样的项目适合自动化测试?


第一, 需求稳定,不会频繁变更。需求不稳定,需求变更频繁,  界面变化,或者是业务流程变化频繁不适合.

第二, 研发和维护周期长,需要频繁执行回归测试。对于短期的一次性项目,我觉得你应该选择手工探索式测试,以发现缺陷为第一要务。而对于一些中长期项目,我的建议是:对比较稳定的软件功能进行自动化测试,对变动较大或者需求暂时不明确的功能进行手工测试,最终目标是用 20% 的精力去覆盖 80% 的回归测试。

第三, 需要在多种平台上重复运行相同测试的场景.

第四, 测试项目通过手工测试无法实现或手工成本太高。所有的性能和压力测试,很难通过手工方式实现。

第五, 被测软件的开发较为规范,能够保证系统的可测试性。

第六, 测试人员已经具备一定的编程能力。

软件开发各阶段都有哪些自动化测试技术?

单元测试、代码级集成测试、Web Service 测试和 GUI 测试阶段的自动化技术

单元测试的自动化技术包括:

不仅仅指测试用例执行的自动化,还包含以下五个方面:
用例框架代码生成的自动化.部分测试输入数据的自动化生成;
自动桩代码的生成;被测代码的自动化静态分析(常用的代码静态分析工具有 Sonar 和 Coverity);测试覆盖率的自动统计与分析。

代码级集成测试的自动化技术:

指将已经开发完成的软件模块放在一起测试,  关注点更多的是软件模块之间的接口调用和数据传递。

代码级集成测试与单元测试最大的区别只是,代码级集成测试中被测函数内部调用的其他函数必须是真实的,不允许使用桩代码代替,而单元测试中允许使用桩代码来模拟内部调用的其他函数。

现在的开发理念追求的是系统复杂性的解耦,会去尽量避免“大单体”应用,采用 Web Service 或者 RPC 调用的方式来协作完成各个软件功能。所以现在的软件企业,尤其是互联网企业,基本不会去做代码级集成测试.

Web Service 测试的自动化技术:

指 SOAP API 和 REST API 这两类 API 测试,典型是采用 SoapUI 或 Postman 等类似的工具。但这类测试工具基本都是界面操作手动发起 Request 并验证 Response,所以难以和 CI/CD 集成,于是就出现了 API 自动化测试框架。

**对于基于代码的 API 测试用例,通常包含三大步骤:**准备 API 调用时需要的测试数据;准备 API 的调用参数并发起 API 的调用;验证 API 调用的返回结果。

最流行的 API 自动测试框架是 REST Assured,它可以方便地发起 Restful API 调用并验证返回结果.

Web Service 测试“自动化”的内涵不仅仅包括 API 测试用例执行的自动化,还包括以下四个方面:

测试脚手架代码的自动化生成;部分测试输入数据的自动生成;Response 验证的自动化;基于 SoapUI 或者 Postman 的自动化脚本生成。

GUI 测试的自动化技术

核心思想是基于页面元素识别技术,对页面元素进行自动化操作,以模拟实际终端用户的行为并验证软件功能的正确性。

**GUI** **自动化测试主要分为两大方向,传统 Web 浏览器和移动端原生应用(Native App)的 GUI 自动化。二者采用的具体技术差别很大,用例设计的思路类似。Web端有selenium,**  **Micro Focus** **的 UFT(前身是 HP 的 QTP).移动端有Appium等.**

GUI 自动化测试的技术、原理和行业最佳实践。

GUI 测试中两个非常重要的概念:测试脚本和数据的解耦,以及页面对象(Page Object)模型.

数据驱动(Data-driven)测试:

测试脚本只有一份,其中需要输入数据的地方会用变量来代替,然后把测试输入数据单独放在一个文件中。这个存放测试输入数据的文件,通常是表格的形式,也就是最常见的 CSV 文件。然后,在测试脚本中通过 data provider 去 CSV 文件中读取一行数据,赋值给相应的变量,执行测试用例。接着再去 CSV 文件中读取下一行数据,读取完所有的数据后,测试结束。CSV 文件中有几行数据,测试用例就会被执行几次。

本质是实现了数据驱动的测试,让操作相同但是数据不同的测试可以通过同一套自动化测试脚本来实现,只是在每次测试执行时提供不同的测试输入数据。

数据驱动的好处:

很好地解决了大量重复脚本的问题,实现了“测试脚本和数据的解耦”。数据驱动测试的数据文件中不仅可以包含测试输入数据,还可以包含测试验证结果数据,甚至可以包含测试逻辑分支的控制变量。数据驱动测试的思想不仅适用于 GUI 测试,还可以用于 API 测试、接口测试、单元测试等。

页面对象模型(Page Object):

 以页面(Web Page 或者 Native App Page)为单位来封装页面上的控件以及控件的部分操作。而测试用例,更确切地说是操作函数,基于页面封装对象来完成具体的界面操作,最典型的模式是“XXXPage.YYYComponent.ZZZOperation”。

如何把控操作函数的粒度?

操作函数的粒度是指,一个操作函数到底应该包含多少操作步骤才是最合适的。以完成一个业务流程(business flow)为主线,抽象出其中的“高内聚低耦合”的操作步骤集合,操作函数就由这些操作步骤集合构成。

如何衔接两个操作函数之间的页面?

如果连续的两个操作函数之间无法用页面衔接,那就需要在两个操作函数之间加入额外的页面跳转代码,或者是在操作函数内部加入特定的页面跳转代码。

GUI测试创建测试数据的方法:

从创建的技术手段上来讲,创建测试数据的方法主要分为三种:API 调用;数据库操作;综合运用 API 调用和数据库操作。

从创建的时机来讲,创建测试数据的方法主要分为两种:测试用例执行过程中,实时创建测试数据,我们通常称这种方式为 On-the-fly。测试用例执行前,事先创建好“开箱即用”的测试数据,通常称这种方式为 Out-of-box。

对于页面对象自动生成,商用测试软件已经实现。如果选择开源测试框架,就需要自己实现这个功能了。

GUI 测试数据自动生成,主要是基于测试输入数据的类型以及对应的自定义规则库实现的,并且对于多个测试输入数据,可以基于笛卡尔积来自动组合出完整的测试用例集合。

对于无头浏览器,你可以把它简单地想象成运行在内存中的浏览器,它拥有完整的浏览器内核。与普通浏览器最大的不同是,它在执行过程中看不到运行的界面。目前,Headless Chrome 结合 Puppeteer 是最先进的无头浏览器方案,如果感兴趣,你可以下载试用

GUI测试的稳定性:

要提高 GUI 测试稳定性,首先你需要知道到底是什么原因引起的不稳定。你必须找出尽可能多的不稳定因素,然后找到每一类不稳定因素对应的解决方案。

五种造成 GUI 测试不稳定的因素非预计的弹出对话框;页面控件属性的细微变化;被测系统的 A/B 测试,随机的页面延迟造成控件识别失败;测试数据问题。

非预计的弹出对话框可以:

(1)  当自动化脚本发现控件无法正常定位,或者无法操作时,GUI 自动化框架自动**进入“异常场景恢复模式**.  在“异常场景恢复模式”下,GUI 自动化框架依次检查各种可能出现的对话框,一旦确认了对话框的类型,立即执行预定义的操作(比如,单击“确定”按钮,关闭这个对话框),接着重试刚才失败的步骤。而对于新类型的对话框,只能通过自动化的方式尝试点击上面的按钮进行处理。每当发现一种潜在会弹出的对话框,我们就把它的详细信息(包括对象定位信息等)更新到“异常场景恢复”库中,下次再遇到相同类型的对话框时,系统就可自动关闭。

页面控件属性的细微变化:

 采用“组合属性”定位控件会更精准**,而且成功率会更高,如果能在**此基础上加入“模糊匹配”技术**,可以进一步**提高控件的识别率**。但是,开源的 GUI 自动化测试框架,目前还没有现成的框架直接支持模糊匹配,通常需要你进行二次开发,实现思路是:实现自己的对象识别控制层,也就是在原本的对象识别基础上额外封装一层,在这个额外封装的层中加上模糊匹配的实现逻辑。通常,不建议把模糊匹配逻辑以硬编码的方式写在代码里,而是引入规则引擎,将具体的规则通过配置文件的方式与代码逻辑解耦。

被测系统的 A/B 测试:

在测试脚本内部对不同的被测版本做分支处理,脚本需要能够区分 A 和 B 两个的不同版本,并做出相应的处理。

随机的页面延迟造成控件识别失败:

 加入重试(retry)机制。重试机制是指,当某一步 GUI 操作失败时,框架会自动发起重试,重试**可以是步骤级别的**,**也可以是页面级别的**,甚至是业务流程级别的。对于开源 GUI 测试框架,重试机制往往不是自带的功能,需要自己二次开发来实现。需要特别注意的是,对于那些会修改一次性使用数据的场景,切忌不要盲目启用页面级别和**业务流程级别**的重试.

测试数据问题:略.

GUI的自动化测试报告

理想中的 GUI 测试报告

 应该是由一系列按时间顺序排列的屏幕截图组成,并且这些截图上可以高亮显示所操作的元素,同时按照执行顺序配有相关操作步骤的详细描述。

开源 GUI 测试框架的测试报告实现思路:

利用 Selenium WebDriver 的 screenshot 函数在一些特定的时机(比如,页面发生跳转时,在页面上操作某个控件时,或者是测试失败时,等等)完成界面截图功能。具体到代码实现,**通常有两种方式:扩展 Selenium 原本的操作函数;在相关的 Hook 操作中调用 screenshot 函数。**
**第一,** **扩展 Selenium 原本的操作函数实现截图以及高亮显示操作元素的功能:**

**自己实现的 click 函数被调用时:**首先,用 Javascript 代码高亮显示被操作的元素,高亮的实现方式就是利用 JavaScript 在对象的边框上渲染一个 5-8 个像素的边缘;然后,调用 screenshot 函数完成点击前的截图;最后,调用 Selenium 原生的 click 函数完成真正的点击操作。那么,以后凡是需要调用 click 函数时,都直接调用这个自己封装的 click 函数,直接得到高亮了被操作对象的界面截图。

**第二,** **在相关的 Hook 操作中调用 screenshot 函数实现截图以及高亮显示操作元素的功能。**

**第三,** 

**大型全球化电商网站的 GUI 自动化测试如何开展(如果你所在的企业或者项目正在大规模开展 GUI 测试,并且准备使用页面对象模型以及业务流程封装等最佳实践的话):**

GUI 测试通常只覆盖最核心且直接影响主营业务流程的 E2E 场景。

    按照自底向上的顺序分层次介绍 GUI 自动化的测试策略。
首先,要从前端组件的级别来保证质量,也就是需要对那些自定义开发的组件进行完整全面的测试。公共组件库会被很多上层的前端模块依赖,它的质量将直接影响这些上层模块的质量,所以我们往往会对这些公共组件进行严格的单元测试。最常用的方案是:基于 Jest 开展单元测试,并考量 JavaScript 的代码覆盖率指标。

完成单元测试后,往往还会基于被测控件构建专用的测试页面,在页面层面再次验证控件相关的功能和状态。这部分测试工作也需要采用自动化的形式实现,具体的做法是:

1.  先构建一个空页面,并加入被测控件,由此可以构建出一个包含被测控件的测试页面,这个页面往往被称为 Dummy Page;
2.  从黑盒的角度出发,在这个测试页面上通过手工和自动化的方式操作被测控件,并验证其功能的正确性。

对于自动化的部分,需要基于 GUI 自动化测试框架开发对应的测试用例。这些测试用例,往往采用和 GUI E2E 一样的测试框架,也是从黑盒的角度来对被测控件做功能验证。

**其次,每一个前端模块,都会构建自己的页面对象库,并且在此基础上封装开发自己的业务流程脚本。这些业务流程的脚本,可以组装成每个前端模块的测试用例。最后,组合各个前端模块,并站在终端用户的视角,以黑盒的方式使用网站的端到端(E2E)测试。**

Monkey测试

monkey测试:

测试软件的稳定性,健壮性的方法,一般可以通过测试过程中打印的日志来发现问题。Monkey是通过命令行来对APP进行测试的工具,允许在模拟器里或真机上。向系统发送伪随机用户时间流实现对应用程序进行压力测试;

环境搭建:
win10需按照ADB工具, XA,然后再配置变量XB
实践: 在手机开发者选项中,勾上USB调试。使用adb命令查看已连接设备:$adb devices

关闭adb的后台进程: adb kill-server     让手机脱离USB线的TCP连接方式: adb tcpip
开启tcp连接方式连接手机: adb connect   收集日志数据,用于后续的分析,比如耗电量: adb bugreport
发送压测命令:对随机应用执行100条monkey命令:adb shell monkey 1000
对特定应用进行monkey测试:adb shell monkey -P XXX.apk -v 测试次数
APP信息:获取当前界面信息:  adb shell dumpsys activity top
获取任务列表: adb shell dumpsys activity activities
获得内存信息: adb shell dumpsys meminfo com.android.settings
获取cpu信息: adb shell dumpsys cpuinfo
获取特定包基本信息: adb shell dumpsys package xx
获取当前activity: adb shell dumpsys activity top
APP入口:adb logcat| findsdr -i displayed 或 appt dump badging xx.apk | grep lanunchable-activity  或
apkanalyzer 最新版本的sdk才有
启动应用: adb shell am start -n xxx -S     日志查询: adb logcat       
当前应用查询  adb logcat | findstr Displayed

ADB SHELL:本身Linux的shell,可以调用Android内置命令.
adb shell dumpsys   adb shell pm     adb shell am      adb shell ps      adb shell monkey

清理应用缓存:adb shell pm clear 包名
显示常用uiautomator命令: adb shell uiautomator 
显示常用输入命令: adb shell input 

Selenium测试

概论

什么是Selenium
 Web测试框架,丰富的API,支持多种语言编写测试脚本且可在多种浏览器执行测试脚本。
Selenium History:
 V1.0 和 V2.0 版本的技术方案是截然不同的,V1.0 的核心是 Selenium RC,而 V2.0 的核心是 WebDriver, V3.0 相比 V2.0 并没有本质上的变化,主要是增加了对 MacOS 的 Safari 和 Windows 的 Edge 的支持,并彻底删除了对 Selenium RC 的支持。
webdriver:
 server-client设计模式设计的。server端即浏览器。当我们的脚本启动浏览器后,等待client发送请求并做出相应;client端即测试代码.
搭建环境:
本地安装Python,在官网下载,安装时勾选path,安装完成后,在cmd中输入python,查看安装成功.
安装selenium,在cmd中输入pip install selenium
安装Chrome Webdriver:[官网](https://sites.google.com/a/chromium.org/chromedriver/downloads)
不同浏览器的驱动:
browser = webdriver.Chrome()    browser = webdriver.Firefox()
browser = webdriver.Safari()    browser = webdriver.Ie()
基本模板:
from selenium import webdriver
browser = webdriver.Chrome(executable_path="..\..\..\zc\chromedrive\chromedriver.exe")
# executable_path来指定chromedirver路径
browser.get('https://www.baidu.com')
print(browser.title)
browser.quit()

常用方法:

八种定位方法:
# 通过 id 定位
dr.find_element_by_id("kw")

# 通过name定位:
dr.find_element_by_name("wd")

# 通过class name定位:
dr.find_element_by_class_name("s_ipt")

# 通过tag name定位:
dr.find_element_by_tag_name("input")

# 通过 xpath 定位的几种写法
dr.find_element_by_xpath("//*[@id='kw']")
dr.find_element_by_xpath("//*[@name='wd']")
dr.find_element_by_xpath("//input[@class='s_ipt']")
dr.find_element_by_xpath("/html/body/form/span/input")
dr.find_element_by_xpath("//span[@class='soutu-btn']/input")
dr.find_element_by_xpath("//form[@id='form']/span/input")
dr.find_element_by_xpath("//input[@id='kw' and @name='wd']")

# 通过 css 定位的几种写法
dr.find_element_by_css_selector("#kw")
dr.find_element_by_css_selector("[name=wd]")
dr.find_element_by_css_selector(".s_ipt")
dr.find_element_by_css_selector("html > body > form > span > input")
dr.find_element_by_css_selector("span.soutu-btn> input#kw")
dr.find_element_by_css_selector("form#form > span > input")

# 通过 link_text 定位
dr.find_element_by_link_text("新闻")
dr.find_element_by_link_text("hao123")
dr.find_element_by_partial_link_text("新")
dr.find_element_by_partial_link_text("hao")
dr.find_element_by_partial_link_text("123")


定位一组元素:WebDriver还提供8种用于定位一组元素的方法。
find_elements_by_id()
find_elements_by_name()
find_elements_by_class_name()
find_elements_by_tag_name()
find_elements_by_link_text()
find_elements_by_partial_link_text()
find_elements_by_xpath()
find_elements_by_css_selector()
浏览器操作:
set_window_size()方法来设置浏览器的大小 ,
maximize_window() 全屏显示.如:browser.set_window_size(480,800)
back()和 forward()方法来模拟后退和前进按钮, refresh()方法模拟F5刷新页面.
元素操作:
Clear()方法,清除文本. Send_keys(‘value’)方法,模拟键盘输入.
Click()方法,单击元素. submit()方法用于提交表单.
size: 返回元素的尺寸. text: 获取元素的文本.
get_attribute(name): 获得属性值。is_displayed(): 设置该元素是否用户可见。
鼠标事件:
在 WebDriver中鼠标操作的方法封装在 ActionChains 类提供。常用方法如下:
perform():执行所有 ActionChains 中存储的行为;
move_to_element(): 鼠标悬停。
context_click(): 右击;  double_click(): 双击;
drag_and_drop(): 拖动;
如:
# 引入 ActionChains 类
from selenium.webdriver.common.action_chains import ActionChains
# 定位到要悬停的元素
cc = browser.find_element_by_xpath('//*[@id="content"]/div/div[1]/div[2]/ul/li[4]/a')
# 对定位到的元素执行鼠标悬停操作
ActionChains(browser).double_click(cc).perform()
键盘事件:
Keys()类提供了键盘上几乎所有按键的方法.
# 引入 Keys 模块
from selenium.webdriver.common.keys import Keys
#常用键盘方法:
send_keys(Keys.BACK_SPACE) 删除键(BackSpace)
send_keys(Keys.SPACE) 空格键(Space)
send_keys(Keys.TAB) 制表键(Tab)
send_keys(Keys.ESCAPE) 回退键(Esc)
send_keys(Keys.ENTER) 回车键(Enter)
send_keys(Keys.CONTROL,'a') 全选(Ctrl+A)
send_keys(Keys.CONTROL,'c') 复制(Ctrl+C)
send_keys(Keys.CONTROL,'x') 剪切(Ctrl+X)
send_keys(Keys.CONTROL,'v') 粘贴(Ctrl+V)
send_keys(Keys.F1) 键盘 F1
send_keys(Keys.F12) 键盘 F12
断言与等待:
#测试时需要拿实际结果与预期结果进行比较,这个比较称为断言
title:用于获得当前页面的标题。 current_url:用户获得当前页面的URL。 
text:获取搜索条目的文本信息。

#WebDriver提供两种类型的等待:显式等待和隐式等待。
显式等待:使WebdDriver等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException)。
隐式等待:WebDriver提供了implicitly_wait()方法来实现隐式等待,默认设置为0。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
base_url = 'http://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.get(base_url)
# 1.显示等待
# WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
#       driver :浏览器驱动。
#       timeout :最长超时时间,默认以秒为单位。
#       poll_frequency :检测的间隔(步长)时间,默认为0.5S。
#       ignored_exceptions :超时后的异常信息,默认情况下抛NoSuchElementException异常
#       until(method, message=‘’)-----调用该方法提供的驱动程序作为一个参数,直到返回值为True。
#       until_not(method, message=‘’)---调用该方法提供的驱动程序作为一个参数,直到返回值为False。
# presence_of_element_located()方法判断元素是否存在。
element = WebDriverWait(driver, 5, 0.5).until(
    EC.presence_of_element_located((By.ID, 'kw'))
    )
element.send_keys('要搜索的内容')
time.sleep(3)
driver.quit()
# 2. 隐式等待
from selenium.common.exceptions import NoSuchElementException
from selenium import webdriver
from time import ctime
import time
base_url2 = 'https://www.baidu.com/'
browser = webdriver.Chrome('../tools/chromedriver.exe')
# 设置隐式等待为10s
browser.implicitly_wait(10)
browser.get(base_url2)
try:
    print(ctime())
    browser.find_element_by_id('kw').send_keys('se')
    time.sleep(3)
except NoSuchElementException as e:
    print(e)
finally:
    print(ctime())
    browser.quit()
多表单切换

WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌页面上的元素无法直接定位。需要通过switch_to.frame()方法将当前定位的主体切换为frame/iframe表单的内嵌页面中。

如:browser.switch_to.frame(‘xx’)
switch_to.frame() 默认可以直接取表单的id 或name属性。如果iframe没有可用的id和name属性,则可以通过下面的方式进行定位。
#先通过xpth定位到iframe
xf = driver.find_element_by_xpath('//*[@id="x-URS-iframe"]')
#再将定位对象传给switch_to.frame()方法
driver.switch_to.frame(xf)
……
driver.switch_to.parent_frame()
除此之外,在进入多级表单的情况下,还可以通过switch_to.default_content()跳回最外层的页面。
多窗口切换

WebDriver提供了switch_to.window()方法,可以实现在不同的窗口之间切换。
current_window_handle:获得当前窗口句柄。
window_handles:返回所有窗口的句柄到当前会话。
switch_to.window():用于切换到相应的窗口,与上一节的switch_to.frame()类似,前者用于不同窗口的切换,后者用于不同表单之间的切换。

from selenium import webdriver
import time
base_url = 'https://www.baidu.com/'
browser = webdriver.Chrome('../tools/chromedriver.exe')
# 隐式等待10秒
browser.implicitly_wait(10)
browser.get(base_url)
# 获得搜索窗口的句柄
search_windows = browser.current_window_handle
browser.find_element_by_link_text('登录').click()
browser.find_element_by_link_text('立即注册').click()
# 活得当前打开窗口的句柄
all_handles = browser.window_handles
# 进入注册窗口
for handle in all_handles:
    if handle != search_windows:
        browser.switch_to.window(handle)
        print('now register window!')
        browser.find_element_by_name('account').send_keys('username')        browser.find_element_by_name('password').send_keys('password')
        time.sleep(2)
browser.quit()
警告框处理

WebDriver中处理JavaScript所生成的alert、confirm以及prompt十分简单.
具体做法是使用 switch_to.alert 方法定位到 alert/confirm/prompt,然后使用text/accept/dismiss/ send_keys等方法进行操作。
text:返回 alert/confirm/prompt 中的文字信息。 accept():接受现有警告框。
dismiss():解散现有警告框。 send_keys(keysToSend):发送文本至警告框。keysToSend:将文本发送至警告框。

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time

base_url = 'https://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.implicitly_wait(10)
driver.get(base_url)
# 鼠标悬停至 “设置” 链接
link = driver.find_element_by_link_text('设置')
ActionChains(driver).move_to_element(link).perform()
# 打开搜索设置
driver.find_element_by_link_text('搜索设置').click()
time.sleep(3)
# 点击 “搜索设置”
driver.find_element_by_class_name('prefpanelgo').click()
time.sleep(3)
# 接受警告框prefpanelgo
driver.switch_to.alert.accept()
time.sleep(3)
driver.quit()
下拉框选择:

WebDriver提供了Select类来处理下拉框, Select类用于定位select标签。
select_by_value() 方法用于定位下接选项中的value值。
send_keys()方法来实现文件上传

from selenium import webdriver
from selenium.webdriver.support.select import Select
from time import sleep

base_url = 'https://www.baidu.com/'
driver = webdriver.Chrome('../tools/chromedriver.exe')
driver.implicitly_wait(10)
driver.get(base_url)

# 鼠标悬停至“设置”链接
driver.find_element_by_name('设置').click()
sleep(2)
# 打开 “搜索设置”
driver.find_element_by_name('搜索设置').click()
sleep(2)
# 搜索结果显示条数
# Select类用于定位select标签。
sel = driver.find_element_by_xpath("//select[@id='nr']")
# select_by_value() 方法用于定位下接选项中的value值。
Select(sel).select_by_value('50')

driver.quit()
文件上传
通过input标签实现的上传功能,可以将其看作是一个输入框,即通过send_keys()指定本地文件路径的方式实现文件上传
from selenium import webdriver
import os


driver = webdriver.Chrome('../tools/chromedriver.exe')
file_path = "file:///" + os.path.abspath('upfile.html')
driver.get(file_path)

# 定位上传按钮的位置
driver.find_element_by_name('file').send_keys(os.path.abspath('upfile.txt'))
driver.quit()
cookie操作:
get_cookies(): 获得所有cookie信息。
get_cookie(name): 返回字典的key为“name”的cookie信息。
add_cookie(cookie_dict) : 添加cookie。“cookie_dict”指字典对象,必须有name 和value 值。
delete_cookie(name,optionsString):删除cookie信息。“name”是要删除的cookie的名称,“optionsString”是该cookie的选项,目前支持的选项包括“路径”,“域”。
delete_all_cookies(): 删除所有cookie信息。

from selenium import webdriver
from time import sleep


base_url = 'https://www.baidu.com/'
browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# 1. 获取 cookie 信息
cookies = browser.get_cookies()
print(cookies)
sleep(2)
browser.quit()

# 2. cookie 写入
browser.add_cookie(
    {
        'name': 'add-cookie',
        'value': 'add-cookie-value'
    }
)
# 遍历cookies打印cookie信息
for cookie in browser.get_cookies():
    print("%s ---> %s" % (cookie['name'], cookie['value']))
sleep(2)
browser.quit()
调用 JavaScript

WebDriver提供了execute_script()方法来执行JavaScript代码。
WebDriver提供了截图函数get_screenshot_as_file()来截取当前窗口。

rom selenium import webdriver
from time import sleep


base_url = 'https://www.baidu.com'
browser = webdriver.Chrome('../tools/chromedriver.exe')
browser.get(base_url)

# window.scrollTo()方法用于设置浏览器窗口滚动条的水平和垂直位置。方法的第一个参数表示水平的左间距,第二个参数表示垂直的上边距。
browser.set_window_size(500, 500)
browser.find_element_by_id('kw').send_keys('百度')
browser.find_element_by_id('su').click()
sleep(2)

# 通过javascript设置浏览器窗口的滚动条位置
js = "window.scrollTo(100, 450);"
browser.execute_script(js)
sleep(2)

browser.quit()
窗口截图

webdriver 提供了截图函数 get_screenshot_as_file() 来截取当前窗口

from selenium import webdriver
from time import sleep

base_url = 'http://www.baidu.com/'
browser = webdriver.Chrome('../tools/chromedriver.exe')

browser.get(base_url)

browser.find_element_by_id('kw').send_keys('python selenium')
browser.find_element_by_id('su').click()
sleep(2)

# 截取当前窗口并指定报错截图的位置
# browser.get_screenshot_as_file('ScreenShot/14_screenShot.jpg')
browser.get_screenshot_as_file('ScreenShot/14_screenShot.png')

browser.quit()
关闭浏览器:
close() 关闭单个窗口
quit() 关闭所有窗口

unittest单元测试框架

什么是unittest?
Python自带的单元测试框架,用于编写和运行可重复的测试,主要用于白盒测试和回归测试
特点
(1)让测试具有持久性,测试与开发同步进行,测试代码与开发代码一并发布;
(2)可以是测试代码与产品代码分离;
(3)针对某一个类的测试代码只需少量改动就可以应用与另一个类的测试;
(4)使用断言方法判断期望值与实际值的差异,返回bool值;
(5)测试驱动设备可以使用共同的处理化变量或实例;
(6)测试包结构便于组织集成运行。
要求
unittest 要求单元测试类必须继承 unittest.TestCase,该类中的测试方法需要满足如下要求:测试方法应该没有返回值。测试方法不应该有任何参数。测试方法应以test 开头。
基本模板
#导入 unittest 这个模块
import unittest

#class 这一行是定义一个测试的类,并继承 unittest.TestCase 这个类
class IntegerArithmeticTestCase(unittest.TestCase):
   # 接下来是定义了两个测试case名称: testAdd和testMultiply4、注释里面有句话很重要:
   #test method names begin 'test*'--翻译:测试用例的名称要以 test 开头
    def testAdd(self):  # test method names begin with 'test'
        self.assertEqual((1 + 2), 3)
        self.assertEqual(0 + 1, 1)

##然后是断言 assert,这里的断言方法是 assertEqual-判断两个是否相等,这个断言可以是一个也可以是多个
    def testMultiply(self):
        self.assertEqual((0 * 10), 0)
        self.assertEqual((5 * 8), 40)

#if 下面的这个 unittest.main()是运行主函数,运行后会看到测试结果
if __name__ == '__main__':
    unittest.main()
前置条件和后置条件

1、setUp:在写测试用例的时候,每次操作其实都是基于打开浏览器输入对应网址这些操作,这个就是执行用例的前置条件。
2、tearDown:执行完用例后,为了不影响下一次用例的执行,一般有个数据还原的过程,这就是执行用例的后置条件。
3、很多小伙伴执行完用例,都不去做数据还原,以致于下一个用例执行失败,这就是典型的自己给自己挖坑埋自己,自己坑自己,习惯不好。
4、前置和后置都是非必要的条件,如果没有也可以写 pass

def setUP(self):
    pass
def tearDown(self):
    pass
11 import unittest
12 
13 #4.前置、后置 和运行测试
14 class Test(unittest.TestCase):
15 
16     def setUp(self):
17         pass                  #如果没有可以不写或者pass代替
18 
19     def tearDown(self):
20         pass
21 
22     def testSubtract(self):  # test method names begin with 'test'
23         result = 6-5   #实际结果
24         hope = 1       #期望结果
25         self.assertEqual(result, hope)
26 
27     def testDivide(self):
28         result = 7 / 2  # 实际结果
29         hope = 3.5  # 期望结果
30         self.assertEqual(result, hope)
31 
32 if __name__ == '__main__':
33     unittest.main()

unittest模块的属性说明

unittest的属性:

unittest.TestCase**:TestCase类,所有测试用例类继承的基本类。
          class BaiduTest(unittest.TestCase):
unittest.main():
       可以方便的将一个单元测试模块变为可直接运行的测试脚本,
       main()方法使用TestLoader类来搜索所有包含在该模块中以“test”命名开头的测试方法,并自动执行他们。
       执行方法的默认顺序是:根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。所以以A开头的测试用例方法会优先执行,以a开头会后执行。

unittest.TestSuite():unittest框架的TestSuite()类是用来创建测试套件的。

unittest.TextTextRunner():
      unittest框架的TextTextRunner()类,通过该类下面的run()方法来运行suite所组装的测试用例,入参为suite测试套件。

unittest.defaultTestLoader():
       defaultTestLoader()类,通过该类下面的discover()方法可自动更具测试目录start\_dir匹配查找测试用例文件(test\*.py),并将查找到的测试用例组装到测试套件,因此可以直接通过run()方法执行discover。用法如下:
       discover=unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')

unittest.skip():装饰器,当运行用例时,有些用例可能不想执行等,可用装饰器暂时屏蔽该条测试用例。一种常见的用法就是比如说想调试某一个测试用例,想先屏蔽其他用例就可以用装饰器屏蔽。

   @unittest.skip(reason): skip(reason)装饰器:无条件跳过装饰的测试,并说明跳过测试的原因。

   @unittest.skipIf(reason): skipIf(condition,reason)装饰器:条件为真时,跳过装饰的测试,并说明跳过测试的原因。

   @unittest.skipUnless(reason): skipUnless(condition,reason)装饰器:条件为假时,跳过装饰的测试,并说明跳过测试的原因。

   @unittest.expectedFailure(): expectedFailure()测试标记为失败。

TestCase类的属性如下:

setUp():
    用于测试用例执行前的初始化工作。如测试用例中需要访问数据库,可以在setUp中建立数据库连接并进行初始化。如测试用例需要登录web,可以先实例化浏览器。

tearDown():
   用于测试用例执行之后的善后工作。如关闭数据库连接。关闭浏览器。

assert*():一些断言方法:在执行测试用例的过程中,最终用例是否执行通过,是通过判断测试得到的实际结果和预期结果是否相等决定的。
##!!注意,去掉\[ \]
    assertEqual(a,b,\[msg='测试失败时打印的信息'\]):断言a和b是否相等,相等则测试用例通过。

    assertNotEqual(a,b,\[msg='测试失败时打印的信息'\]):断言a和b是否相等,不相等则测试用例通过。

    assertTrue(x,\[msg='测试失败时打印的信息'\]):断言x是否True,是True则测试用例通过。

    assertFalse(x,\[msg='测试失败时打印的信息'\]):断言x是否False,是False则测试用例通过。

    assertIs(a,b,\[msg='测试失败时打印的信息'\]):断言a是否是b,是则测试用例通过。

    assertNotIs(a,b,\[msg='测试失败时打印的信息'\]):断言a是否是b,不是则测试用例通过。

    assertIsNone(x,\[msg='测试失败时打印的信息'\]):断言x是否None,是None则测试用例通过。

    assertIsNotNone(x,\[msg='测试失败时打印的信息'\]):断言x是否None,不是None则测试用例通过。

    assertIn(a,b,\[msg='测试失败时打印的信息'\]):断言a是否在b中,在b中则测试用例通过。

    assertNotIn(a,b,\[msg='测试失败时打印的信息'\]):断言a是否在b中,不在b中则测试用例通过。

    assertIsInstance(a,b,\[msg='测试失败时打印的信息'\]):断言a是是b的一个实例,是则测试用例通过。

    assertNotIsInstance(a,b,\[msg='测试失败时打印的信息'\]):断言a是是b的一个实例,不是则测试用例通过。

TestSuite类的属性如下:(组织用例时需要用到)
addTest(): addTest()方法是将测试用例添加到测试套件中,如下方,是将test_baidu模块下的BaiduTest类下的test_baidu测试用例添加到测试套件。

suite = unittest.TestSuite() suite.addTest(test\_baidu.BaiduTest('test\_baidu'))

TextTextRunner的属性如下:(组织用例时需要用到)
run(): run()方法是运行测试套件的测试用例,入参为suite测试套件。

runner = unittest.TextTestRunner()
runner.run(suite)
如何控制unittest用例执行的顺序呢?
方式1,通过TestSuite类的addTest方法,按顺序加载测试用例
12 #4.执行顺序和运行测试
13 import unittest
14 
15 class TestLogin(unittest.TestCase):
16 
17     def setUp(self):
18         pass
19     def test_login_blog(self):
20         """登录博客园
21 
22         :return:
23         """
24         print("登录博客园")
25     def test_add_essay(self):
26         """ 添加随笔
27 
28         :return:
29         """
30         print("添加随笔")
31     def test_release_essay(self):
32         """ 发布随笔
33 
34         :return:
35         """
36         print("发布随笔")
37     def test_quit_blog(self):
38         """退出博客园
39 
40         :return:
41         """
42         print("退出博客园")
43 
44     def tearDown(self):
45         pass
46 if __name__ == '__main__':
47     # 启动单元测试
48     # unittest.main()
49 
50     # 获取TestSuite的实例对象
51     suite = unittest.TestSuite()
52 
53     # 将测试用例添加到测试容器中
54     suite.addTest(TestLogin('test_login_blog'))
55     suite.addTest(TestLogin('test_add_essay'))
56     suite.addTest(TestLogin('test_release_essay'))
57     suite.addTest(TestLogin('test_quit_blog'))
58 
59     # 创建TextTestRunner类的实例对象
60     runner = unittest.TextTestRunner()
61     runner.run(suite)
62     #unittest.TextTestRunner(verbosity=3).run(suite)
方式2,通过修改函数名的方式
11 import unittest
12 #4.执行顺序和运行测试
13 import unittest
14 
15 class TestLogin(unittest.TestCase):
16 
17     def setUp(self):
18         pass
19     def test_1_login_blog(self):
20         """登录博客园
21 
22         :return:
23         """
24         print("登录博客园")
25     def test_2_add_essay(self):
26         """ 添加随笔
27 
28         :return:
29         """
30         print("添加随笔")
31     def test_3_release_essay(self):
32         """ 发布随笔
33 
34         :return:
35         """
36         print("发布随笔")
37     def test_4_quit_blog(self):
38         """退出博客园
39 
40         :return:
41         """
42         print("退出博客园")
43 
44     def tearDown(self):
45         pass
46 if __name__ == '__main__':
47     # 启动单元测试
48     unittest.main()

Pytest单元测试框架

什么是Pytest?

官方文档

一个非常成熟的 Python 测试框架,可以做到做个场景的测试工作,如:单元测试、接口测试、web测试等。
pytest兼容unittest测试用例,但是反过来unittest不兼容pytest,
是一个插件化平台,丰富的插件扩展增强了它的功能,也可以根据自己的需要定制化开发自己的插件,非常的灵活
demo
import pytest


class TestClass(object):
    
    def test_one(self):
        print("one case...\n")
        x = "test"
        assert 'e' in x
        
    def test_two(self):
        print("two case...\n")
        x = "hello"
        assert hasattr(x, 'check')
     
     
if __name__ == "__main__":
    pytest.main()

Allure测试报告

Allure概览

什么是Allure
由Qameta Software团队开源的一款旨在于解决让每个人能更容易生成并更简洁阅读的测试报告框架。它支持大多数的测试框架,如:Pytest、TestNG等,简单易用便于集成。
环境配置

官网地址,注意一定别选最新的,还有项目名千万别以pytest开头
Allure要生效需要在测试文件和测试通配文件(conftest.py)中配置 allure。

pip3 install pytest
pip3 install allure-pytest
前提配置Java1.8+的环境.
官网下载解压,配置Path,变量为安装文件的allure.bat文件.
在cmd中输入allure,查看是否安装成功.
模板案例

import pytest
import allure


# allure.feature 定义功能
@allure.feature("报告购物车")
class TestAllure(object):
    # 定义用户场景
    @allure.story("加入购物车")
    def test_add_goods_cart(self):
        # 调用步骤函数
        login("crisimple", "123456")

        # 将测试用例分成几个步骤,将测试步骤打印到测试报告中,步骤二
        with allure.step("浏览商品"):
            # allure.attach--打印一些附加信息
            allure.attach("商品1", "C")
            allure.attach("商品2", "C")

        # 步骤三
        with allure.step("加入商品"):
            allure.attach("商品1", 2)
            allure.attach("商品2", 3)

        # 步骤四
        with allure.step("校验商品"):
            allure.attach("商品1加入成功", "共2个")
            allure.attach("商品2加入失败", "共0个")

    @allure.story("继续购物")
    def test_continue_shopping_cart(self):
        login("crisimple", "123456")
        allure.attach("商品3", 4)
        print("继续购物成功")

    @allure.story("减少商品失败")
    def test_edit_shopping_cart(self):
        login("crisimple", "123")
        assert 0

    @pytest.mark.skip(reason="删除购物车不执行")
    @allure.story("删除购物车")
    def test_delete_shopping_cart(self):
        login("crisimple", "123")
        print()


# 将函数作为一个步骤,调用此函数时,报告中输出一个步骤,步骤名称通常时函数名,这样的函数通常称为步骤函数
@allure.step("用户登录")
def login(user, passwd):
    if user == "crisimple" and passwd == "123456":
        print(user, passwd)
        print("登录成功")
    else:
        print(user, passwd)
        print("登录失败,请重新尝试")

Allure生成测试报告
# 在文件地址打开cmd,假设文件名为test_demo.py,生成json格式运行结果
pytest --alluredir=report test_demo.py
$命令中的 --alluredir=report 指明了生成的json结果文件存放的目录为当前目录下的report文件夹

# 使用`allure`生成最终的测试报告
allure generate report

#实际内容需要 `allure` 进行渲染后才能看到
allure open allure-report
Allure常用注解
Feature: 标注主要功能模块

Story: 标注Features功能模块下的分支功能

Severity: 标注测试用例的重要级别

Step: 标注测试用例的重要步骤

Issue和TestCase: 标注Issue、Case,可加入URL
Allure支持Jenkins Plugin

参考
参考

参考

(1) 掘金博主
(2) unittest官方文档链接:中文文档,英文文档
(3) 实战接口项目
(4) 掘金博客_自动化测试**


SoapEye
89 声望6 粉丝

历史就是历史,它是客观存在的。


引用和评论

0 条评论