python+selenium自动化软件测试

1.1 环境搭建

1.1.1 selenium简介
Selenium 是用于测试 Web 应用程序用户界面 (UI) 的常用框架。它是一款用于运行端到端功能测试的超强工具。您可以使用多个编程语言编写测试,并且 Selenium 能够在一个或多个浏览器中执行这些测试。
Selenium的发展经历了三个阶段,第一个阶段,也就是selenium1的时代,在运行selenium1.0程序之前,我们得先启动selenium server端(selenium remote control),我们简称RC。RC主要包括三个部分:launcher,http proxy,selenium core。其中selenium core是被selenium server嵌入到浏览器页面中的,selenium core内部是一堆javascript函数构成,通过调用这些函数来实现对浏览器的各种操作。
很显然比较繁琐,这并不是最佳自动化解决方案,于是后来有了webdriver。
selenium2 的时代合并了webdriver,也就是我们通常说的selenium,selenium2是默认支持Firefox浏览器的,这点非常方便。

当然也支持其他更多浏览器,Ie和chrome浏览器需要下载驱动包,并添加到环境变量下。

selenium3 是2016年10月份发布的,并且现在默认安装都是selenium3了,selenium3在selenium2的基础上做了一些调整,最明显的区别 就是 selenium2对Firefox的支持最高只支持46及以下版本。selenium3可以支持47以上版本,但是需要下载 geckodriver.exe驱动,并添加到环境变量path下。


接下来的内容以selenium2为主。


************环境组合**************************
初学者最佳环境:python2.7+selenium2+Firefox46以下版本
喜欢尝新的环境:python3.6+selenium3+Firefox47以上版本
*********************************************
小编的环境:
windows10 64位

python 2.7.12

selenium 2.53.6

firefox 44

 

1.1.2 python安装
1.小编的电脑操作系统:win10 64位系统
2.下载Python安装包,选择2.7版本和3.6版本都可以
(下面的教程,两个版本会一起讲,所以不用担心版本问题)
官网下载地址:https://www.python.org/

 

 3.Python安装,双击傻瓜式安装(别安装在c盘,用英文路径,不要有空格),安装时候选中下方红色框框,如果这一步成功,1.1.3环境变量这一步可以省略。

 

1.1.3 环境变量
1.安装完成后,看下这个目录D:\python\Scripts,有没pip.exe和easy_install.exe(一般都有,没有的话得卸载重新安装一次了)

2.将D:\python和D:\python\Scripts(注意这两个是复制自己电脑上的路径),添加到环境变量path下。
D:\python;D:\python\Scripts;(注意带上英文的分号)

 

 

1.1.4 安装selenium
  1.打开cmd窗口输入:pip
(如果出现其它提示请检查上面几步,确认无误后出现Did not provide a command,看1.3解决pip异常这篇)

 

  2.cmd输入指令安装在线安装selenium
>pip install selenium==2.53.6
(注意:首次装一定要看到进度100%完成,如果中途失败了,重新输入指令安装,直到看到100%完成为止)

 

 

1.1.5 验证selenium
 如何才能知道selenium正确安装好了呢?
 1.确保电脑上安装了Firefox浏览器46以下版本
 cmd窗口输入如下指令
  >python
  >from selenium import webdriver
  >webdriver.Firefox()
 3.如果能启动浏览器,说明环境安装OK。

(启动不成功,说明没认真看文档,卸载了重新来一次)
1.1.6 浏览器
  1.如果你打算用Firefox浏览器,那么千万别安装47以上版本(selenium2不兼容47以上)
  2.如果你打算用Ie或Chrome浏览器,需要先下载浏览器驱动,将驱动文件放到python根目录。

 

如果有的已经安装过3.0的版本,启动firefox时候会报错,下一章讲如何使用pip降级selenium版本
1.1.7 firefox历史版本
firefox历年版本的官方镜像地址:
https://download-installer.cdn.mozilla.net/pub/firefox/releases/ 

1.2 pip降级selenium3.0

selenium版本安装后启动Firefox出现异常:'geckodriver' executable needs to be in PATH
selenium默默的升级到了3.0,然而网上的教程都是基于selenium2的,最近有不少小伙伴踩坑了,决定有必要出这一篇,帮助刚入门的小伙伴们解决好环境问题。
selenium+python环境搭配:
selenium2+firefox46以下版本(无需驱动包,firefox喜欢偷偷升级,你懂的)
selenium3+firefox46以上版本(必须下载驱动:geckodriver.exe,且添加到环境变量)
1.2.1 遇到问题
    1.安装完selenium后,再cmd进入python环境
    2.从selenium导入webdriver

    3.启动Firefox浏览器
>>python
>>from selenium import webdriver
>>webdriver.Firefox()
然后出现以下异常:'geckodriver' executable needs to be in PATH

 

1.2.2 解决方案
    1.'geckodriver' executable needs to be inPATH,这句话意思就是说,geckodriver.exe的驱动文件需要添加到环境变量下,

selenium2是默认支持firefox的,不需要驱动包,但是,selenium3需要驱动包的支持了,于是就有了上面的问题
   2.解决办法一:继续使用selenium3,去下载驱动包,然后加到环境变量下(不推荐此办法,因为解决完这个问题后,后面还会接着有其它问题)
   3.解决办法二:selenium3降级到selenium2(接下来会介绍)
 
1.2.3 检查pip环境
    1.打开cmd,输入pip,检查pip环境是否正常
>>pip

    2.如果输入pip出现提示:Did not provide a command 说明pip环境有问题,临时解决办法,输入pip时候加上后缀pip.exe就可以了,具体原因看下一篇解决办法。
1.2.4 pip查看selenium版本号
    1.打开cmd,输入pip show selenium
>>pip show selenium
   2.看红色区域位置版本号显示:2.53.0,显示的就是当前使用的版本号

(如果你这里显示的是3.0开头,就需要接下来步骤了)

 

 

1.2.5 pip降级selenium
    1.为了避免与之前安装的selenium版本冲突,先找到selenium3.0目录:python\Lib\site-packages目录
把里面selenium开头的文件全部删除就可以了。python所有的第三方包都在这个目录下面。

 

     2.打开cmd,输入pip install selenium==2.53.6(注意是两个==,中间不要留空格,这里推荐2.53.6的版本)
>>pip install selenium==2.53.6

 

1.2.6 升级pip版本
    1.在使用pip过程中如果出现下方红色区域字样,就是说pip版本过低了,建议升级
    2.如何升级pip呢?看最后一句话:python -m pip install --upgrade pip

 

   3.把上面对应的提示照着敲一遍就可以了

 

1.3 解决pip使用异常问题

1.3.1 pip出现异常
有一小部分童鞋在打开cmd输入pip后出现下面情况:Didnot provide a command

Did not provide a command?这是什么鬼?

正常情况应该是酱紫

 

1.3.2 解决办法
1.pip是一个.exe的可执行文件,在cmd输入pip.exe就可以解决了。

 

2.所以在后面的安装指令中都需要带上后缀,那么问题来了,为什么会出现这种情况,如何彻底解决?
1.3.3 配置环境变量
1.主要原因是环境变量的PATHEXT里面缺少.EXE的文件名
2.在PATHEXT下编辑后面加上;.EXE(注意分号是英文的)

 

3.环境变量配置好了后,关掉cmd,重新打开输入pip试试(要是这一步还不能解决,继续往下看)
1.3.4 必杀技
1.找到pip所在的文件目录打开

 

2.在文件夹地址栏输入cmd,回车。

 然后在打开的窗口输入pip(或pip.exe)试试吧

1.3.5 绝杀技能
打开cmd(快捷键:win+r),cd到pip所在的目录,如D:\test\python2\Scripts
>d:
>cd d:/test/python2/Scripts
>pip

 

 (要是看到这里,还没解决,你这台电脑可以砸了!!!)

1.4 Chrome浏览器

前言
selenium2启动Chrome浏览器是需要安装驱动包的,但是不同的Chrome浏览器版本号,对应的驱动文件版本号又不一样,如果版本号不匹配,是没法启动起来的。
 #############最佳环境搭配#####################
小编环境:selenium2.53.6+Chrome版本V49.0+chromedriverv2.22
(根据小编经验selenium2搭配Chrome版本40-50比较好,版本太高了会各种奇葩问题的)
###########################################
1.4.1 Chrome遇到问题
1.如果在启动chrome浏览器时候,出现如下界面,无法打开网址,那么首先恭喜你,踩到了坑,接下来的内容或许对你有所帮助。

>># coding:utf-8
>>from selenium import webdriver
>>driver = webdriver.Chrome()
>>driver.get("http://www.cnblogs.com/yoyoketang/")

 

1.4.2 查看版本号

1.查看Chrome版本号,设置>关于,查出来版本号是49.0

 

2.查看chromedriver.exe版本号,双击这个文件就可以了,查出来版本号是V2.9

3.很显然是chromedriver的版本号过低了,于是可以找个更高级的版本:V2.22
 
1.4.3 chromedriver
1.确保chromedriver.exe文件在path路径下,这里我放到Python的根目录了(python根目录已配置到path了)

 

2.确保驱动文件名称是chromedriver.exe,如果名称后面带版本号的,改下文件名称就行。
3.Chrome版本V49.0+chromedriverv2.22
 
1.4.4 各版本匹配表

chromedriver版本    支持的Chrome版本
v2.29                 v56-58
v2.28                 v55-57
v2.27                 v54-56
v2.26                 v53-55
v2.25                 v53-55
v2.24                 v52-53
v2.23                 v51-53
v2.22                 v49-52
v2.21                 v46-50
v2.20                 v43-48
v2.19                 v43-47

v2.18                 v43-46
v2.17                 v42-43
v2.13                 v42-45
v2.15                 v40-43
v2.14                 v39-42
v2.13                v38-41
v2.12                v36-40
v2.11                v36-40
v2.10                v33-36
v2.9                  v31-34
v2.8                  v30-33
v2.7                  v30-33

v2.6                  v29-32
v2.5                  v29-32
v2.4                  v29-32

Chrome浏览器的chromedriver版本驱动大全,下载地址:
http://chromedriver.storage.googleapis.com/index.html

IE浏览器的IEdriver版本驱动大全,下载地址:
http://selenium-release.storage.googleapis.com/index.html

 

 

1.5 pycharm使用

前言    
在写脚本之前,先要找个顺手的写脚本工具。python是一门解释性编程语言,所以一般把写python的工具叫解释器。写python脚本的工具很多,小编这里就不一一列举的,只要自己用着顺手就可以的,如果你还没有选好解释器,小编这里推荐pycharm。
     在安装pycharm后,有一些小伙伴不会破解,这里小编还是推荐大家买正版的。当然,如果你不想付费,想破解pycharm,也是很容易的事情,这里小编列举几种破解办法。前提是你要先下载pycharm安装包,安装包可以去官网http://www.jetbrains.com/pycharm/下载最新版。
1.5.1 pycharm安装
方法一:
    1.在注册界面,选择License serve。填入http://idea.lanyus.com/71 
    2.点击ok

方法二:
    1.注册界面选择:Activationcoede
    2.打开网址:http://idea.lanyus.com/71,点击“获取注册码”按钮
    3.复制弹出框的注册码
    4.copy到注册界面Activationcoede位置

 

 

方法三:
    1.安装pycharm在注册界面先别动
    2.调整电脑系统时间到2036年(20年应该够用了)。
    3.注册界面选择申请30天试用
    4.退出pycharm

    5.电脑时间调整回来。

方法四:
    1.安装pycharm在注册界面,选择使用30天
    2.打开pycharm菜单里Help>Register

    3.打开网址:http://idea.lanyus.com/71,点击“获取注册码”按钮
    4.copy到注册界面Activationcoede位置

 

接下来开始pycharm之旅吧~


1.5.2 新建工程

    1.在d盘新建一个test文件夹
    2.打开pycharm左上角File按钮
    3.点NewProject新建一个工程

 

1.5.3 新建脚本
    1.在pycharm左侧菜单树右键,新建一个Directory(文件夹)。
    2.选择对应文件夹,在文件夹中新建Python File(脚本文件)。
    3.脚本名称自己命名,后缀.py会自动带出

 


 1.5.4 开始编程
    1.双击打开需要编写的脚本
    2.在右侧编辑框输入:print("hello world!")
    3.点脚本的title,右击后选择Run“test01”,运行结果如下

 

1.6 selenium3+firefox环境搭建

有不少小伙伴在安装selenium环境后启动firefox报错,因为现在selenium升级到3.0了,跟2.0的版本还有有一点区别的。
(备注:这里不建议大家用selenium3,坑太多,如果selenium2实在用不了,那就看这篇吧)
安装环境过程中主要会遇到三个坑:
1.'geckodriver' executable needs to be in PATH
2.Expected browser binary location, but unable to find binary in default location
3.Unsupported Marionette protocol version 2, required 3
环境准备:
--python3.6

--selenium3.0
--firefox50
 
一、安装python
1.安装python这个简单,下载版本后傻瓜式安装就行了。
2.安装好之后,看下这个目录D:\python\Scripts,有没pip.exe和easy_install.exe(一般都有,没有的话得重新安装一次了)
3.将D:\python和D:\python\Scripts,添加到环境变量path下

 

二、检查pip工具
1.打开cmd,输入:pip,出现如下图界面,说明pip环境OK.
>>pip

 

三、安装selenium3.0
1.cmd输入:pip install selenium
>>pip install selenium
2.首次安装要看到100%完成,中途失败就重新多输入几次安装。

 

四、检查selenium环境

1.在cmd输入如下指令检查环境
>>python
>>from selenium import webdriver
>>driver=webdriver.Firefox()
>>driver.get("https://www.baidu.com")
2.能看到浏览器正常启动,说明环境OK,如果遇到异常就继续看下面解决方案。

 

五、遇到第一个坑:'geckodriver' executable needs to be in PATH
1.如果启动浏览器过程中报如下错误
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\test\python3\lib\site-packages\selenium\webdriver\firefox\webdriver.py", line 145, in __init__
    self.service.start()
  File "D:\test\python3\lib\site-packages\selenium\webdriver\common\service.py", line 81, in start
    os.path.basename(self.path), self.start_error_message)
selenium.common.exceptions.WebDriverException: Message: 'geckodriver' executable needs to be in PATH.
2.这个是因为最新的selenium3.0启动firefox需要geckodriver.exe这个驱动文件。

3.下载之后,配置到环境变量path下(可以直接放python根目录)
 
六、遇到第二坑:Expected browser binary location, but unable to find binary in default location
1.如果启动浏览器过程中报如下错误:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\test\python3\lib\site-packages\selenium\webdriver\firefox\webdriver.py", line 155, in __init__
    keep_alive=True)
  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 92, in __init__
    self.start_session(desired_capabilities, browser_profile)

  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 179, in start_session
    response = self.execute(Command.NEW_SESSION, capabilities)
  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 238, in execute
    self.error_handler.check_response(response)
  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 193, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: Expected browser binary location, but unable to find binary in default location,
no 'moz:firefoxOptions.binary' capability provided, and no binary flag set on the command line.

2.这个是因为firefox.exe这个文件也需要配置到环境变量path下。
3.这个路径就是安装完firefox后,找到firefox.exe这个文件的地址,加到path下。

 

七、遇到第三坑:Unsupported Marionette protocol version 2, required 3
1.如果启动浏览器过程中出现如下错误
 Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

  File "D:\test\python3\lib\site-packages\selenium\webdriver\firefox\webdriver.py", line 155, in __init__
    keep_alive=True)
  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 92, in __init__
    self.start_session(desired_capabilities, browser_profile)
  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 179, in start_session
    response = self.execute(Command.NEW_SESSION, capabilities)
  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 238, in execute
    self.error_handler.check_response(response)
  File "D:\test\python3\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 193, in check_response

    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: Unsupported Marionette protocol version 2, required 3
2.这个错误原因是firefox版本过低了,最新的selenium3.0版本支持firefox47以上的版本,升级版本就可以了

 

 总结:整个环境的配置是python3.6+selenium3.0+firefox47以上版本,当然python用2.7版本也是可以的。


2.1 操作元素基本方法

前言
前面已经把环境搭建好了,从这篇开始,正式学习selenium的webdriver框架。我们平常说的 selenium自动化,其实它并不是类似于QTP之类的有GUI界面的可视化工具,我们要学的是webdriver框架的API。
本篇主要讲如何用Python调用webdriver框架的API,对浏览器做一些常规的操作,如打开、前进、后退、刷新、设置窗口大小、截屏、退出等操作。

2.1.1 打开网页

1.从selenium里面导入webdriver模块
2.打开Firefox浏览器(Ie和Chrome对应下面的)
3.打开百度网址

2.1.2 设置休眠

1.由于打开百度网址后,页面加载需要几秒钟,所以最好等到页面加载完成后再继续下一步操作
2.导入time模块,time模块是Python自带的,所以无需下载
3.设置等待时间,单位是秒(s),时间值可以是小数也可以是整数

2.1.3 页面刷新

1.有时候页面操作后,数据可能没及时同步,需要重新刷新
2.这里可以模拟刷新页面操作,相当于浏览器输入框后面的刷新按钮

2.1.4 页面切换

1.当在一个浏览器打开两个页面后,想返回上一页面,相当于浏览器左上角的左箭头按钮。

2.返回到上一页面后,也可以切换到下一页,相当于浏览器左上角的右箭头按钮。

2.1.5 设置窗口大小

1.可以设置浏览器窗口大小,如设置窗口大小为手机分辨率540*960
2.也可以最大化窗口

 2.1.6 截屏

1. 打开网站之后,也可以对屏幕截屏
2.截屏后设置指定的保存路径+文件名称+后缀

2.1.7 退出

1.退出有两种方式,一种是close;另外一种是quit。
2.close用于关闭当前窗口,当打开的窗口较多时,就可以用close关闭部分窗口。
3.quit用于结束进程,关闭所有的窗口。
4.最后结束测试,要用quit。quit可以回收c盘的临时文件。

掌握了浏览器的基本操作后,接下来就可以开始学习元素定位了,元素定位需要有一定的html基础。没有基础的可以按下浏览器的F12快捷键先看下html的布局,先了解一些就可以了。

2.1.8 加载浏览器配置

启动浏览器后,发现右上角安装的插件不见了,这是因为webdriver启动浏览器时候,是开的一个虚拟线程,跟手工点开是有区别的,selenium的一切操作都是模拟人工(不完全等于人工操作)。

加载Firefox配置

   有小伙伴在用脚本启动浏览器时候发现原来下载的插件不见了,无法用firebug在打开的页面上继续定位页面元素,调试起来不方便 。加载浏览器配置,需要用FirefoxProfile(profile_directory)这个类来加载,profile_directory既为浏览器配置文件的路径地址。

一、遇到问题
1.在使用脚本打开浏览器时候,发现右上角原来下载的插件firebug不见了,到底去哪了呢?
2.用脚本去打开浏览器时候,其实是重新打开了一个进程,跟手动打开浏览器不是一个进程。
所以没主动加载插件,不过selenium里面其实提供了对应的方法去打开,只是很少有人用到。

 

 

二、FirefoxProfile
1.要想了解selenium里面API的用法,最好先看下相关的帮助文档打开cmd窗口,
输入如下信息:

->python
->from selenium import webdriver
->help(webdriver.FirefoxProfile)

Help on class FirefoxProfile in module
selenium.webdriver.firefox.firefox_profile:
class FirefoxProfile(builtin.object)
|  Methods defined here:

|
|  init(self, profile_directory=None)
|      Initialises a new instance of a Firefox Profile
|    
|      :args:
|       - profile_directory: Directory of profile that you want to use.
|         This defaults to None and will create a new
|         directory when object is created.

2.翻译过来大概意思是说,这里需要profile_directory这个配置文件路径的参数
3.profile_directory=None,如果没有路径,默认为None,启动的是一个新的,有的话就加载指定的路径。

三、profile_directory
1.问题来了:Firefox的配置文件地址如何找到呢?
2.打开Firefox点右上角设置>?(帮助)>故障排除信息>显示文件夹

3.打开后把路径复制下来就可以了:
C:\Users\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default

 

四、启动配置文件
1.由于文件路径存在字符:\ ,反斜杠在代码里是转义字符,这个有点代码基础的应该都知道。
不懂什么叫转义字符的,自己翻书补下基础吧!
2.遇到转义字符,为了不让转义,有两种处理方式:
第一种:\ (前面再加一个反斜杠)

第二种:r”\"(字符串前面加r,使用字符串原型)

 

五、参考代码:

# coding=utf-8
from selenium import webdriver
# 配置文件地址
profile_directory = r'C:\Users\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default'

# 加载配置配置
profile = webdriver.FirefoxProfile(profile_directory)
# 启动浏览器配置
driver = webdriver.Firefox(profile)

 其实很简单,在调用浏览器的前面,多加2行代码而已,主要是要弄清楚原理。

 

2.2 常用8种元素定位(Firebug和firepath)

前言   
元素定位在firefox上可以安装Firebug和firepath辅助工具进行元素定位。


2.2.1 环境准备

1.浏览器选择:Firefox
2.安装插件:Firebug和FirePath(设置》附加组件》搜索:输入插件名称》下载安装后重启浏览器)
3.安装完成后,页面右上角有个小爬虫图标
4.快速查看xpath插件:XPath Checker这个可下载,也可以不用下载
5.插件安装完成后,点开附加组件》扩展,如下图所示

 

2.2.2 查看页面元素

以百度搜索框为例,先打开百度网页
1.点右上角爬虫按钮
2.点左下角箭头
3.将箭头移动到百度搜索输入框上,输入框高亮状态
4.下方红色区域就是单位到输入框的属性:

<input id="kw" class="s_ipt" type="text" autocomplete="off" maxlength="100" name="wd">

2.2.3 find_element_by_id()

1.从上面定位到的元素属性中,可以看到有个id属性:id="kw",这里可以通过它的id属性定位到这个元素。
2.定位到搜索框后,用send_keys()方法,输入文本。

2.2.4 find_element_by_name() 

   1.从上面定位到的元素属性中,可以看到有个name属性:name="wd",这里可以通过它的name属性单位到这个元素。
    说明:这里运行后会报错,说明这个搜索框的name属性不是唯一的,无法通过name属性直接定位到输入框

2.2.5 find_element_by_class_name()

1.从上面定位到的元素属性中,可以看到有个class属性:class="s_ipt",这里可以通过它的class属性定位到这个元素。

2.2.6 find_element_by_tag_name()

1.从上面定位到的元素属性中,可以看到每个元素都有tag(标签)属性,如搜索框的标签属性,就是最前面的input。
2.很明显,在一个页面中,相同的标签有很多,所以一般不用标签来定位。以下例子,仅供参考和理解,运行肯定报错。

 2.2.7 find_element_by_link_text()

1.定位百度页面上"hao123"这个按钮

 

查看页面元素:

<a class="mnav" target="_blank" href="http://www.hao123.com">hao123</a>

2.从元素属性可以分析出,有个href = "http://www.hao123.com

说明它是个超链接,对于这种元素,可以用以下方法:

 2.2.8 find_element_by_partial_link_text()

1.有时候一个超链接它的字符串可能比较长,如果输入全称的话,会显示很长,这时候可以用一模糊匹配方式,截取其中一部分字符串就可以了

2.如“hao123”,只需输入“ao123”也可以定位到

2.2.9 find_element_by_xpath()

1.以上定位方式都是通过元素的某个属性来定位的,如果一个元素它既没有id、name、class属性也不是超链接,这么办呢?或者说它的属性很多重复的。这个时候就可以用xpath解决。
2.xpath是一种路径语言,跟上面的定位原理不太一样,首先第一步要先学会用工具查看一个元素的xpath。

 

 3.按照上图的步骤,在FirePath插件里copy对应的xpath地址。

 2.2.10 find_element_by_css_selector()

1.css是另外一种语法,比xpath更为简洁,但是不太好理解。这里先学会如何用工具查看,后续的教程再深入讲解
2.打开FirePath插件选择css
3.定位到后如下图红色区域显示

 

总结:
selenium的webdriver提供了18种(注意是18种,不是8种)的元素定位方法,前面8种是通过元素的属性来直接定位的,后面的xpath和css定位更加灵活,需要重点掌握其中一个。
前八种是大家都熟悉的,经常会用到的:

1.id定位:find_element_by_id(self, id_)
2.name定位:find_element_by_name(self, name)
3.class定位:find_element_by_class_name(self, name)
4.tag定位:find_element_by_tag_name(self, name)
5.link定位:find_element_by_link_text(self, link_text)
6.partial_link定位find_element_by_partial_link_text(self, link_text)
7.xpath定位:find_element_by_xpath(self, xpath)
8.css定位:find_element_by_css_selector(self, css_selector)

这八种是复数形式(2.8和2.27章节有介绍)

9.id复数定位find_elements_by_id(self, id_)
10.name复数定位find_elements_by_name(self, name)
11.class复数定位find_elements_by_class_name(self, name)
12.tag复数定位find_elements_by_tag_name(self, name)
13.link复数定位find_elements_by_link_text(self, text)
14.partial_link复数定位find_elements_by_partial_link_text(self, link_text)
15.xpath复数定位find_elements_by_xpath(self, xpath)
16.css复数定位find_elements_by_css_selector(self, css_selector

这两种是参数化的方法,会在以后搭建框架的时候,会经常用到PO模式,才会用到这个参数化的方法(将会在4.2有具体介绍)

17.find_element(self, by='id', value=None)
18.find_elements(self, by='id', value=None)

2.3 xpath定位

前言    
在上一篇简单的介绍了用工具查看目标元素的xpath地址,工具查看比较死板,不够灵活,有时候直接复制粘贴会定位不到。这个时候就需要自己手动的去写xpath了,这一篇详细讲解xpath的一些语法。
什么是xpath呢?
官方介绍:XPath即为XML路径语言,它是一种用来确定XML文档中某部分位置的语言。反正小编看这个介绍是云里雾里的,通俗一点讲就是通过元素的路径来查找到这个元素的。

2.3.1 xpath:属性定位

1.xptah也可以通过元素的id、name、class这些属性定位,如下图:

2.于是可以用以下xpath方法定位

2.3.2 xpath:其它属性

1.如果一个元素id、name、class属性都没有,这时候也可以通过其它属性定位到

2.3.3 xpath:标签

1.有时候同一个属性,同名的比较多,这时候可以通过标签筛选下,定位更准一点
2.如果不想制定标签名称,可以用*号表示任意标签
3.如果想制定具体某个标签,就可以直接写标签名称

2.3.4 xpath:层级

1.如果一个元素,它的属性不是很明显,无法直接定位到,这时候我们可以先找它老爸(父元素)。
2.找到它老爸后,再找下个层级就能定位到了。

3.如上图所示,要定位的是input这个标签,它的老爸的id=s_kw_wrap。
4.要是它老爸的属性也不是很明显,就找它爷爷id=form。
5.于是就可以通过层级关系定位到。

2.3.5 xpath:索引

1.如果一个元素它的兄弟元素跟它的标签一样,这时候无法通过层级定位到。因为都是一个父亲生的,多胞胎兄弟。
2.虽然双胞胎兄弟很难识别,但是出生是有先后的,于是可以通过它在家里的排行老几定位到。
3.如下图三胞胎兄弟。

 

4.用xpath定位老大、老二和老三(这里索引是从1开始算起的,跟Python的索引不一样)。

2.3.6 xpath:逻辑运算

1.xpath还有一个比较强的功能,是可以多个属性逻辑运算的,可以支持与(and)、或(or)、非(not)
2.一般用的比较多的是and运算,同时满足两个属性

 

2.3.7 xpath:模糊匹配

1.xpath还有一个非常强大的功能,模糊匹配。
2.掌握了模糊匹配功能,基本上没有定位不到的。
3.比如我要定位百度页面的超链接“hao123”,在上一篇中讲过可以通过by_link,也可以通过by_partial_link,模糊匹配定位到。当然xpath也可以有同样的功能,并且更为强大。

可以把xpath看成是元素定位界的屠龙刀。武林至尊,宝刀xpath,css不出,谁与争锋?下节课将亮出倚天剑css定位。

2.4 CSS定位

前言
大部分人在使用selenium定位元素时,用的是xpath定位,因为xpath基本能解决定位的需求。css定位往往被忽略掉了,其实css定位也有它的价值,css定位更快,语法更简洁。
这一篇css的定位方法,主要是对比上一篇的xpath来的,基本上xpath能完成的,css也可以做到。两篇对比学习,更容易理解。
2.4.1 css:属性定位
1.css可以通过元素的id、class、标签这三个常规属性直接定位到
2.如下是百度输入框的的html代码:

<input id="kw" class="s_ipt" type="text" autocomplete="off" maxlength="100" name="wd"/>

3.css用#号表示id属性,如:#kw
4.css用.表示class属性,如:.s_ipt
5.css直接用标签名称,无任何标示符,如:input

2.4.2 css:其它属性

1.css除了可以通过标签、class、id这三个常规属性定位外,也可以通过其它属性定位
2.以下是定位其它属性的格式

 

2.4.3 css:标签

1.css页可以通过标签与属性的组合来定位元素

2.4.4 css:层级关系

1.在前面一篇xpath中讲到层级关系定位,这里css也可以达到同样的效果
2.如xpath:

//form[@id='form']/span/input和
//form[@class='fm']/span/input也可以用css实现

 

 

2.4.5 css:索引

1.以下图为例,跟上一篇一样:

2.css也可以通过索引option:nth-child(1)来定位子元素,这点与xpath写法用很大差异,其实很好理解,直接翻译过来就是第几个小孩。

2.4.6 css:逻辑运算

1.css同样也可以实现逻辑运算,同时匹配两个属性,这里跟xpath不一样,无需写and关键字

 

2.4.7 css:模糊匹配

1.css的模糊匹配contains('xxx'),网上虽然用各种资料显示能用,但是小编亲自试验了下,一直报错。
2.在各种百度后找到了答案:you can't do this withCSS selectors, because there is no such thing as:contains() in CSS. It was a proposal that was abandoned years ago.
非常遗憾,这个语法已经被抛弃了,所以这里就不用管这个语法了。
css语法远远不止上面提到的,还有更多更强大定位策略,有兴趣的可以继续深入研究。官方说法,css定位更快,语法更简洁,但是xpath更直观,更好理解一些。

2.5 SeleniumBuilder辅助定位元素

前言
对于用火狐浏览器的小伙伴们,你还在为定位元素而烦恼嘛?
上古神器Selenium Builder来啦,哪里不会点哪里,妈妈再也不用担心我的定位元素问题啦!(但是也不是万能,基本上都能覆盖到)

2.5.1 安装Selenium Builder

在火狐浏览器的附加组件中搜索添加Selenium Builder即可。安装好后如下图所示:

 

 2.5.2 直接运用

1.打开你要测试的URL或者打开插件后输入你要测试的URL,如下图

2.点击后弹出一个弹窗,如下图:

注:如果你是直接在你要测的网页页面打开这个插件时,selenium builder会直接获取你要测的URL

3.点击record:

然后你就可以哪里不会点哪里了。这里举个例子:

2.5.3 实践案例

1.百度首页,点击百度一下,然后点击登录,再一次点击账号和密码输入框,让我们来看看结果。

2.这里没有展开,点击展开后可以发现定位该元素的多种方法

 

直接选择你想要的方法复制粘贴即可,不用的话直接关掉弹窗即可。

2.6 操作元素(键盘和鼠标事件)

前言
在前面的几篇中重点介绍了一些元素的定位方法,定位到元素后,接下来就是需要操作元素了。本篇总结了web页面常用的一些操作元素方法,可以统称为行为事件
有些web界面的选项菜单需要鼠标悬停在某个元素上才能显示出来(如百度页面的设置按钮)。

2.6.1 简单操作

    1.点击(鼠标左键)页面按钮:click()
    2.清空输入框:clear()
    3.输入字符串:send_keys()
    4.send_keys()如果是发送中文的,前面需加u,如:u"中文",因为这里是输入到windows系统了,windows系统是GBK编码,我们的脚本是utf-8,需要转码为Unicode国际编码,这样才能识别到。

 

2.6.2 submit提交表单

1.在前面百度搜索案例中,输入关键字后,可以直接按回车键搜索,也可以点搜索按钮搜索。
2.submit()一般用于模拟回车键。

 

2.6.3 键盘操作 

    1.selenium提供了一整套的模拟键盘操作事件,前面submit()方法如果不行的话,可以试试模拟键盘事件
    2.模拟键盘的操作需要先导入键盘模块:from selenium.webdriver.common.keysimport Keys
    3.模拟enter键,可以用send_keys(Keys.ENTER)

 

    4.其它常见的键盘操作:
       键盘F1到F12:send_keys(Keys.F1)把F1改成对应的快捷键:

       复制Ctrl+C:send_keys(Keys.CONTROL,'c') 

       粘贴Ctrl+V:send_keys(Keys.CONTROL,'v') 

       全选Ctrl+A:send_keys(Keys.CONTROL,'a') 

       剪切Ctrl+X:send_keys(Keys.CONTROL,'x') 

       制表键Tab:  send_keys(Keys.TAB) 

       这里只是列了一些常用的,当然除了键盘事件,也有鼠标事件。

2.6.4 鼠标悬停事件

    1.鼠标不仅仅可以点击(click),鼠标还有其它的操作,如:鼠标悬停在某个元素上,鼠标右击,鼠标按住某个按钮拖到
    2.鼠标事件需要先导入模块:from selenium.webdriver.common.action_chainsimport ActionChains
        perform() 执行所有ActionChains中的行为;
        move_to_element() 鼠标悬停。
    3.这里以百度页面设置按钮为例:

    4.除了常用的鼠标悬停事件外,还有
       右击鼠标:context_click()
       双击鼠标:double_click()
       依葫芦画瓢,替换上面案例中对应的鼠标事件就可以了
       selenium提供了一整套完整的鼠标和键盘行为事件,功能还是蛮强大滴。下一篇介绍多窗口的情况下如何处理。

2.7 多窗口、句柄(handle)

前言   
有些页面的链接打开后,会重新打开一个窗口,对于这种情况,想在新页面上操作,就得先切换窗口了。获取窗口的唯一标识用句柄表示,所以只需要切换句柄,我们就能在多个页面上灵活自如的操作了。
一、认识多窗口
1.打开赶集网:http://bj.ganji.com/,点击招聘求职按钮会发现右边多了一个窗口标签

 2.我们用代码去执行点击的时候,发现界面上出现两个窗口,如下图这种情况就是多窗口了。

 

   3.到这里估计有小伙伴纳闷了,手工点击是2个标签,怎么脚本点击就变成2个窗口了,这个在2.1里面讲过,脚本执行是不加载配置的,手工点击是浏览器默认设置了新窗口打开方式为标签,这里用鼠标按住点二个标签,拖拽出来,也就变成2个标签了,是一回事。

 

 二、获取当前窗口句柄

    1.元素有属性,浏览器的窗口其实也有属性的,只是你看不到,浏览器窗口的属性用句柄(handle)来识别。

    2.人为操作的话,可以通过眼睛看,识别不同的窗口点击切换。但是脚本没长眼睛,它不知道你要操作哪个窗口,这时候只能句柄来判断了。

    3.获取当前页面的句柄:driver.current_window_handle

三、获取所有句柄
    1.定位赶集网招聘求职按钮,并点击
    2.点击后,获取当前所有的句柄:window_handles

 

 

四、切换句柄

网上大部分教程都是些的第一种方法,小编这里新增一个更简单的方法,直接从获取所有的句柄list里面取值。

方法一(不推荐):

    1.循环判断是否与首页句柄相等

    2.如果不等,说明是新页面的句柄

    3.获取的新页面句柄后,可以切换到新打开的页面上

    4.打印新页面的title,看是否切换成功

方法二:

    1.直接获取all_h这个list数据里面第二个hand的值:all_h[1]

五、关闭新窗口,切回主页
   1.close是关闭当前窗口,因为此时有两个窗口,用close可以关闭其中一个,quit是退出整个进程(如果当前有两个窗口,会一起关闭)。
   2.切换到首页句柄:h
   3.打印当前页面的title,看是否切换到首页了

 

六、参考代码

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://bj.ganji.com/")
h = driver.current_window_handle
print h     # 打印首页句柄
driver.find_element_by_link_text("招聘求职").click()
all_h = driver.window_handles
print all_h        # 打印所有的句柄

# 方法一:判断句柄,不等于首页就切换(不推荐此方法,太繁琐)
# for i in all_h:
#     if i != h:
#         driver.switch_to.window(i)
#         print driver.title
# 方法二:获取list里面第二个直接切换
driver.switch_to.window(all_h[1])
print driver.title
# 关闭新窗口
driver.close()
# 切换到首页句柄
driver.switch_to.window(h)
# 打印当前的title
print driver.title

2.8 定位一组元素elements

前言    
前面的几篇都是讲如何定位一个元素,有时候一个页面上有多个对象需要操作,如果一个个去定位的话,比较繁琐,这时候就可以定位一组对象。
webdriver 提供了定位一组元素的方法,跟前面八种定位方式其实一样,只是前面是单数,这里是复数形式:find_elements


本篇拿百度搜索作为案例,从搜索结果中随机选择一条搜索结果,然后点击查看。

 

一、定位搜索结果
    1.在百度搜索框输入关键字“测试部落”后,用firebug查看页面元素,可以看到这些搜索结果有共同的属性。

    2.从搜索的结果可以看到,他们的父元素一样:<h3 class="t">
    3.标签都一样,且target属性也一样:<a target="_blank" />
    4.于是这里可以用css定位(当然用xpath也是可以的)

 

二、确认定位结果
    1.前面的定位策略只是一种猜想,并不一定真正获取到自己想要的对象的,也行会定位到一些不想要的对象。
    2.于是可以获取对象的属性,来验证下是不是定位准确了。这里可以获取href属性,打印出url地址。

 

三、随机函数
    1.搜索结果有10条,从这10条中随机取一个就ok了
    2.先导入随机函数:import random
    3.设置随机值范围为0~9:a=random.randint(0~9)

 

四、随机打开url
    1.从返回结果中随机取一个url地址
    2.通过get方法打卡url
    3.其实这种方式是接口测试了,不属于UI自动化,这里只是开阔下思维,不建议用这种方法

 

五、通过click点击打开
    1.前面那种方法,是直接访问url地址,算是接口测试的范畴了,真正模拟用户点击行为,得用click的方法

# coding:utf-8
from selenium import webdriver
import random

driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
driver.implicitly_wait(10)
driver.find_element_by_id("kw").send_keys(u"测试部落")
driver.find_element_by_id("kw").submit()
s = driver.find_elements_by_css_selector("h3.t>a")

# 设置随机值
t = random.randint(0, 9)
# 随机取一个结果点击鼠标
s[t].click()

 

不知道有小伙伴有没注意一个细节,前面在搜索框输入关键字后,我并没有去点击搜索按钮,而是用的submit的方法,submit相当于回车键。
具体的操作对象方法,下篇详细介绍。本篇主要学会定位一组对象,然后随机操作其中的一个。

2.9 iframe

一、frame和iframe区别
Frame与Iframe两者可以实现的功能基本相同,不过Iframe比Frame具有更多的灵活性。 frame是整个页面的框架,iframe是内嵌的网页元素,也可以说是内嵌的框架
Iframe标记又叫浮动帧标记,可以用它将一个HTML文档嵌入在一个HTML中显示。它和Frame标记的最大区别是在网页中嵌入 的<Iframe></Iframe>所包含的内容与整个页面是一个整体,而<Frame>< /Frame>所包含的内容是一个独立的个体,是可以独立显示的。另外,应用Iframe还可以在同一个页面中多次显示同一内容,而不必重复这段内 容的代码。

二、案例操作:163登录界面
1.打开http://mail.163.com/登录页面

2.用firebug定位登录框

3.鼠标停留在左下角(定位到iframe位置)时,右上角整个登录框显示灰色,说明iframe区域是整个登录框区域

4.左下角箭头位置显示iframe属性<iframe id="x-URS-iframe" frameborder="0" name="" 

 三、切换iframe
1.由于登录按钮是在iframe上,所以第一步需要把定位器切换到iframe上
2.用switch_to_frame方法切换,此处有id属性,可以直接用id定位切换

 

 

四、如果iframe没有id怎么办?
1.这里iframe的切换是默认支持id和name的方法的,当然实际情况中会遇到没有id属性和name属性为空的情况,这时候就需要先定位iframe元素对象
2.定位元素还是之前的八种方法同样适用,这里我可以通过tag先定位到,也能达到同样效果

 

五、释放iframe
1.当iframe上的操作完后,想重新回到主页面上操作元素,这时候,就可以用switch_to_default_content()方法返回到主页面

 

六、如何判断元素是否在iframe上?
1.定位到元素后,切换到firepath界面
2.看firebug工具左上角,如果显示Top Window说明没有iframe
3.如果显示iframe#xxx这样的,说明在iframe上,#后面就是它的id

 

七、如何解决switch_to_frame上的横线呢?    
1.先找到官放的文档介绍

 

2.python的脚本上面划一横线,是说这个语法已经过时了(也可以继续用,只是有部分人有强迫症)。上面文档介绍说官方已经不推荐上面的写法了,用这个写法就好了driver.switch_to.frame()


八、参考代码如下:

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://mail.163.com/")
driver.implicitly_wait(30)
# 切换iframe
# iframe = driver.find_element_by_tag_name("iframe")
# driver.switch_to_frame(iframe)
# driver.switch_to_frame("x-URS-iframe")
driver.switch_to.frame("x-URS-iframe")
driver.find_element_by_name("email").send_keys("123")
driver.find_element_by_name("password").send_keys("456")
# 释放iframe,重新回到主页面上
driver.switch_to.default_content()

2.10 select下拉框

本篇以百度设置下拉选项框为案例,详细介绍select下拉框相关的操作方法。

一、认识select
    1.打开百度-设置-搜索设置界面,如下图所示

 

 

    2.箭头所指位置,就是select选项框,打开页面元素定位,下方红色框框区域,可以看到select标签属性:                   

<select id="nr" name="NR">

    3.选项有三个。

<option selected="" value="10">每页显示10条</option>
<option value="20">每页显示20条</option>
<option value="50">每页显示50条</option>

二、二次定位
    1.定位select里的选项有多种方式,这里先介绍一种简单的方法:二次定位
    2.基本思路,先定位select框,再定位select里的选项            
    3.代码如下:

 

   4.还有另外一种写法也是可以的,把最下面两步合并成为一步:   

driver.find_element_by_id("nr").find_element_by_xpath("//option[@value='50']").click()

三、直接定位
    1.有很多小伙伴说firebug只能定位到select框,不能定位到里面的选项,其实是工具掌握的不太熟练。小编接下来教大家如何定位里面的选项。
    2.用firebug定位到select后,下方查看元素属性地方,点select标签前面的+号,就可以展开里面的选项内容了。

 3.然后自己写xpath定位或者css,一次性直接定位到option上的内容。(不会自己手写的,回头看前面的元素定位内容)

 

四、Select模块(index)

    1.除了上面介绍的两种简单的方法定位到select选项,selenium还提供了更高级的玩法,导入Select模块。直接根据属性或索引定位。
    2.先要导入select方法:
from selenium.webdriver.support.select import Select       
    3.然后通过select选项的索引来定位选择对应选项(从0开始计数),如选择第三个选项:select_by_index(2)

五、Select模块(value)

    1.Select模块里面除了index的方法,还有一个方法,通过选项的value值来定位。每个选项,都有对应的value值,如

<select id="nr" name="NR">
    <option selected="" value="10">每页显示10条</option>
    <option value="20">每页显示20条</option>          
    <option value="50">每页显示50条</option>
</select>

   2.第二个选项对应的value值就是"20":select_by_value("20")

 

六、Select模块(text)
    1.Select模块里面还有一个更加高级的功能,可以直接通过选项的文本内容来定位。
    2.定位“每页显示50条”:select_by_visible_text("每页显示50条")

 

七、Select模块其它方法
    1.select里面方法除了上面介绍的三种,还有更多的功能如下:

select_by_index()  :通过索引定位
select_by_value()  :通过value值定位
select_by_visible_text() :通过文本值定位
deselect_all()          :取消所有选项
deselect_by_index()     :取消对应index选项
deselect_by_value()      :取消对应value选项
deselect_by_visible_text() :取消对应文本选项

first_selected_option()  :返回第一个选项
all_selected_options()   :返回所有的选项

 

八、整理代码如下:

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.select import Select
driver = webdriver.Firefox()
url = "https://www.baidu.com"
driver.get(url)
driver.implicitly_wait(20)
# 鼠标移动到“设置”按钮
mouse = driver.find_element_by_link_text("设置")
ActionChains(driver).move_to_element(mouse).perform()
driver.find_element_by_link_text("搜索设置").click()
# 通过text:select_by_visible_text()
s = driver.find_element_by_id("nr")
Select(s).select_by_visible_text("每页显示50条")

# # 分两步:先定位下拉框,再点击选项s = driver.find_element_by_id("nr")s.find_element_by_xpath("//option[@value='50']").click()
# # 另外一种写法                                     
driver.find_element_by_id("nr").find_element_by_xpath("//option[@value='50']").click()
# # 直接通过xpath定位
driver.find_element_by_xpath(".//*[@id='nr']/option[2]").click()
# # 通过索引:select_by_index()
s = driver.find_element_by_id("nr")
Select(s).select_by_index(2)
# # 通过value:select_by_value()
s = driver.find_element_by_id("nr")
Select(s).select_by_value("20")

2.11 alert\confirm\prompt

前言   
不是所有的弹出框都叫alert,在使用alert方法前,先要识别出到底是不是alert。先认清楚alert长什么样子,下次碰到了,就可以用对应方法解决。
alert\confirm\prompt弹出框操作主要方法有:
text:获取文本值
accept() :点击"确认"
dismiss() :点击"取消"或者叉掉对话框
send_keys() :输入文本值 --仅限于prompt,在alert和confirm上没有输入框

一、认识alert\confirm\prompt
     1.如下图,从上到下依次为alert\confirm\prompt,先认清楚长什么样子,以后遇到了就知道如何操作了。

    2.html源码如下(有兴趣的可以copy出来,复制到txt文本里,后缀改成html就可以了,然后用浏览器打开):

<html>  
   <head>
     <title>Alert</title>  
    </head>  
<body>  
<input id = "alert" value = "alert" type = "button" onclick = "alert('您关注了yoyoketang吗?');"/>  
<input id = "confirm" value = "confirm" type = "button" onclick = "confirm('确定关注微信公众号:yoyoketang?');"/>  
<input
id = "prompt" value = "prompt" type = "button" onclick = "var name = 
prompt('请输入微信公众号:','yoyoketang'); document.write(name) "/>    
</body>   
</html>  

二、alert操作

   1.先用switch_to_alert()方法切换到alert弹出框上
    2.可以用text方法获取弹出的文本 信息
    3.accept()点击确认按钮
    4.dismiss()相当于点右上角x,取消弹出框
   (url的路径,直接复制浏览器打开的路径)

三、confirm操作
   1.先用switch_to_alert()方法切换到alert弹出框上
    2.可以用text方法获取弹出的文本 信息
    3.accept()点击确认按钮
    4.dismiss()相当于点取消按钮或点右上角x,取消弹出框
(url的路径,直接复制浏览器打开的路径)

四、prompt操作
   1.先用switch_to_alert()方法切换到alert弹出框上
    2.可以用text方法获取弹出的文本 信息
    3.accept()点击确认按钮
    4.dismiss()相当于点右上角x,取消弹出框
    5.send_keys()这里多个输入框,可以用send_keys()方法输入文本内容
(url的路径,直接复制浏览器打开的路径)

五、select遇到的坑
    1.在操作百度设置里面,点击“保存设置”按钮时,alert弹出框没有弹出来。(Ie浏览器是可以的)
    2.分析原因:经过慢慢调试后发现,在点击"保存设置"按钮时,由于前面的select操作后,失去了焦点
    3.解决办法:在select操作后,做个click()点击操作

s = driver.find_element_by_id("nr")
Select(s).select_by_visible_text("每页显示20条")
time.sleep(3)
s.click()

 

 六、最终代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.select import Select
import time
driver = webdriver.Firefox()
url = "https://www.baidu.com"
driver.get(url)
driver.implicitly_wait(20)
# 鼠标移动到“设置”按钮
mouse = driver.find_element_by_link_text("设置")
ActionChains(driver).move_to_element(mouse).perform()
driver.find_element_by_link_text("搜索设置").click()
# 通过text:select_by_visible_text()
s = driver.find_element_by_id("nr")
Select(s).select_by_visible_text("每页显示20条")
time.sleep(3)
s.click()
driver.find_element_by_link_text("保存设置").click()
time.sleep(5)
# 获取alert弹框
t = driver.switch_to_alert()
print t.text
t.accept()

这一篇应该比较简单,alert相关的内容比较少,虽然有一些页面也有弹窗,但不是所有的弹窗都叫alert。

alert的弹出框界面比较简洁,调用的是Windows系统弹窗警告框,没花里胡哨的东西,还是很容易区分的。

2.12 单选框和复选框(radiobox、checkbox)

本篇主要介绍单选框和复选框的操作
一、认识单选框和复选框
    1.先认清楚单选框和复选框长什么样

    2.各位小伙伴看清楚哦,上面的单选框是圆的;下图复选框是方的,这个是业界的标准,要是开发小伙伴把图标弄错了,可以先抽他了。
二、radio和checkbox源码
    1.上图的html源码如下,把下面这段复制下来,写到文本里,后缀改成.html就可以了。

 <html>  
    <head>  
      <meta http-equiv="content-type" content="text/html;charset=utf-8"/>  
      <title>单选和复选</title>  
    </head>  
    <body>  

    <h4>单选:性别</h4>  
    <form>  
    <label value="radio"></label>   
    <input name="sex" value="male"id="boy" type="radio"><br>  
    <label value="radio1"></label>  
    <input name="sex" value="female"id="girl" type="radio">  
    </form>  
    
    <h4>微信公众号:从零开始学自动化测试</h4> 
    <form>  
    <!-- <labelfor="c1">checkbox1</label> --> 
    <input id="c1"type="checkbox">selenium<br>  
    <!-- <labelfor="c2">checkbox2</label> -->  
    <input id="c2"type="checkbox">python<br>  
    <!-- <labelfor="c3">checkbox3</label> -->  
    <input id="c3"type="checkbox">appium<br>  
    
    <!-- <form>  
    <input type="radio" name="sex" value="male"/> Male  
    <br />  
    <input type="radio" name="sex"value="female" /> Female  
    </form> -->  
    </body>  
    </html> 

三、单选:radio
  1.首先是定位选择框的位置

 

  2.定位id,点击图标就可以了,代码如下(获取url地址方法:把上面源码粘贴到文本保存为.html后缀后用浏览器打开,在浏览器url地址栏复制出地址就可以了)
  3.先点击boy后,等十秒再点击girl,观察页面变化

 

四、复选框:checkbox
  1.勾选单个框,比如勾选selenium这个,可以根据它的id=c1直接定位到点击就可以了。

  2.那么问题来了:如果想全部勾选上呢?

五、全部勾选:
    1.全部勾选,可以用到定位一组元素,从上面源码可以看出,复选框的type=checkbox,这里可以用xpath语法:.//*[@type='checkbox']

     2.这里注意,敲黑板做笔记了:find_elements是不能直接点击的,它是复数的,所以只能先获取到所有的checkbox对象,然后通过for循环去一个个点击操作


六、判断是否选中:is_selected()
    1.有时候这个选项框,本身就是选中状态,如果我再点击一下,它就反选了,这可不是我期望的结果,那么可不可以当它是没选中的时候,我去点击下;当它已经是选中状态,我就不点击呢?那么问题来了:如何判断选项框是选中状态?
    2.判断元素是否选中这一步才是本文的核心内容,点击选项框对于大家来说没什么难度。获取元素是否为选中状态,打印结果如下图。
    3.返回结果为bool类型,没点击时候返回False,点击后返回True,接下来就很容易判断了,既可以作为操作前的判断,也可以作为测试结果的判断。

 

七、参考代码:

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("file:///C:/Users/Gloria/Desktop/checkbox.html")
# 没点击操作前,判断选项框状态
s = driver.find_element_by_id("boy").is_selected()
print s
driver.find_element_by_id("boy").click()
# 点击后,判断元素是否为选中状态
r = driver.find_element_by_id("boy").is_selected()
print r
# 复选框单选
driver.find_element_by_id("c1").click()
# 复选框全选
checkboxs = driver.find_elements_by_xpath(".//*[@type='checkbox']")
for i in checkboxs:
    i.click()

2.13 table表格定位

前言
    在web页面中经常会遇到table表格,特别是后台操作页面比较常见。本篇详细讲解table表格如何定位。
一、认识table
    1.首先看下table长什么样,如下图,这种网状表格的都是table

  2.源码如下:(用txt文本保存,后缀改成html)

<!DOCTYPE html>
<meta charset="UTF-8"> <!-- for HTML5 -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<html>  
        <head>  
            <title>Table测试模板</title>  
              
        </head>  
        <body>  
            <table border="1" id="myTable"> 
                <tr>  
                    <th>QQ群</th>  
                    <th>QQ号</th>  
                    <th>群主</th>  
                </tr>  
                <tr>  
                    <td>selenium自动化</td>  
                    <td>232607095</td>  
                    <td>YOYO</td>  
                </tr>  
                <tr>  
                    <td>appium自动化</td>  
                    <td>512200893</td>  
                    <td>YOYO</td>  
                </tr>  
            </table>  
        </body>  
</html> 

二、table特征
    1.table页面查看源码一般有这几个明显的标签:table、tr、th、td
    2.<table>标示一个表格
    3.<tr>标示这个表格中间的一个行
    4.</th> 定义表头单元格
    5.</td> 定义单元格标签,一组<td>标签将将建立一个单元格,<td>标签必须放在<tr>标签内

三、xpath定位table
    1.举个例子:我想定位表格里面的“selenium自动化”元素,这里可以用xpath定位:.//*[@id='myTable']/tbody/tr[2]/td[1]

    2.这里定位的格式是固定的,只需改tr和td后面的数字就可以了.如第二行第一列tr[2]td[1].
对xpath语法不熟悉的可以看这篇Selenium2+python自动化7-xpath定位
四、打印表格内容
    1.定位到表格内文本值,打印出来,脚本如下:

 

 五、参考代码:

# coding:utf-8
from selenium import webdriver
import time
url = 'file:///C:/Users/Gloria/Desktop/table.html'
driver = webdriver.Firefox()
driver.get(url)
time.sleep(3)
t = driver.find_element_by_xpath(".//*[@id='myTable']/tbody/tr[2]/td[1]")
print t.text

补充说明:有些小伙伴可能会遇到table在ifame上的情况,这时候就需要先切换iframe了。

2.14 加载Firefox配置(略,已在2.1.8讲过,请查阅2.1.8节课)

2.14-1 加载Chrome配置

一、加载Chrome配置
chrome加载配置方法,只需改下面一个地方,username改成你电脑的名字(别用中文!!!)

'--user-data-dir=C:\Users\username\AppData\Local\Google\Chrome\User Data'
# coding:utf-8
from selenium import webdriver
# 加载Chrome配置
option = webdriver.ChromeOptions()
option.add_argument('--user-data-dir=C:\Users\Gloria\AppData\Local\Google\Chrome\User Data')
driver = webdriver.Chrome(chrome_options=option)
driver.implicitly_wait(30)
driver.get("http://www.cnblogs.com/yoyoketang/")

二、Wap测试
1.做Wap测试的可以试下,伪装成手机访问淘宝,会出现触屏版

 

# coding:utf-8
from selenium import webdriver
option = webdriver.ChromeOptions()
# 伪装iphone登录
# option.add_argument('--user-agent=iphone')
# 伪装android
option.add_argument('--user-agent=android')
driver = webdriver.Chrome(chrome_options=option)
driver.get('http://www.taobao.com/')

2.15 富文本(richtext)

前言
     富文本编辑框是做web自动化最常见的场景,有很多小伙伴不知从何下手,本篇以博客园的编辑器为例,解决如何定位富文本,输入文本内容
一、加载配置
    1.打开博客园写随笔,首先需要登录,这里为了避免透露个人账户信息,我直接加载配置文件,免登录了。
      

二、打开编辑界面
    1.博客首页地址:bolgurl = "http://www.cnblogs.com/"
    2.我的博客园地址:yoyobolg = bolgurl + "yoyoketang"
    3.点击“新随笔”按钮,id=blog_nav_newpost

三、iframe切换
    1.打开编辑界面后先不要急着输入内容,先sleep几秒钟
    2.输入标题,这里直接通过id就可以定位到,没什么难点
    3.接下来就是重点要讲的富文本的编辑,这里编辑框有个iframe,所以需要先切换

(关于iframe不懂的可以看前面这篇:<iframe>)

 

 四、输入正文
    1.这里定位编辑正文是定位上图的红色框框位置body部分,也就是id=tinymce

    2.定位到之后,直接send_keys()方法就可以输入内容了

    3.有些小伙伴可能输入不成功,可以在输入之前先按个table键,send_keys(Keys.TAB)

五、参考代码:

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
profileDir = r'C:\Users\Gloria\AppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default'
profile = webdriver.FirefoxProfile(profileDir)
driver = webdriver.Firefox(profile)

bolgurl = "http://www.cnblogs.com/"
yoyobolg = bolgurl + "yoyoketang"
driver.get(yoyobolg)

driver.find_element_by_id("blog_nav_newpost").click()
time.sleep(5)
edittile = u"Selenium2+python自动化23-富文本"
editbody = u"这里是发帖的正文"
driver.find_element_by_id("Editor_Edit_txbTitle").send_keys(edittile)

driver.switch_to.frame("Editor_Edit_EditorBody_ifr")
driver.find_element_by_id("tinymce").send_keys(Keys.TAB)
driver.find_element_by_id("tinymce").send_keys(editbody)

2.16-1 非input文件上传(SendKeys)

前言
不少小伙伴问非input标签如何上传文档,这个本身就是一坑,无奈很多小伙伴非要跳坑里去,那就介绍一个非主流的上传文件方法吧,用第三方库SendKeys.
 
一、SendKeys安装
1.pip安装SendKeys
>pip install SendKeys

2.在安装的时候如果你出现上面保存,先别急着截图贴群求大神,上面已经告诉解决办法了:Get it from http://aka.ms/vcpython27
3.按上面给的地址下载文件,一路傻瓜式安装就行
4.出现如下界面,说明安装成功了

二、参考代码
1.以下代码在Chrom浏览器上是运行通过的,要先登录博客园记住密码,然后加载配置免登录
2.chrome加载配置方法,只需改下面一个地方,username改成你电脑的名字(别用中文!!!)

'--user-data-dir=C:\Users\username\AppData\Local\Google\Chrome\User Data'

3.后面两次回车,是因为搜狗输入法,第一个回车是确认输入,第二个是确定选中的文件

4.这里点文件上传按钮也是一个坑,用工具定位的这个元素,点击有问题,所以我改用它父元素定位了

# coding:utf-8
from selenium import webdriver
import SendKeys
import time
# 加载Firefox配置
# profileDir = r'C:\Users\xxxAppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default'
# profile = webdriver.FirefoxProfile(profileDir)
# driver = webdriver.Firefox(profile)
# 加载Chrome配置
option = webdriver.ChromeOptions()
option.add_argument('--user-data-dir=C:\Users\xxxAppData\Local\Google\Chrome\User Data')
driver = webdriver.Chrome(chrome_options=option)
driver.implicitly_wait(30)
driver.get("http://www.cnblogs.com/yoyoketang/")
driver.find_element_by_link_text("新随笔").click()
time.sleep(3)
# 点开编辑器图片
driver.find_element_by_css_selector("img.mceIcon").click()
time.sleep(3)
# 定位所有iframe,取第二个
iframe = driver.find_elements_by_tag_name('iframe')[1]
# 切换到iframe上
driver.switch_to_frame(iframe)
# 文件路径
time.sleep(2)
driver.find_element_by_class_name("qq-upload-button").click()
# driver.find_element_by_name("file").click()   # 这里点文件上传按钮也是一个坑,我用它父元素定位了,参考上面一行
time.sleep(5)
# SendKeys方法输入内容
SendKeys.SendKeys("D:\\test\\jie1\\blog\\12.png")  # 发送文件地址
time.sleep(1)
SendKeys.SendKeys("{ENTER}")   # 发送回车键
time.sleep(1)
SendKeys.SendKeys("{ENTER}")    # 因为我的电脑是搜索输入法,所以多看一次回车
# driver.quit()

(备注:这里Firefox上运行有个坑,第二次回车失效了,这个暂时没想到好的解决办法)
只能说处处都是坑,且用且珍惜!

2.16 文件上传(send_keys)

前言
文件上传是web页面上很常见的一个功能,用脚本去实现文件上传却不是那么简单。
一般分两个场景:一种是input标签,这种可以用selenium提供的send_keys()方法轻松解决;
另外一种非input标签实现起来比较困难,可以借助autoit工具或者SendKeys第三方库。
本篇以博客园的上传图片为案例,通过send_keys()方法解决文件上传问题
一、识别上传按钮
1.点开博客园编辑器里的图片上传按钮,弹出”上传本地图片”框。
2.用firebug查看按钮属性,这种上传图片按钮有个很明显的标识,它是一个input标签,并且type属性的值为file。只要找到这两个标识,我们就可以直接用send_keys()方法上传文件了。

 

 

二、定位iframe
1.这里定位图片上传按钮情况有点复杂,首先它是在iframe上。
2.这个iframe的id是动态的,且没有name属性,其它属性也不是很明显。
3.通过搜索发现,这个页面上有两个iframe,需要定位的这个iframe是处于第二个位置。

 

4.可以通过标签定位所有的iframe标签,然后取对应的第几个就可以了。

 

三、文件上传

1.先定位到文件上传按钮,直接调用send_keys()方法就可以实现啦

# coding:utf-8
from selenium import webdriver
import time
profileDir = r'C:\Users\Gloria\AppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default'
profile = webdriver.FirefoxProfile(profileDir)
driver = webdriver.Firefox(profile)
driver.implicitly_wait(30)
driver.get("http://www.cnblogs.com/yoyoketang/")
driver.find_element_by_link_text("新随笔").click()
time.sleep(3)
# 点开编辑器图片
driver.find_element_by_css_selector("img.mceIcon").click()
time.sleep(3)
# 定位所有iframe,取第二个
iframe = driver.find_elements_by_tag_name('iframe')[1]
# 切换到iframe上
driver.switch_to_frame(iframe)
# 文件路径
driver.find_element_by_name('file').send_keys(r"D:\test\xuexi\test\14.png")

非input标签的文件上传,就不适用于此方法了,需要借助autoit工具或者SendKeys第三方库。

2.17 获取元素属性

前言
通常在做断言之前,都要先获取界面上元素的属性,然后与期望结果对比。本篇介绍几种常见的获取元素属性方法。
一、获取页面title
1.有很多小伙伴都不知道title长在哪里,看下图左上角。

 

2.获取title方法很简单,直接driver.title就能获取到。

二、获取元素的文本
1.如下图这种显示在页面上的文本信息,可以直接获取到
2.查看元素属性:<a id="setf" target="_blank" οnmοusedοwn="return ns_c({'fm':'behs','tab':'favorites','pos':0})
" href="//www.baidu.com/cache/sethelp/help.html">把百度设为主页</a>

3.通过driver.text获取到文本

三、获取元素的标签
1.获取百度输入框的标签属性

 

四、获取元素的其它属性
1.获取其它属性方法:get_attribute("属性"),这里的参数可以是class、name等任意属性
2.如获取百度输入框的class属性

五、获取输入框内的文本值
1、如果在百度输入框输入了内容,这里输入框的内容也是可以获取到的

六、获取浏览器名称
1.获取浏览器名称很简单,用driver.name就能获取到

# 获取浏览器名称
driver.name
七、参考代码

# coding:utf-8
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")
time.sleep(2)

title = driver.title
print title

text = driver.find_element_by_id("setf").text
print text
# 获取元素的标签
tag = driver.find_element_by_id("kw").tag_name
print tag
# 获取元素的其它属性
name = driver.find_element_by_id("kw").get_attribute("class")
print name
# 获取输入框的内容
driver.find_element_by_id("kw").send_keys("yoyoketang")
value = driver.find_element_by_id("kw").get_attribute("value")
print value

# 获取浏览器名称
print driver.name

2.18 爬页面源码(page_source)

前言
有时候通过元素的属性的查找页面上的某个元素,可能不太好找,这时候可以从源码中爬出想要的信息。selenium的page_source方法可以获取到页面源码。
一、page_source
1.selenium的page_source方法可以直接返回页面源码
2.重新赋值后打印出来

二、re非贪婪模式
1.这里需导入re模块(正则表达式模块)
2.用re的正则匹配:非贪婪模式
3.findall方法返回的是一个list集合
4.匹配出来之后发现有一些不是url链接,可以筛选下

三、筛选url地址出来
1.加个if语句判断,‘http’在url里面说明是正常的url地址了
2.把所有的url地址放到一个集合,就是我们想要的结果啦

四、参考代码

# coding:utf-8
from selenium import webdriver
import re
driver = webdriver.Firefox()
driver.get("http://www.cnblogs.com/yoyoketang/")
page = driver.page_source
# print page
# "非贪婪匹配,re.S('.'匹配字符,包括换行符)"
url_list = re.findall('href=\"(.*?)\"', page, re.S)
url_all = []
for url in url_list:
    if "http" in url:
        print url
        url_all.append(url)
# 最终的url集合
print url_all

2.19 cookie相关操作

前言
虽然cookie相关操作在平常ui自动化中用得少,偶尔也会用到,比如登录有图形验证码,可以通过绕过验证码方式,添加cookie方法登录。
登录后换账号登录时候,也可作为后置条件去删除cookie然后下个账号登录
一、获取cookies:get_cookies()
1.获取cookies方法直接用:get_cookies()
2.先启动浏览器,获取cookies,打印出来发现是空:[]
3.打开博客首页后,重新获取cookies,打印出来,就有值了

 

二、登录后的cookies
1.先登录博客园(这里登录用自己的账号和密码吧)
2.重新获取cookies,发现跟之前获取的不一样了
3.主要是找到这一个cookie,发现它的name和value发生了变化,这就是未登录和已登录的区别了(对比上下两张图)
{u'name': u'.CNBlogsCookie', u'value': u'B7813EBA142142CE88CC8C0B33B239F566xxxx'}

三、获取指定name的cookie:driver.get_cookie(name)
1.获取cookies发现里面有多个cookie,有时候我们只需要其中的一个,把重要的提出来,比如登录的cookie
2.这里用get_cookie(name),指定对应的cookie的name值就行了,比如博客园的:.CNBlogsCookie

四、清除指定cookie:delete_cookie()
1.为了进一步验证上一步获取到的就是登录的cookie,可以删除它看看页面什么变化
2.删除这个cookie后刷新页面,发现刚才的登录已经失效了,变成未登录状态了

五、清除所有cookies:delete_all_cookies()
1.清除所有cookies后登录状态也失效了,cookies为空[]

六、cookie操作的几个方法
1.get_cookies():获取所有cookies
2.driver.get_cookie(name):获取指定name的cookie:
3.清除指定cookie:delete_cookie()
4.delete_all_cookies():清除所有cookies
5.add_cookie(cookie_dict):添加cookie的值
(第五个方法可以用于绕过验证码登录,下篇详细介绍)

 七、参考代码

# coding:utf-8
from selenium import webdriver
import time
driver = webdriver.Firefox()
# 启动浏览器后获取cookies
print driver.get_cookies()
driver.get("http://www.cnblogs.com/yoyoketang/")
# 打开主页后获取cookies
print driver.get_cookies()
# 登录后获取cookies
url = "https://passport.cnblogs.com/user/signin"
driver.get(url)
driver.implicitly_wait(30)
driver.find_element_by_id("input1").send_keys(u"上海-悠悠")
driver.find_element_by_id("input2").send_keys(u"xxx")
driver.find_element_by_id("signin").click()
time.sleep(3)
print driver.get_cookies()
# 获取指定name的cookie
print driver.get_cookie(name=".CNBlogsCookie")
# 清除指定name的cookie
driver.delete_cookie(name=".CNBlogsCookie")
print driver.get_cookies()
# 为了验证此cookie是登录的,可以删除后刷新页面
driver.refresh()
# 清除所有的cookie
driver.delete_all_cookies()
print driver.get_cookies()

2.20 绕过验证码(add_cookie)

前言
验证码这种问题是比较头疼的,对于验证码的处理,不要去想破解方法,这个验证码本来就是为了防止别人自动化登录的。如果你能破解,说明你们公司的验证码吗安全级别不高,那就需要提高级别了。
对于验证码,要么是让开发在测试环境弄个万能的验证码,如:1234,要么就是尽量绕过去,如本篇介绍的添加cookie的方法。
一、fiddler抓包
1.前一篇讲到,登录后会生成一个已登录状态的cookie,那么只需要直接把这个值添加到cookies里面就可以了。
2.可以先手动登录一次,然后抓取这个cookie,这里就需要用抓包工具fiddler了
3.先打开博客园登录界面,手动输入账号和密码(不要点登录按钮)

4.打开fiddler抓包工具,此时再点博客园登录按钮

 

5.登录成功后,再查看cookie变化,发现多了两组参数,多的这两组参数就是我们想要的,copy出来,一会有用

二、添加cookie方法:driver.add_cookie()
1.add_cookie(cookie_dict)方法里面参数是cookie_dict,说明里面参数是字典类型。
2.源码官方文档介绍:

add_cookie(self, cookie_dict):
   Adds a cookie to your current session.
   
   :Args:
    - cookie_dict: A dictionary object, with required keys - "name" and "value";
       optional keys - "path", "domain", "secure", "expiry"
   
   Usage:
       driver.add_cookie({'name' : 'foo', 'value' : 'bar'})
       driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/'})
       driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/', 'secure':True})

3.从官方的文档里面可以看出,添加cookie时候传入字典类型就可以了,等号左边的是name,等号右边的是value。
4.把前面抓到的两组数据(参数不仅仅只有name和value),写成字典类型:
{'name':'.CNBlogsCookie','value':'2C3AE01E461B2D2F1572D02CB936D77A053089AA2xxxx...'}
{'name':'.Cnblogs.AspNetCore.Cookies','value':'CfDJ8Mmb5OBERd5FqtiQlKZZIG4HKz_Zxxx...'}

三、cookie组成结构

1.用抓包工具fidller只能看到cookie的name和value两个参数,实际上cookie还有其它参数。
2.cookie参数组成,以下参数是我通过get_cookie(name)获取到的。

cookie ={u'domain': u'.cnblogs.com',
            u'name': u'.CNBlogsCookie',
            u'value': u'xxxx',
            u'expiry': 1491887887,
            u'path': u'/',
            u'httpOnly': True,
            u'secure': False}

name:cookie的名称
value:cookie对应的值,动态生成的
domain:服务器域名
expiry:Cookie有效终止日期
path:Path属性定义了Web服务器上哪些路径下的页面可获取服务器设置的Cookie
httpOnly:防脚本攻击
secure:在Cookie中标记该变量,表明只有当浏览器和Web Server之间的通信协议为加密认证协议时,
浏览器才向服务器提交相应的Cookie。当前这种协议只有一种,即为HTTPS。

四、添加cookie
1.这里需要添加两个cookie,一个是.CNBlogsCookie,另外一个是.Cnblogs.AspNetCore.Cookies。
2.我这里打开的网页是博客的主页:http://www.cnblogs.com/yoyoketang,没进入登录页。
3.添加cookie后刷新页面,接下来就是见证奇迹的时刻了。

五、参考代码:

# coding:utf-8
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get("http://www.cnblogs.com/yoyoketang")
# # 添加cookie
c1 = {u'domain': u'.cnblogs.com',
      u'name': u'.CNBlogsCookie',
      u'value': u'xxxx',
      u'expiry': 1491887887,
      u'path': u'/',
      u'httpOnly': True,
      u'secure': False}
c2 = {u'domain': u'.cnblogs.com',
      u'name': u'.Cnblogs.AspNetCore.Cookies',
      u'value': u'xxxx',
      u'expiry': 1491887887,
      u'path': u'/',
      u'httpOnly': True,
      u'secure': False}
driver.add_cookie(c1)  # 添加2个值
driver.add_cookie(c2)
time.sleep(3)          # 交流QQ群:232607095
# 刷新下页面就见证奇迹了
driver.refresh()  

有几点需要注意:

1.登录时候要勾选下次自动登录按钮。

2.add_cookie()只添加name和value,对于博客园的登录是不成功。

3.本方法并不适合所有的网站,一般像博客园这种记住登录状态的才会适合。

2.21 JS处理滚动条

前言
    selenium并不是万能的,有时候页面上操作无法实现的,这时候就需要借助JS来完成了。
常见场景:
当页面上的元素超过一屏后,想操作屏幕下方的元素,是不能直接定位到,会报元素不可见的。这时候需要借助滚动条来拖动屏幕,使被操作的元素显示在当前的屏幕上。滚动条是无法直接用定位工具来定位的。selenium里面也没有直接的方法去控制滚动条,这时候只能借助J了,还好selenium提供了一个操作js的方法:execute_script(),可以直接执行js的脚本。

一、JavaScript简介

1.JavaScript是世界上最流行的脚本语言,因为你在电脑、手机、平板上浏览的所有的网页,以及无数基于HTML5的手机App,交互逻辑都是由JavaScript驱动的。简单地说,JavaScript是一种运行在浏览器中的解释型的编程语言。那么问题来了,为什么我们要学JavaScript?

2.有些特殊的操作selenium2+python无法直接完成的,JS刚好是这方面的强项,所以算是一个很好的补充。对js不太熟悉的,可以网上找下教程,简单了解些即可。
http://www.w3school.com.cn/js/index.asp4

二、控制滚动条高度
1.滚动条回到顶部:

js="var q=document.getElementById('id').scrollTop=0"
driver.execute_script(js)

2.滚动条拉到底部

js="var q=document.documentElement.scrollTop=10000"
driver.execute_script(js)

3.这里可以修改scrollTop 的值,来定位右侧滚动条的位置,0是最上面,10000是最底部。

三、横向滚动条
1.有时候浏览器页面需要左右滚动(一般屏幕最大化后,左右滚动的情况已经很少见了)。

2.通过左边控制横向和纵向滚动条

scrollTo(x, y)js = "window.scrollTo(100,400);"
driver.execute_script(js)

3.第一个参数x是横向距离,第二个参数y是纵向距离

四、Chrome浏览器
1.以上方法在Firefox上是可以的,但是用Chrome浏览器,发现不管用。
谷歌浏览器就是这么任性,不听话,于是用以下方法解决谷歌浏览器滚动条的问题。
2.Chrome浏览器解决办法:

js = "var q=document.body.scrollTop=0"
driver.execute_script(js)
 
五、元素聚焦
1.虽然用上面的方法可以解决拖动滚动条的位置问题,但是有时候无法确定我需要操作的元素
在什么位置,有可能每次打开的页面不一样,元素所在的位置也不一样,怎么办呢?
2.这个时候我们可以先让页面直接跳到元素出现的位置,然后就可以操作了。同样需要借助JS去实现。
3.元素聚焦:
target = driver.find_element_by_xxxx()
driver.execute_script("arguments[0].scrollIntoView();", target)

六、获取浏览器名称:driver.name
1.为了解决不同浏览器操作方法不一样的问题,可以写个函数去做兼容。
2.先用driver.name获取浏览器名称,然后用if语句做个判断

 

 

七、兼容性
1.兼容谷歌和firefox/IE

八、scrollTo函数
楼下有个小伙伴说这个scrollTo函数不存在兼容性问题,小编借花献佛了。
--scrollHeight 获取对象的滚动高度。 
--scrollLeft 设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离。 
--scrollTop 设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离。

--scrollWidth 获取对象的滚动宽度。 

scrollTo函数不存在兼容性问题,直接用这个函数就可以了

#滚动到底部
js = "window.scrollTo(0,document.body.scrollHeight)"
driver.execute_script(js)
#滚动到顶部
js = "window.scrollTo(0,0)"  
driver.execute_script(js)

九、参考代码如下:

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
print driver.name
## 回到顶部
#def scroll_top():
#        if driver.name == "chrome":
#               js = "var q=document.body.scrollTop=0"
#        else:
#                js = "var q=document.documentElement.scrollTop=0"
#        return driver.execute_script(js)
# 拉到底部
#def scroll_foot():
#       if driver.name == "chrome":
#                js = "var q=document.body.scrollTop=10000"
#        else:
#                js = "var q=document.documentElement.scrollTop=10000"
#        return driver.execute_script(js)
#滚动到底部
js = "window.scrollTo(0,document.body.scrollHeight)"
driver.execute_script(js)
#滚动到顶部
js = "window.scrollTo(0,0)"  
driver.execute_script(js)
# 聚焦元素
target = driver.find_element_by_xxxx()
driver.execute_script("arguments[0].scrollIntoView();", target)

JS功能还是很强大的,它还可以处理富文本、内嵌滚动条的问题。

2.22 JS处理富文本

前言
    <富文本>这篇解决了富文本上iframe问题,其实没什么特别之处,主要是iframe的切换,本篇讲解通过js的方法处理富文本上iframe的问题
一、加载配置
    1.打开博客园写随笔,首先需要登录,这里为了避免透露个人账户信息,我直接加载配置文件,免登录了。
      不懂如何加载配置文件的,看加载firefox配置

二、打开编辑界面
    1.博客首页地址:bolgurl = "http://www.cnblogs.com/"
    2.我的博客园地址:yoyobolg = bolgurl + "yoyoketang"
    3.点击“新随笔”按钮,id=blog_nav_newpost

三、定位iframe
    1.打开编辑界面后先不要急着输入内容,先sleep几秒钟
    2.输入标题,这里直接通过id就可以定位到,没什么难点
    3.接下来就是重点要讲的富文本的编辑,这里编辑框有个iframe,所以需要先切换

 

 四、js输入中文
    1.这里定位编辑正文是定位上图的红色框框位置body部分,也就是id=tinymce

    2.定位到之后,用js的方法直接输入,无需切换iframe

    3.直接点保存按钮,无需再切回来

五、参考代码:

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
# profileDir路径对应直接电脑的配置路径
profileDir = r'C:\xxx\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default'
profile = webdriver.FirefoxProfile(profileDir)
driver = webdriver.Firefox(profile)
bolgurl = "http://www.cnblogs.com/"
yoyobolg = bolgurl + "yoyoketang"
driver.get(yoyobolg)
driver.find_element_by_id("blog_nav_newpost").click()
time.sleep(5)
edittile = u"Selenium2+python自动化23-富文本"
editbody = u"这里是发帖的正文"
driver.find_element_by_id("Editor_Edit_txbTitle").send_keys(edittile)
body = "这里是通过js发的正文内容"
# js处理iframe问题(js代码太长了,我分成两行了)
js = 'document.getElementById("Editor_Edit_EditorBody_ifr")' \
     '.contentWindow.document.body.innerHTML="%s"' % body
driver.execute_script(js)
# 保存草稿
driver.find_element_by_id("Editor_Edit_lkbDraft").click()

2.23 js处理日历控件(修改readonly属性)

前言
    日历控件是web网站上经常会遇到的一个场景,有些输入框是可以直接输入日期的,有些不能,以我们经常抢票的12306网站为例,详细讲解如何解决日历控件为readonly属性的问题。
    基本思路:先用js去掉readonly属性,然后直接输入日期文本内容
一、日历控件
    1.打开12306的车票查询界面,在出发日期输入框无法直接输入时间
    2.常规思路是点开日历控件弹出框,从日历控件上点日期,这样操作比较烦躁,并且我们测试的重点不在日历控件上,只是想输入个时间,做下一步的操作
    3.用firebug查看输入框的属性:readonly="readonly",如下:

<input id="train_date" class="inp-txt" type="text" value="" name="leftTicketDTO.train_date" autocomplete="off" maxlength="10" readonly="readonly">

 

 二、去掉readonly属性

    1.很明显这种元素的属性是readonly,输入框是无法直接输入的,这时候需要先去掉元素的readonly属性,然后就可以输入啦。

    2.点左下角firebug的“编辑按钮”,找到对应元素,直接删除readonly="readonly",然后回车。

    3.在页面出发日位置输入:yoyoketang 试试,嘿嘿,有没有发现可以输入成功。当然这里只是为了验证可以输入内容,测试时候还是输入测试的日期。

三、用js去掉readonly属性
    1.用js去掉元素属性基本思路:先定位到元素,然后用removeAttribute("readonly")方法删除属性。
    2.出发日元素id为:train_date,对应js代码为:'document.getElementById("train_date").removeAttribute("readonly");'

四、输入日期
    1.输入日期前,一定要先清空文本,要不然无法输入成功的。
    2.这里输入日期后,会自动弹出日历控件,随便点下其它位置就好了,接下来会用js方法传入日期,就不会弹啦!

 

五、js方法输入日期
   1.这里也可以用js方法输入日期,其实很简单,直接改掉输入框元素的value值就可以啦。

六、参考代码如下:

from selenium import webdriver
driver = webdriver.Firefox()
driver.get("https://kyfw.12306.cn/otn/index/init")
# 去掉元素的readonly属性
js = 'document.getElementById("train_date").removeAttribute("readonly");'
driver.execute_script(js)
# 用js方法输入日期
js_value = 'document.getElementById("train_date").value="2016-12-25"'
driver.execute_script(js_value)
# # 清空文本后输入值
# driver.find_element_by_id("train_date").clear()
# driver.find_element_by_id("train_date").send_keys("2016-12-25")

 

2.24 js处理内嵌div滚动条

前言
    前面有篇专门用js解决了浏览器滚动条的问题,生活总是多姿多彩,有的滚动条就在页面上,这时候又得仰仗js大哥来解决啦。
一、内嵌滚动条
    1.下面这张图就是内嵌div带有滚动条的样子,记住它的长相。

    2.页面源码如下:(老规矩:copy下来,用文本保存下来,后缀改成.html,用浏览器打开)

<!DOCTYPE html>
<meta charset="UTF-8"> <!-- for HTML5 -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<html>
  <head>
    <style type="text/css">
      div.scroll
      {
        background-color:#afafaf;
        width:500px;
        height:100px;
        overflow:auto;
      }
    </style>
  </head>


  <body>
    <p>个人微信公众号:yoyoketang</p>
    <p>这是一个内嵌的div滚动条</p>
    <div id="yoyoketang" name="yoyo" class="scroll">这是一个内嵌div:民国年间,九大家族镇守长沙,被称为“九门提督”。这九门势力庞大,外八行的无人不知,无人不
晓,几乎所有冥器,流出长沙必然经过其中一家。1933年秋,一辆神秘鬼车缓缓驶入长沙火车站,九门之首“张大佛爷”张启山身为布防官,奉命调查始末。张启山与八爷齐铁嘴一路探访,发现长沙城外有一座疑点重重的矿山,一直被日本人窥伺。
为破解矿山之谜,张启山求助同为九门上三门的戏曲名伶二月红,无奈二月红虽出身考古世家,却心系重病的妻子丫头,早已金盆洗手。张启山为了国家大义和手足之情,北上去往新月饭店为二月红爱妻求药。在北平,张启山邂逅了新月饭店的大小姐尹新月,并为尹新月连点三盏天灯,散尽家财。尹新月帮助张启山等人顺利返回
长沙,二人暗生情愫。二月红爱妻病入膏肓,服药后不见好转,最终故去。二月红悲伤之余却意外发现家族祖辈与矿山亦有重大关联,于是振作精神,决定与张启山联手,解开矿山之谜zhegedancihenchanghenchangchangchangchangchanchanchanchangchangchangchancg
    </div>
  </body>
</html>

二、纵向滚动

    1.这个是div的属性:<div id="yoyoketang" name="yoyo" class="scroll">

    2.这里最简单的通过id来定位,通过控制 scrollTop的值来控制滚动条高度

    3.运行下面代码,观察页面是不是先滚动到底部,过五秒再回到顶部。(get里面地址是浏览器打开该页面的地址)

 

三、横向滚动

  1.先通过id来定位,通过控制scrollLeft的值来控制滚动条高度

四、用class属性定位
    1.js用class属性定位,返回的是一个list对象,这里取第一个就可以了。
    2.这里要注意了,element和elements有很多小伙伴傻傻分不清楚。

有时候很多元素属性都一样时候,就可以用复数定位,取对应的第几个就可以了。

2.25 js处理多窗口

前言
在打开页面上链接的时候,经常会弹出另外一个窗口(多窗口情况前面这篇有讲解:Selenium2+python自动化13-多窗口、句柄(handle)),这样在多个窗口之间来回切换比较复杂,那么有没有办法让新打开的链接在一个窗口打开呢?
要解决这个问题,得从html源码上找到原因,然后修改元素属性才能解决。很显然js在这方面是万能的,于是本篇得依靠万能的js大哥了。
一、多窗口情况
    1.在打baidu的网站链接时,会重新打开一个窗口
    (注意:我的百度页面是已登录状态,没登录时候是不会重新打开窗口的)

二、查看元素属性:target="_blank"
1.查看元素属性,会发现这些链接有个共同属性:target="_blank"

三、去掉target="_blank"属性
1.因为此链接元素target="_blank",所以打开链接的时候会重新打开一个标签页,那么解决这个问题,去掉该属性就可以了。
2.为了验证这个问题,可以切换到html编辑界面,手动去掉“_blank”属性。

 

3.删除“_blank”属性后,重新打开链接,这时候会发现打开的新链接会在原标签页打开。

四、js去掉target="_blank"属性
1.第一步为了先登录,我这里加载配置文件免登录了(不会的看这篇:Selenium2+python自动化18-加载Firefox配置)
2.这里用到js的定位方法,定位该元素的class属性
3.定位到该元素后直接修改target属性值为空

五、参考代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
# 加载配置文件免登录
profileDir = r'C:\Users\Gloria\AppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default'
profile = webdriver.FirefoxProfile(profileDir)
driver = webdriver.Firefox(profile)
driver.get("https://www.baidu.com/")
# 修改元素的target属性
js = 'document.getElementsByClassName("mnav")[0].target="";'
driver.execute_script(js)
driver.find_element_by_link_text("糯米").click()

注意:并不是所有的链接都适用于本方法,本篇只适用于有这个target="_blank"属性链接情况。

本篇仅提供解决问题的办法和思路,不要完全照搬代码!!!

2.26 js解决click失效问题

前言
有时候元素明明已经找到了,运行也没报错,点击后页面没任何反应。这种问题遇到了,是比较头疼的,因为没任何报错,只是click事件失效了。
本篇用2种方法解决这种诡异的点击事件失效问题
一、遇到的问题
1.在练习百度的搜索设置按钮时,点保存设置按钮,alert弹出没弹出(代码没报错,只是获取alert失败),相信不只是我一个人遇到过。

二、点击父元素
1.遇到这种问题,应该是前面操作select后导致的后遗症(因为我注释掉select那段是可以点击成功的)。
2.第一种解决办法,先点击它的父元素一次,然后再点击这个元素。

3.实现代码如下

三、js直接点击

1.遇到这种诡异问题,是时候出绝招了:js大法。
2.用js直接执行点击事件。

四、参考代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.select import Select
import time
driver = webdriver.Firefox()
url = "https://www.baidu.com"
driver.get(url)
time.sleep(3)
mouse = driver.find_element("link text", "设置")
ActionChains(driver).move_to_element(mouse).perform()
time.sleep(3)
driver.find_element("link text", "搜索设置").click()
time.sleep(3)
s = driver.find_element("id", "nr")
Select(s).select_by_visible_text("每页显示50条")
# 方法一:先点父元素 交流QQ群:232607095
# driver.find_element("id", "gxszButton").click()
# driver.find_element("class name", "prefpanelgo").click()
# 方法二:用js直接去点击 交流QQ群:232607095
js = 'document.getElementsByClassName("prefpanelgo")[0].click();'
driver.execute_script(js)

2.27 18种定位方法总结

前言
江湖传言,武林中流传八种定位,其中xpath是宝刀屠龙,css是倚天剑。
除了这八种,其实还有十种定位方法,眼看就快失传了,今天小编让失传已久的定位方法重出江湖!

一、十八种定位方法

前八种是大家都熟悉的,经常会用到的

1.id定位:find_element_by_id(self, id_)
2.name定位:find_element_by_name(self, name)
3.class定位:find_element_by_class_name(self, name)
4.tag定位:find_element_by_tag_name(self, name)
5.link定位:find_element_by_link_text(self, link_text)
6.partial_link定位find_element_by_partial_link_text(self, link_text)
7.xpath定位:find_element_by_xpath(self, xpath)
8.css定位:find_element_by_css_selector(self, css_selector)

这八种是复数形式

9.id复数定位find_elements_by_id(self, id_)
10.name复数定位find_elements_by_name(self, name)
11.class复数定位find_elements_by_class_name(self, name)
12.tag复数定位find_elements_by_tag_name(self, name)
13.link复数定位find_elements_by_link_text(self, text)
14.partial_link复数定位find_elements_by_partial_link_text(self, link_text)
15.xpath复数定位find_elements_by_xpath(self, xpath)
16.css复数定位find_elements_by_css_selector(self, css_selector)

这两种就是快失传了的

17.find_element(self, by='id', value=None)
18.find_elements(self, by='id', value=None)

二、element和elements傻傻分不清
1.element方法定位到是是单数,是直接定位到元素
2.elements方法是复数,这个学过英文的都知道,定位到的是一组元素,返回的是list队列
3.可以用type()函数查看数据类型
4.打印这个返回的内容看看有什么不一样

三、elements定位方法
1.前面2.8章节讲过定位一组元素用elements的方法,elements也可以用于单数定位。

2.这里重点介绍下用elements方法如何定位元素,当一个页面上有多个属性相同的元素时,然后父元素的属性也比较模糊,不太好定位。这个时候不用怕,换个思维,别老想着一次定位到,可以先把相同属性的元素找出来,取对应的第几个就可以了。

3.如下图,百度页面上有六个class一样的元素,我要定位“地图”这个元素。

4.取对应下标即可定位了。

四、参考代码

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
# 这里是定位的单个id
element = driver.find_element_by_id("kw")
print type(element)
print element
# 这里定位是多个class
elements = driver.find_elements_by_class_name("mnav")
print type(elements)
print elements
# 这里用的css语法
s = driver.find_elements("css selector", ".mnav")
# '地图'在第四个位置
print s[3].text
s[3].click()
# 这个写法也是可以的
# driver.find_elements("css selector", ".mnav")[3].click()

2.28 查看webdriver API(带翻译)

前言
    前面都是点点滴滴的介绍selenium的一些api使用方法,那么selenium的api到底有多少呢?本篇就教大家如何去查看selenium api,不求人,无需伸手找人要,在自己电脑就有。
    pydoc是Python自带的模块,主要用于从python模块中自动生成文档,这些文档可以基于文本呈现的、也可以生成WEB 页面的,还可以在服务器上以浏览器的方式呈现!
一、pydoc
    1.到底什么是pydoc? ,这个是准确的解释:Documentation generator and online help system. pydoc是Python自带的模块,主要用于从python模块中自动生成文档,这些文档可以基于文本呈现的、也可以生成WEB 
页面的,还可以在服务器上以浏览器的方式呈现!简而言之,就是帮你从代码和注释自动生成文档的工具。

    2.举个栗子,我需要查看python里面open函数的功能和语法,打开cmd,输入:python -m pydoc open

    3.-m参数:python以脚本方法运行模块
>>python -m pydoc open

 

 那么问题来了,这个是已经知道有这个函数,去查看它的功能,selenium里面不知道到底有多少个函数或方法,那如何查看呢?

二、启动server
    1.打开cmd命令行,输入:python -m pydoc -p 6666
    2.-p参数:这个表示在本机上启动服务
    3.6666参数:这个是服务端口号,随意设置

打开后,界面会出现一个地址:http://localhost:6666/,在浏览器直接打开。

三、浏览器查看文档
    1.在浏览器输入:http://localhost:6666/
    2.Built-in Moudles :这个是python自带的模块

 

四、webdriver API

    1.找到这个路径:python2.7\lib\site-packages,点开selenium
    2.打开的selenium>webdriver>firefox>webdriver,最终路径:http://localhost:6666/selenium.webdriver.firefox.webdriver.html
    3.最终看到的这些就是selenium的webdriver API帮助文档啦

【附录】webdriver API(带翻译)

    1.找到这个路径:python2.7\lib\site-packages,点开selenium
    2.打开的selenium>webdriver>firefox>webdriver,最终路径:http://localhost:6666/selenium.webdriver.firefox.webdriver.html
    3.最终看到的这些就是selenium的webdriver API帮助文档啦

1.add_cookie(self,cookie_dict)
##翻译:添加cookie,cookie参数为字典数据类型
Adds a cookie to your current session.
:Args:
- cookie_dict: A dictionary object, with required keys - "name" and"value";
optional keys - "path", "domain", "secure","expiry"
Usage:
driver.add_cookie({'name' : 'foo', 'value' : 'bar'})
driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/'})
driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/','secure':True})
2.back(self)
##浏览器返回
Goes one step backward in the browser history.

:Usage:
driver.back()
3.close(self)
##关闭浏览器
Closes the current window.
:Usage:
driver.close()
4.create_web_element(self,element_id)
##给元素分配一个id
Creates a web element with the specified element_id.
5.delete_all_cookies(self)
##删除所有的cookies

Delete all cookies in the scope of the session.
:Usage:
driver.delete_all_cookies()
6.delete_cookie(self,name)
##删除指定name的cookie
Deletes a single cookie with the given name.
:Usage:
driver.delete_cookie('my_cookie')
7.execute(self,driver_command, params=None)
Sends a command to be executed by a command.CommandExecutor.
:Args:
- driver_command: The name of the command to execute as a string.

- params: A dictionary of named parameters to send with the command.
:Returns:
The command's JSON response loaded into a dictionary object.
8.execute_async_script(self,script, *args)
Asynchronously Executes JavaScript in the current window/frame.
:Args:
- script: The JavaScript to execute.
- \*args: Any applicable arguments for your JavaScript.
:Usage:
driver.execute_async_script('document.title')
9.execute_script(self,script, *args)

##执行JS
Synchronously Executes JavaScript in the current window/frame.
:Args:
- script: The JavaScript to execute.
- \*args: Any applicable arguments for your JavaScript.
:Usage:
driver.execute_script('document.title')
10.file_detector_context(*args,**kwds)
Overrides the current file detector (if necessary) in limited context.
Ensures the original file detector is set afterwards.
Example:
with webdriver.file_detector_context(UselessFileDetector):

someinput.send_keys('/etc/hosts')
:Args:
- file_detector_class - Class of the desired file detector. If the class is different
from the current file_detector, then the class is instantiated with args andkwargs
and used as a file detector during the duration of the context manager.
- args - Optional arguments that get passed to the file detector class during
instantiation.
- kwargs - Keyword arguments, passed the same way as args.
11.find_element(self,by='id', value=None)
##定位元素,参数化的方法

'Private' method used by the find_element_by_* methods.
:Usage:
Use the corresponding find_element_by_* instead of this.
:rtype: WebElement
12.find_element_by_class_name(self,name)
##通过class属性定位元素
Finds an element by class name.
:Args:
- name: The class name of the element to find.
:Usage:
driver.find_element_by_class_name('foo')
13.find_element_by_css_selector(self,css_selector)

##通过css定位元素
Finds an element by css selector.
:Args:
- css_selector: The css selector to use when finding elements.
:Usage:
driver.find_element_by_css_selector('#foo')
14.find_element_by_id(self,id_)
##通过id定位元素
Finds an element by id.
:Args:
- id\_ - The id of the element to be found.
:Usage:

driver.find_element_by_id('foo')
15.find_element_by_link_text(self,link_text)
##通过link链接定位
Finds an element by link text.
:Args:
- link_text: The text of the element to be found.
:Usage:
driver.find_element_by_link_text('Sign In')
16.find_element_by_name(self,name)
##通过name属性定位
Finds an element by name.
:Args:

- name: The name of the element to find.
:Usage:
driver.find_element_by_name('foo')
17.find_element_by_partial_link_text(self,link_text)
##通过部分link的模糊定位
Finds an element by a partial match of its link text.
:Args:
- link_text: The text of the element to partially match on.
:Usage:
driver.find_element_by_partial_link_text('Sign')
18.find_element_by_tag_name(self,name)
##通过标签定位

Finds an element by tag name.
:Args:
- name: The tag name of the element to find.
:Usage:
driver.find_element_by_tag_name('foo')
19.find_element_by_xpath(self,xpath)
##通过xpath语法定位
Finds an element by xpath.
:Args:
- xpath - The xpath locator of the element to find.
:Usage:
driver.find_element_by_xpath('//div/td[1]')

20.find_elements(self,by='id', value=None)
##定位一组元素
'Private' method used by the find_elements_by_* methods.
:Usage:
Use the corresponding find_elements_by_* instead of this.
:rtype: list of WebElement
21.find_elements_by_class_name(self,name)
Finds elements by class name.
:Args:
- name: The class name of the elements to find.
:Usage:
driver.find_elements_by_class_name('foo')

22.find_elements_by_css_selector(self,css_selector)
Finds elements by css selector.
:Args:
- css_selector: The css selector to use when finding elements.
:Usage:
driver.find_elements_by_css_selector('.foo')
23.find_elements_by_id(self,id_)
Finds multiple elements by id.
:Args:
- id\_ - The id of the elements to be found.
:Usage:
driver.find_elements_by_id('foo')

24.find_elements_by_link_text(self,text)
Finds elements by link text.
:Args:
- link_text: The text of the elements to be found.
:Usage:
driver.find_elements_by_link_text('Sign In')
25.find_elements_by_name(self,name)
Finds elements by name.
:Args:
- name: The name of the elements to find.
:Usage:
driver.find_elements_by_name('foo')

26.find_elements_by_partial_link_text(self,link_text)
Finds elements by a partial match of their link text.
:Args:
- link_text: The text of the element to partial match on.
:Usage:
driver.find_element_by_partial_link_text('Sign')
27.find_elements_by_tag_name(self,name)
Finds elements by tag name.
:Args:
- name: The tag name the use when finding elements.
:Usage:
driver.find_elements_by_tag_name('foo')

28.find_elements_by_xpath(self,xpath)
Finds multiple elements by xpath.
:Args:
- xpath - The xpath locator of the elements to be found.
:Usage:
driver.find_elements_by_xpath("//div[contains(@class, 'foo')]")
29.forward(self)
##切换到下一页面
Goes one step forward in the browser history.
:Usage:
driver.forward()
30.get(self, url)

##打开url地址
Loads a web page in the current browser session.
31.get_cookie(self,name)
##获取指定名称的cookie
Get a single cookie by name. Returns the cookie if found, None if not.
:Usage:
driver.get_cookie('my_cookie')
32.get_cookies(self)
##获取所有的cookies
Returns a set of dictionaries, corresponding to cookies visible in the currentsession.
:Usage:

driver.get_cookies()
33.get_log(self,log_type)
Gets the log for a given log type
:Args:
- log_type: type of log that which will be returned
:Usage:
driver.get_log('browser')
driver.get_log('driver')
driver.get_log('client')
driver.get_log('server')
34.get_screenshot_as_base64(self)
##截图base64格式

Gets the screenshot of the current window as a base64 encoded string
which is useful in embedded images in HTML.
:Usage:
driver.get_screenshot_as_base64()
35.get_screenshot_as_file(self,filename)
##截图保存为指定文件名称
Gets the screenshot of the current window. Returns False if there is
any IOError, else returns True. Use full paths in your filename.
:Args:
- filename: The full path you wish to save your screenshot to.
:Usage:
driver.get_screenshot_as_file('/Screenshots/foo.png')

36.get_screenshot_as_png(self)
##截图为png格式二进制流
Gets the screenshot of the current window as a binary data.
:Usage:
driver.get_screenshot_as_png()
37.get_window_position(self,windowHandle='current')
Gets the x,y position of the current window.
:Usage:
driver.get_window_position()
38.get_window_size(self,windowHandle='current')
##获取窗口的宽高
Gets the width and height of the current window.

:Usage:
driver.get_window_size()
39.implicitly_wait(self,time_to_wait)
##隐式等待
Sets a sticky timeout to implicitly wait for an element to be found,
or a command to complete. This method only needs to be called one
time per session. To set the timeout for calls to
execute_async_script, see set_script_timeout.
:Args:
- time_to_wait: Amount of time to wait (in seconds)
:Usage:
driver.implicitly_wait(30)

40.maximize_window(self)
##最大化窗口
Maximizes the current window that webdriver is using
41.refresh(self)
##刷新页面
Refreshes the current page.
:Usage:
driver.refresh()
save_screenshot = get_screenshot_as_file(self, filename)
Gets the screenshot of the current window. Returns False if there is
any IOError, else returns True. Use full paths in your filename.
:Args:

- filename: The full path you wish to save your screenshot to.
:Usage:
driver.get_screenshot_as_file('/Screenshots/foo.png')
42.set_page_load_timeout(self,time_to_wait)
##设置页面加载超时时间
Set the amount of time to wait for a page load to complete
before throwing an error.
:Args:
- time_to_wait: The amount of time to wait
:Usage:
driver.set_page_load_timeout(30)
43.set_script_timeout(self,time_to_wait)

Set the amount of time that the script should wait during an
execute_async_script call before throwing an error.
:Args:
- time_to_wait: The amount of time to wait (in seconds)
:Usage:
driver.set_script_timeout(30)
44.set_window_position(self,x, y, windowHandle='current')
Sets the x,y position of the current window. (window.moveTo)
:Args:
- x: the x-coordinate in pixels to set the window position
- y: the y-coordinate in pixels to set the window position
:Usage:

driver.set_window_position(0,0)
45.set_window_size(self,width, height, windowHandle='current')
##设置窗口大小
Sets the width and height of the current window. (window.resizeTo)
:Args:
- width: the width in pixels to set the window to
- height: the height in pixels to set the window to
:Usage:
driver.set_window_size(800,600)
46.start_client(self)
Called before starting a new session. This method may be overridden
to define custom startup behavior.

start_session(self, desired_capabilities,browser_profile=None)
Creates a new session with the desired capabilities.
:Args:
- browser_name - The name of the browser to request.
- version - Which browser version to request.
- platform - Which platform to request the browser on.
- javascript_enabled - Whether the new session should support JavaScript.
- browser_profile - A selenium.webdriver.firefox.firefox_profile.FirefoxProfileobject. Only used if Firefox is requested.
47.stop_client(self)
Called after executing a quit command. This method may be overridden

to define custom shutdown behavior.
48.switch_to_active_element(self)
##切换到活动的元素上,一般失去焦点时候会用到
Deprecated use driver.switch_to.active_element
49.switch_to_alert(self)
##切换到alert弹出框上
Deprecated use driver.switch_to.alert
50.switch_to_default_content(self)
##切换到默认的主页面上
Deprecated use driver.switch_to.default_content
51.switch_to_frame(self,frame_reference)
##切换iframe

Deprecated use driver.switch_to.frame
52.switch_to_window(self,window_name)
##切换窗口
Deprecated use driver.switch_to.window
Data descriptors inherited fromselenium.webdriver.remote.webdriver.WebDriver:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)
53.application_cache
Returns a ApplicationCache Object to interact with the browser app cache

54.current_url
##获取当前页面的url地址
Gets the URL of the current page.
:Usage:
driver.current_url
55.current_window_handle
##获取当前页面handle
Returns the handle of the current window.
:Usage:
driver.current_window_handle
56.desired_capabilities

returns the drivers current desired capabilities being used
57.file_detector
58.log_types
Gets a list of the available log types
:Usage:
driver.log_types
59.mobile
60.name
##获取浏览器名称
Returns the name of the underlying browser for this instance.
:Usage:
- driver.name

61.orientation
Gets the current orientation of the device
:Usage:
orientation = driver.orientation
62.page_source
##获取页面源码
Gets the source of the current page.
:Usage:
driver.page_source
63.switch_to
##切换iframe,handle等方法
64.title

##获取页面title
Returns the title of the current page.
:Usage:
driver.title
65.window_handles
##获取所有的handle
Returns the handles of all windows within the current session.
:Usage:
driver.window_handles

 

小编后续有空再翻译下吧,英文水平有限。在学习过程中有遇到疑问的,可以加selenium(python+java) QQ群交流:232607095

 

2.29 练习题1:去掉页面动态窗

我们在浏览网页时经常会碰到各种花样的弹窗,在做UI自动化测试的时候势必要处理这些弹窗,这里就介绍一下目前前端界两种弹窗的处理方法。
一、alert弹窗 

 

 

这种弹窗是最简单的一种,Selenium里有自带的方法来处理它,用switch_to.alert先定位到弹窗,然后使用一系列方法来操作:
accept - 点击【确认】按钮

dismiss - 点击【取消】按钮(如有按钮)

send_keys - 输入内容(如有输入框)

这里举一个菜鸟教程上的一个例子:

http://www.runoob.com/try/try.php?filename=tryjs_alert,

在页面左边点击【显示警告框】就会弹出一个alert弹窗:

我们用以下代码就能实现切换至弹窗并点击【确定】按钮的效果:
al = driver.switch_to_alert() al.accept()
这里这个switch_to_alert()其实是旧写法,照理应该是用switch_to.alert(),但是新写法却会报错,目前猜测是版本问题,可能不支持新写法,这里就先用旧写法。

以下是完整代码,为了运行的时候看得清楚,我加了两处等待:

# encoding:utf-8 
from selenium import webdriver 
import time 
driver = webdriver.Firefox() 
driver.get("http://www.runoob.com/try/try.php?filename=tryjs_alert")  
driver.switch_to.frame("iframeResult")
driver.find_element_by_xpath("html/body/input").click() 
time.sleep(1) al = driver.switch_to_alert() 
time.sleep(1) al.accept()

二、自定义弹窗 
由于alert弹窗不美观,现在大多数网站都会使用自定义弹窗,使用Selenium自带的方法就驾驭不了了,此时就要搬出JS大法。这里举一个新世界教育官网首页的例子(http://sh.xsjedu.org): 

 

大家能看到,图中的这种弹窗就是现在主流的表现形式,处理这种弹窗可以利用HTML DOM Style 对象,有一个display属性,可以设置元素如何被显示,
详细解释可以参考http://www.w3school.com.cn/jsref/prop_style_display.asp。将display的值设置成none就可以去除这个弹窗了:
js = 'document.getElementById("doyoo_monitor").style.display="none";'

完整代码如下:

# encoding:utf-8
from selenium import webdriver
import time 
driver = webdriver.Firefox() 
driver.get("http://sh.xsjedu.org/")
time.sleep(1)
js='document.getElementById("doyoo_monitor").style.display="none";'
driver.execute_script(js)

是不是既简单又高效?

2.30 练习题2:定位百度-更多产品

练习题2:定位百度首页上更多产品里面的‘全部产品’

 

参考代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Firefox()
url = "https://www.baidu.com"
driver.get(url)
driver.maximize_window()
time.sleep(2)
e = driver.find_element_by_link_text("更多产品")
ActionChains(driver).move_to_element(e).perform()
time.sleep(1)
# ele = driver.find_element_by_name("tj_more")
# 经确认,是可以定位到元素的
# print ele.text
# 这一步点击失效了
# ele.click()  
# js大法好,完美解决ckick失效问题
js = "document.getElementsByName('tj_more')[0].click()"
driver.execute_script(js)

2.31 练习题3:获取百度联系词

前言
最近有小伙伴问百度输入后,输入框下方的联想词如何定位到,这个其实难度不大,用前面所讲的元素定位完全可以定位到的。
本篇以百度输入框输入关键字匹配后,打印出联想词汇。
一、定位输入框联想词
1.首先在百度输入框输入关键词,如:博客,然后输入框下方会自动匹配出关键词。
2.这时候可以用firebug工具定位到联想出来的词,可以看到下方匹配出来的词都有共同的class属性,这时候就可以全部定位到了。

二、打印全部匹配出来的词
1.通过get_attribute()方法获取到文本信息

三、点击其中一个
1.点击其中的一个联想词,如:第二个
2.这里可以先加一个判断,如果获取到了就点击,没获取到就不点击了,以免抛异常。
(如果想依次点击,用for循环就可以了)

三、参考代码

# coding:utf-8
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")
time.sleep(1)
driver.find_element_by_id("kw").send_keys(u"博客")
# 获取百度输入框的
time.sleep(1)
bd = driver.find_elements_by_class_name("bdsug-overflow")
for i in bd:
    print i.get_attribute("data-key")
# 点击其中的一个,如:第二个
if len(bd) > 1:
    bd[1].click()
    # 打印当前页面url
    print driver.current_url
else:
    print "未获取到匹配的词"

2.32 js几种定位方法总结

前言
本篇总结了几种js常用的定位元素方法,并用js点击按钮,对input输入框输入文本
 
一、以下总结了5种js定位的方法
除了id是定位到的是单个element元素对象,其它的都是elements返回的是list对象
1.通过id获取
document.getElementById(“id”)
2.通过name获取
 document.getElementsByName(“Name”)

返回的是list

3.通过标签名选取元素
document.getElementsByTagName(“tag”)
4.通过CLASS类选取元素
document.getElementsByClassName(“class”)
兼容性:IE8及其以下版本的浏览器未实现getElementsByClassName方法
5.通过CSS选择器选取元素
document.querySelectorAll(“css selector")
兼容性:IE8及其以下版本的浏览器只支持CSS2标准的选择器语法
 
二、id定位
1.定位博客首页的管理按钮:id="blog_nav_contact"

2.js的定位语法里面id定位获取的是单个元素对象,可以直接用click()方法点击元素

 

三、class定位
1.js里面class定位获取到是是一个list列表对象
2.操作元素的话通过下标取对应的第几个值,如果只用一个那就取下标[0]

3.定位到输入框,可以直接用value="xxx"方法输入内容

4.ByName和ByTagName跟上面class一样,都是定位的一组元素
 
四、CSS选择器
1.css选择器定位到的也是一组元素,语法跟前面学到的css语法是一样的

五、参考代码:

# coding: utf-8
from selenium import Webdriver
import time

driver = webdriver.Firefox()
driver.get("http://cnblogs.com/yoyoketang")

#定位首页管理按钮:id=blog_nav_contact
js1 = 'document.getElementById("blog_nav_contact")'.click;'
driver.execute_script(js1)

#输入账号
js2 = 'document.getElementsByClassName("input-text")[0].value="悠悠";'
driver.execute_script(js2)

#输入密码
js3 = 'document.getElementsByClassName("input-text")[1].value="xxx";'
driver.execute_script(js3)

#勾选记住密码
js4 = 'document.getElementsByName("remember_me")[0].click();'
driver.execute_script(js4)

#点击登录按钮
js5 = 'document.querySelectorAll(#signin)[0].click();'
driver.execute_script(js5)

 

2.33 定位的坑:class属性有空格

前言
有些class属性中间有空格,如果直接复制过来定位是会报错的InvalidSelectorException: Message:
The given selector u-label f-dn is either invalid or does not result in a WebElement. The following error occurred:
InvalidSelectorError: Compound class names not permitted
这个报错意思是说定位语法错了。
 
一、定位带空格的class属性
1.以126邮箱为例:http://mail.126.com/,定位账号输入框

 

2.如果直接复制过来用class属性定位是会报错的

 

二、class属性科普
1.class属性中间的空格并不是空字符串,那是间隔符号,表示的是一个元素有多个class的属性名称,在整个HTML文档,使用CSS中的同一个class类可能是一个或多个!
(class属性是比较特殊的一个,除了这个有多个属性外,其它的像name,id是没多个属性的)

2.想补习html基础知识的可以参考菜鸟教程:http://www.runoob.com/html/html-attributes.html
 
三、class定位
1.既然知道class属性有空格是多个属性了,那定位的时候取其中的一个就行(并且要唯一),也就是说class="j-inputtext dlemail",取j-inputtext 和dlemail都是可以的,这样这个class属性在页面上唯一就行

2.那么问题来了:如何才知道这个元素的某个属性是不是在页面上是唯一的呢?
 
四、判断元素唯一性
1.F12切换到HTML界面,在搜索框输入关键字搜索,如:j-inputtext,然后按回车搜索,看页面上有几个class属性中有j-inputtext这个属性的,就知道是不是唯一的了。

五、class属性不唯一怎么办
1.如果这个class的多个属性都不是唯一的咋办呢,元素不唯一也不用怕,可以用复数定位,把所有的相同元素定位出来,按下标取第几个就行。

 

六、css定位
1.css来定位class属性的元素前面加个点(.)就行,然后空格变成点(.)就能定位了
2.当然css也可以取class属性的其中一个属性(页面上唯一的)来定位,定位方法是灵活多变的

 

七、参考代码

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://mail.126.com/")
driver.implicitly_wait(20)
driver.switch_to.frame("x-URS-iframe")
# 方法一:取单个class属性
driver.find_element_by_class_name("dlemail").send_keys("yoyo")
driver.find_element_by_class_name("dlpwd").send_keys("12333")
# 方法二:定位一组取下标定位(乃下策)
# driver.find_elements_by_class_name("j-inputtext")[0].send_keys("yoyo")
# driver.find_elements_by_class_name("j-inputtext")[1].send_keys("12333")
# 方法三:css定位
# driver.find_element_by_css_selector(".j-inputtext.dlemail").send_keys("yoyo")
# driver.find_element_by_css_selector(".j-inputtext.dlpwd").send_keys("123")
# 方法四:取单个class属性也是可以的
# driver.find_element_by_css_selector(".dlemail").send_keys("yoyo")
# driver.find_element_by_css_selector(".dlpwd").send_keys("123")

2.34 jquery定位(简直逆天)

前言
元素定位可以说是学自动化的小伙伴遇到的一道门槛,学会了定位也就打通了任督二脉,前面分享过selenium的18般武艺,再加上五种js的定位大法。
这些还不够的话,今天再分享一个定位神器jquery,简直逆天了!
 
一、jquery搜索元素
1.按F12进控制台
2.点全部按钮
3.右侧如果没出现输入框,就点下小箭头按钮
4.输入框输入jquery定位语法,如:$("#input1")

5.点运行按钮
6.左边会出现定位到的元素,如果有多个会以list列表的形式展示出。

 

二、jquery定位语法
1.jquery语法可以学下w3school的教程:http://www.w3school.com.cn/jquery/jquery_syntax.asp

2.格式如下:
$(selector).action()
--selector:这里的定位语法和css的定位语法是一致的,如:id就是#,class就是点(.),tag标签名前面就无符号
--action:这个是定位元素之后的操作行为事件,如click

三、jquery行为
1.发送文本语法:$(selector).val(输入文本的值)
2.清空文本语法:$(selector).val('')   # 空字符串,两个单引号
3.点击按钮:$(selector).click()

四、参考脚本

# coding:utf-8
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get("https://passport.cnblogs.com/user/signin")
driver.implicitly_wait(20)
# 输入账号
username = "$('#input1').val('上海-悠悠')"
driver.execute_script(username)
# 清空文本
# time.sleep(5)
# clear = "$('#input1').val('')"
# driver.execute_script(clear)
# 输入密码
psw = "$('#input2').val('yoyo')"
driver.execute_script(psw)
# 点击登录按钮
button = "$('#signin').click()"
driver.execute_script(button)

 

3.1 unittest简介

前言
(python基础比较弱的,建议大家多花点时间把基础语法学好,这里有套视频,可以照着练习下:http://pan.baidu.com/s/1i44jZdb 密码:92fs)
熟悉java的应该都清楚常见的单元测试框架Junit和TestNG,这个招聘的需求上也是经常见到的。python里面也有单元测试框架-unittest,相当于是一个python版的junit。
python里面的单元测试框架除了unittest,还有一个pytest框架,这个用的比较少,后面有空再继续分享。

3.1.1 unittest简介

1).先导入unittest

2).用help函数查看源码解析
3).查看描述:
Python unit testing framework, based on Erich Gamma's JUnit and KentBeck's Smalltalk testing framework.
翻译:python的单元测试框架,是基于java的junit测试框架。

3.1.2 简单用法

1).可以把上图的这段代码copy出来,单独运行,看看测试结果。

   Simple usage:

import unittest
    
class IntegerArithmeticTestCase(unittest.TestCase):
       deftestAdd(self):  ## test method names begin 'test*'
            self.assertEqual((1 + 2), 3)
            self.assertEqual(0 + 1, 1)
       deftestMultiply(self):
            self.assertEqual((0 * 10), 0)
            self.assertEqual((5 * 8), 40)
    
if __name__ == '__main__':
       unittest.main()

2).第一行是导入unittest这个模块
3).class这一行是定义一个测试的类,并继承unittest.TestCase这个类
4).接下来是定义了两个测试case名称:testAdd和testMultiply
5).注释里面有句话很重要,这个要敲下黑板记笔记了:## test method names begin 'test*'
--翻译:测试用例的名称要以test开头
6).然后是断言assert,这里的断言方法是assertEqual-判断两个是否相等,

这个断言可以是一个也可以是多个
7).if下面的这个unittest.main()是运行主函数,运行后会看到测试结果(跑了两个用例耗时0.000秒,两个用例都通过):

----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK


3.1.3 小试牛刀

1).上面的两个案例是加法和乘法,我们可以写个case试下减法和除法。
2).有很多小伙伴不知道断言怎么写,断言其实就是拿实际结果和期望结果去对比,对比的方法很多,这里只是举的最简单的一个判断相等的方法。

3).最后运行结果,第二个是失败的,失败原因:AssertionError: 3 != 3.5
F.
======================================================================
FAIL: testDivide (__main__.Test)

这里是测试除法
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:/test/web-project/p.py", line 14, in testDivide
    self.assertEqual(result, hope)
AssertionError: 3 != 3.5
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)

3.1.4 前置和后置

1).setUp:在写测试用例的时候,每次操作其实都是基于打开浏览器输入对应网址这些操作,这个就是执行用例的前置条件。

2).tearDown:执行完用例后,为了不影响下一次用例的执行,一般有个数据还原的过程,这就是执行用例的后置条件。
3).很多人执行完用例,都不去做数据还原,以致于下一个用例执行失败,这就是不喜欢擦屁股的事情,习惯不好。
4).前置和后置都是非必要的条件,如果没有也可以写pass

3.1.5 博客案例

1).打开博客首页为例,写一个简单的case
2).判断title完全等于期望结果
3).运行通过,下面会有一个绿条显示:1 test passed

3.1.6 参考代码

# coding=utf-8
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
import time
import unittest
class Blog(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.get("http://www.cnblogs.com/yoyoketang")
    def test_blog(self):
        time.sleep(3)
        result = EC.title_is(u'上海-悠悠 - 博客园')(self.driver)
        print result
        self.assertTrue(result)
    def tearDown(self):
        self.driver.quit()
if __name__ == "__main__":
    unittest.main()

3.2 unittest执行顺序

前言
很多初学者在使用unittest框架时候,不清楚用例的执行顺序到底是怎样的。对测试类里面的类和方法分不清楚,不知道什么时候执行,什么时候不执行。
本篇通过最简单案例详细讲解unittest执行顺序。
一、案例分析
1.先定义一个测试类,里面写几个简单的case

# coding:utf-8
import unittest
import time
class Test(unittest.TestCase):
    def setUp(self):
        print "start!"
    def tearDown(self):
        time.sleep(1)
        print "end!"
    def test01(self):
        print "执行测试用例01"
    def test03(self):
        print "执行测试用例03"
    def test02(self):
        print "执行测试用例02"
    def addtest(self):
        print "add方法"
if __name__ == "__main__":
    unittest.main()

二、执行结果

D:\test\python2\python.exe D:/test/test01.py
start!
执行测试用例01
end!
start!
执行测试用例02
end!

start!

执行测试用例03

end!
.
----------------------------------------------------------------------
Ran 3 tests in 3.001s
OK

 

三、结果分析
1.执行顺序:

start!-执行测试用例01-end!
start!-执行测试用例02-end!
start!-执行测试用例03-end!


2.从执行结果可以看出几点:
--先执行的前置setUp,然后执行的用例(test*),最后执行的后置tearDown。
--测试用例(test*)的执行顺序是根据01-02-03执行的,也就是说根据用例名称来顺序执行的。
--addtest(self)这个方法没执行,说明只执行test开头的用例。

四、selenium实例
1.具体实例参考 登录方法(参数化)

# coding:utf-8
from selenium import webdriver
import unittest
import time
class Bolg(unittest.TestCase):
    u'''登录博客'''
    def setUp(self):
        self.driver = webdriver.Firefox()
        url = "https://passport.cnblogs.com/user/signin"
        self.driver.get(url)
        self.driver.implicitly_wait(30)
    def login(self, username, psw):
        u'''这里写了一个登录的方法,账号和密码参数化'''
        self.driver.find_element_by_id("input1").send_keys(username)
        self.driver.find_element_by_id("input2").send_keys(psw)
        self.driver.find_element_by_id("signin").click()
        time.sleep(3)
    def is_login_sucess(self):
        u'''判断是否获取到登录账户名称'''
        try:
            text = self.driver.find_element_by_id("lnk_current_user").text
            print text
            return True
        except:
            return False
    def test_01(self):
        u'''登录案例参考:账号,密码自己设置'''
        self.login(u"上海-悠悠", u"xxxx"# 调用登录方法
        # 判断结果
        result = self.is_login_sucess()
        self.assertTrue(result)
    def test_02(self):
        u'''登录案例参考:账号,密码自己设置'''
        self.login(u"上海-悠悠", u"xxxx"# 调用登录方法
        # 判断结果
        result = self.is_login_sucess()
        self.assertTrue(result)
    def tearDown(self):
        self.driver.quit()

if __name__ == "__main__":
    unittest.main()

3.3 unittest批量执行

我们在写用例的时候,单个脚本的用例好执行,那么多个脚本的时候,如何批量执行呢?这时候就需要用到unittet里面的discover方法来加载用例了。
加载用例后,用unittest里面的TextTestRunner这里类的run方法去一次执行多个脚本的用例。
一、新建测试项目
1.pycharm左上角File>New Projetc>Pure Python,在location位置命名一个测试工程的名称:yoyotest,然后保存

 

 2.选中刚才新建的工程右键>New>Python Package>新建一个case文件夹

3.重复第2步的操作,新建一个case的文件夹,在里面添加一个baidu和一个blog的文件夹,里面分别有两个用例的脚本,如下图所示。
test_01,test_02,test_03,test_04是我们写用例的脚本
4.test_01创建完后,打开脚本,写入用例

5.在yoyotest这个项目下面创建一个脚本run_all_case.py,接下来用这个脚本去批量执行所有的用例。

二、diascover加载测试用例
1.discover方法里面有三个参数:

-case_dir:这个是待执行用例的目录。
-pattern:这个是匹配脚本名称的规则,test*.py意思是匹配test开头的所有脚本。
-top_level_dir:这个是顶层目录的名称,一般默认等于None就行了。

2.discover加载到的用例是一个list集合,需要重新写入到一个list对象testcase里,这样就可以用unittest里面的TextTestRunner这里类的run方法去执行。

3.运行后结果如下,就是加载到的所有测试用例了:

<unittest.suite.TestSuite tests=[<baidu.test_01.Test testMethod=test01>, <baidu.test_01.Test testMethod=test02>, <baidu.test_01.Test testMethod=test03>, <baidu.test_02.Test 
testMethod=test01>, <baidu.test_02.Test testMethod=test02>, <baidu.test_02.Test testMethod=test03>, <bolg.test_03.Test testMethod=test01>, <bolg.test_03.Test testMethod=test02>, 
<bolg.test_03.Test testMethod=test03>, <bolg.test_04.Test testMethod=test01>, <bolg.test_04.Test testMethod=test02>, <bolg.test_04.Test testMethod=test03>]>

三、run测试用例
1.为了更方便的理解,可以把上面discover加载用例的方法封装下,写成一个函数

2.参考代码:

# coding:utf-8
import unittest
import os
# 用例路径
case_path = os.path.join(os.getcwd(), "case")
# 报告存放路径
report_path = os.path.join(os.getcwd(), "report")
def all_case():
    discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None)
    print(discover)
    return discover
if __name__ == "__main__":
    runner = unittest.TextTestRunner()
    runner.run(all_case())

3.4 unittest之装饰器(@classmethod)

前言
前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间。
于是就想是不是可以只打开一次浏览器,执行完用例再关闭呢?这就需要用到装饰器(@classmethod)来解决了。
 
一、装饰器
1.用setUp与setUpClass区别

setup():每个测试case运行前运行
teardown():每个测试case运行完后执行
setUpClass():必须使用@classmethod 装饰器,所有case运行前只运行一次
tearDownClass():必须使用@classmethod装饰器,所有case运行完后只运行一次

2.@是修饰符,classmethod是python里的类方法

 

二、执行顺序
1.用类方法写几个简单case,可以对比这篇:Selenium2+python自动化52-unittest执行顺序

# coding:utf-8
import unittest
import time

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print "start!"
   
    @classmethod
    def tearDownClass(cls):
        time.sleep(1)
        print "end!"
    def test01(self):
        print "执行测试用例01"
    def test03(self):
        print "执行测试用例03"
    def test02(self):
        print "执行测试用例02"
    def addtest(self):
        print "add方法"


if __name__ == "__main__":
    unittest.main()

2.从执行结果可以看出,前置和后置在执行用例前只执行了一次。
start!
执行测试用例01
执行测试用例02
执行测试用例03
...end!

----------------------------------------------------------------------
Ran 3 tests in 1.001s

三、selenium实例
1.可以把打开浏览器操作放到前置setUpClass(cls)里,这样就可以实现打开一次浏览器,执行多个case了

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
import unittest

class BolgHome(unittest.TestCase):

    u'''博客首页'''
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Firefox()
        url = "http://www.cnblogs.com/yoyoketang/"
        cls.driver.get(url)
        cls.driver.implicitly_wait(30)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_01(self):
        u'''验证元素存在:博客园'''
        locator = ("id", "blog_nav_sitehome")
        text = u"博客园"
        result = EC.text_to_be_present_in_element(locator, text)(self.driver)
        self.assertTrue(result)
    def test_02(self):
        u'''验证元素存在:首页'''
        locator = ("id", "blog_nav_myhome")
        text = u"首页"
        result = EC.text_to_be_present_in_element(locator, text)(self.driver)
        self.assertTrue(result)

if __name__ == "__main__":
    unittest.main()

       上述代码运行的顺序就是从上至下,而不再是每次执行完成一个testcase之后,执行一次teardownClass再进行下一个testcase。

  这样一来,退出浏览器仅仅执行一次即可,这样有一个不好的地方就是,teardownClass这个函数不能再进行每个测试用例的终结操作,比如:修改个人信息后恢复到登录成功后的状态,对当前测试用例的异常处理等。

  后来,本人尝试在tearDownClass后增加如下代码:

def tearDown(self):
    self.driver.refresh()
    self.assertEqual( [], self.verificationErrors )

     然后,果然每次测试用完成都会刷新当前页面,这样一来,每一个testcase的用例都能被终结函数tearDown结束,最后再执行tearDownClass关闭测试浏览器。

     需要说明的是:

  @classmethod是python本身的装饰器,所以他不要使用隶属于unittest框架下断言assertEqual。

  unittest本身也带有装饰器unittest.skip(),专门用于跳过testcase的装饰器,其用法如下:

     

 @unittest.skip(reason), skip装饰器:直接跳过装饰下的testcase,reason用来说明原因,下同。

 @unittest.skipIf(condition,reason), skipIf装饰器:condition条件为True时,跳过装饰下的testcase,计入skip的testcase执行次数。

 @unittest.skipUnless(condition,reason),skipUnless装饰器:condition条件为False时,跳过装饰下的testcase,计入skip的testcase执行次数。

 @unittest.expectedFailure(), expectedFailure装饰器:执行装饰下的testcase,执行失败则跳过该testcase,计入expected下成败的testcase次数。

  一般来讲,使用@unittest.skipIf 或者 @unittest.skipUnless,应该也能实现@classmethod装饰器的效果, 想来只是实现起来相对来说较为麻烦。

3.5 unittest生成测试报告HTMLTestRunner

前言
批量执行完用例后,生成的测试报告是文本形式的,不够直观,为了更好的展示测试报告,最好是生成HTML格式的。
unittest里面是不能生成html格式报告的,需要导入一个第三方的模块:HTMLTestRunner
备注:(以下是python2.7的HTMLTestRunner,python3.x的HTMLTestRunner需要自己稍做修改,可以在这里下载:http://pan.baidu.com/s/1hs5OXNY)
 
一、导入HTMLTestRunner
1.这个模块下载不能通过pip安装了,只能下载后手动导入,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html

2.Download下HTMLTestRunner.py文件就是我们需要下载的包。
3.下载后手动拖到python安装文件的Lib目录下

二、demo解析

1.下载Download下的第二个文件test_HTMLTestRunner.py,这个就是官方给的一个测试demo了,从这个文件可以找到该模块的用法。
2.找到下图这段,就是官方给的一个demo了,test_main()里上半部分就是加载测试case,我们不需要搞这么复杂。
参考前面一篇内容就行了Selenium2+python自动化53-unittest批量执行(discover)
3.最核心的代码是下面的红色区域,这个就是本篇的重点啦。

三、生成html报告
1.我们只需把上面红色区域代码copy到上一篇的基础上稍做修改就可以了,这里主要有三个参数:
--stream:测试报告写入文件的存储区域
--title:测试报告的主题
--description:测试报告的描述
2.report_path是存放测试报告的地址

四、测试报告详情
1.找到测试报告文件,用浏览器打开,点开View里的Detail可以查看详情描述。

2.为了生成带中文描述的测试用例,可以在case中添加注释,如在test_01的脚本添加如下注释:

class Test(unittest.TestCase):
    def setUp(self):
        print "start!"
    def tearDown(self):
        time.sleep(1)
        print "end!"
    def test01(self):
        u'''测试登录用例,账号:xx 密码xx'''
        print "执行测试用例01"
    def test03(self):
        u'''测试登搜索用例,关键词:xxx'''
        print "执行测试用例03"

 

3.重新运行后查看测试报告

 

五、参考代码:
1.我下面的代码文件路径用的相对路径,这样就避免代码换个地址找不到路径的情况了

# coding:utf-8
import unittest
import os
import HTMLTestRunner
# 用例路径
case_path = os.path.join(os.getcwd(), "case")
# 报告存放路径
report_path = os.path.join(os.getcwd(), "report")
def all_case():
    discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None)
    print(discover)
    return discover

if __name__ == "__main__":
    # runner = unittest.TextTestRunner()
    # runner.run(all_case())
    # html报告文件路径
    report_abspath = os.path.join(report_path, "result.html")
    fp = open(report_abspath, "wb")
    runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u'自动化测试报告,测试结果如下:', description=u'用例执行情况:')
    # 调用add_case函数返回值
    runner.run(all_case())
    fp.close()

3.6 html报告乱码问题优化

前言
python2用HTMLTestRunner生成测试报告时,有中文输出情况会出现乱码,这个主要是编码格式不统一,改下编码格式就行。
下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html
 
一、中文乱码
1.测试报告中,msg自定义异常内容有中文情况会出现乱码,如下图所示

 

二、修改编码
1.找到HTMLTestRunner.py文件,搜索:uo =
2.找到红色区域设置编码的两个地方

3.注释掉红色区域这两个设置,重新添加编码格式为:uo = o.decode('utf-8')   ue = e.decode('utf-8')

4.修改好之后记得保存,重新运行,乱码问题就解决了

三、python3报告问题
1.python3的小伙伴直接用这个下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html的文件,是不能直接生成报告的,需要稍做修改
2.修改后的源文件已经打包:https://files.cnblogs.com/files/zidonghua/HTMLTestRunner%28%E7%8B%AC%E5%AE%B6%E5%90%88%E9%9B%86%29.zip

(另外很多朋友在简单的示例代码中,生成不了测试报告,我来上传一份完整项目模板:https://files.cnblogs.com/files/zidonghua/%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E6%90%9E%E4%B8%8D%E5%87%BA%E6%9D%A5%E7%9A%84%E7%9C%8B%E8%BF%99%E9%87%8C.rar

3.7 unittest之断言

前言
在测试用例中,执行完测试用例后,最后一步是判断测试结果是pass还是fail,自动化测试脚本里面一般把这种生成测试结果的方法称为断言(assert)。
用unittest组件测试用例的时候,断言的方法还是很多的,下面介绍几种常用的断言方法:assertEqual、assertIn、assertTrue

3.7.1 简单案例


1).下面写了4个case,其中第四个是执行失败的

# coding:utf-8
import unittest

class Test(unittest.TestCase):
    def test01(self):
        '''判断 a == b '''
        a = 1
        b = 1
        self.assertEqual(a, b)
    def test02(self):
        '''判断 a in b'''
        a = "hello"
        b = "hello world!"
        self.assertIn(a, b)

    def test03(self):
        '''判断 a isTrue '''
        a = True
        self.assertTrue(a)
    def test04(self):
        '''失败案例'''
        a = "上海-悠悠"
        b = "yoyo"
        self.assertEqual(a, b)

if __name__ == "__main__":
    unittest.main()

2).执行结果如下
Failure
Expected :'\xe4\xb8\x8a\xe6\xb5\xb7-\xe6\x82\xa0\xe6\x82\xa0'
Actual   :'yoyo'
 <Click to see difference>
Traceback (most recent call last):
  File "D:\test\yoyotest\kecheng\test12.py", line 27, in test04
    self.assertEqual(a, b)
AssertionError: '\xe4\xb8\x8a\xe6\xb5\xb7-\xe6\x82\xa0\xe6\x82\xa0' != 'yoyo'
3.执行的结果,中文编码不对,没正常显示中文,遇到这种情况,可以自定义异常输出

3.7.2 自定义异常

1).以assertEqual为例分析:
assertEqual(self, first, second, msg=None)
    Fail if the two objects are unequal as determined by the'=='
    operator.
2).翻译:如果两个对象不能相等,就返回失败,相当于return: first==second
3).这里除了相比较的两个参数first和second,还有第三个参数msg=None,这个msg参数就是遇到异常后自定义输出信息

3.7.3 unittest常用的断言方法

1).assertEqual(self, first, second,msg=None)

--判断两个参数相等:first == second
2).assertNotEqual(self, first, second,msg=None)
--判断两个参数不相等:first != second
3).assertIn(self, member, container,msg=None)
--判断是字符串是否包含:member in container
4).assertNotIn(self, member,container, msg=None)
--判断是字符串是否不包含:member not in container
5).assertTrue(self, expr, msg=None)
--判断是否为真:expr is True
6).assertFalse(self, expr, msg=None)
--判断是否为假:expr is False

7).assertIsNone(self, obj, msg=None)
--判断是否为None:objis None
8).assertIsNotNone(self, obj,msg=None)
--判断是否不为None:obj is not None
3.7.4 unittest所有断言方法
1).下面是unittest框架支持的所有断言方法,有兴趣的同学可以慢慢看。(官方资料)

|  assertAlmostEqual(self, first, second, places=None, msg=None,delta=None)
|      Fail if the two objects are unequal asdetermined by their
|      difference rounded to the given number ofdecimal places
|      (default 7) and comparing to zero, or bycomparing that the
|      between the two objects is more than the givendelta.
|      
|      Note that decimal places (from zero) areusually not the same
|      as significant digits (measured from the mostsignficant digit).
|      
|      If the two objects compare equal then they willautomatically
|      compare almost equal.
|  
|  assertAlmostEquals = assertAlmostEqual(self, first, second,places=None, msg=None, delta=None)
|  
|  assertDictContainsSubset(self, expected, actual, msg=None)
|      Checks whether actual is a superset ofexpected.
|  
|  assertDictEqual(self, d1, d2, msg=None)
|  
|  assertEqual(self, first, second, msg=None)
|      Fail if the two objects are unequal asdetermined by the '=='
|      operator.
|  
|  assertEquals = assertEqual(self, first, second, msg=None)
| 
|  assertFalse(self, expr, msg=None)
|      Check that the expression is false.
|  
|  assertGreater(self, a, b, msg=None)
|      Just like self.assertTrue(a > b), but with anicer default message.
|  
|  assertGreaterEqual(self, a, b, msg=None)
|      Just like self.assertTrue(a >= b), but witha nicer default message.
|  
|  assertIn(self, member, container, msg=None)
|      Just like self.assertTrue(a in b), but with anicer default message.
|  
|  assertIs(self, expr1, expr2, msg=None)
|      Just like self.assertTrue(a is b), but with anicer default message.
|  
|  assertIsInstance(self, obj, cls, msg=None)
|      Same as self.assertTrue(isinstance(obj, cls)),with a nicer
|      default message.
|  
|  assertIsNone(self, obj, msg=None)
|      Same as self.assertTrue(obj is None), with anicer default message.
|  
|  assertIsNot(self, expr1, expr2, msg=None)
|      Just like self.assertTrue(a is not b), but witha nicer default message.
|  
|  assertIsNotNone(self, obj, msg=None)
|      Included for symmetry with assertIsNone.
|  
|  assertItemsEqual(self, expected_seq, actual_seq, msg=None)
|      An unordered sequence specific comparison. Itasserts that
|      actual_seq and expected_seq have the sameelement counts.
|      Equivalent to::
|      
|         self.assertEqual(Counter(iter(actual_seq)),
|                          Counter(iter(expected_seq)))
|      
|      Asserts that each element has the same count inboth sequences.
|      Example:
|          - [0, 1, 1] and [1, 0,1] compare equal.
|          - [0, 0, 1] and [0, 1]compare unequal.
|  
|  assertLess(self, a, b, msg=None)
|      Just like self.assertTrue(a < b), but with anicer default message.
|  
|  assertLessEqual(self, a, b, msg=None)
|      Just like self.assertTrue(a <= b), but witha nicer default message.
|  
|  assertListEqual(self, list1, list2, msg=None)
|      A list-specific equality assertion.
|      
|      Args:
|          list1: The first listto compare.
|          list2: The second listto compare.
|          msg: Optional messageto use on failure instead of a list of
|                 differences.
|  
|  assertMultiLineEqual(self, first, second, msg=None)
|      Assert that two multi-line strings are equal.
|  
|  assertNotAlmostEqual(self, first, second, places=None, msg=None,delta=None)
|      Fail if the two objects are equal as determinedby their
|      difference rounded to the given number ofdecimal places
|      (default 7) and comparing to zero, or bycomparing that the
|      between the two objects is less than the givendelta.
|      
|      Note that decimal places (from zero) areusually not the same
|      as significant digits (measured from the mostsignficant digit).
|      
|      Objects that are equal automatically fail.
|  
|  assertNotAlmostEquals = assertNotAlmostEqual(self, first, second, places=None,msg=None, delta=None)
|  
|  assertNotEqual(self, first, second, msg=None)
|      Fail if the two objects are equal as determinedby the '!='
|      operator.
|  
|  assertNotEquals = assertNotEqual(self, first, second, msg=None)
|  
|  assertNotIn(self, member, container, msg=None)
|      Just like self.assertTrue(a not in b), but witha nicer default message.
|  
|  assertNotIsInstance(self, obj, cls, msg=None)
|      Included for symmetry with assertIsInstance.
|  
|  assertNotRegexpMatches(self, text, unexpected_regexp, msg=None)
|      Fail the test if the text matches the regularexpression.
|  
|  assertRaises(self, excClass, callableObj=None, *args, **kwargs)
|      Fail unless an exception of class excClass israised
|      by callableObj when invoked with arguments argsand keyword
|      arguments kwargs. If a different type ofexception is
|      raised, it will not be caught, and the testcase will be
|      deemed to have suffered an error, exactly asfor an
|      unexpected exception.
|      
|      If called with callableObj omitted or None,will return a
|      context object used like this::
|      
|           withself.assertRaises(SomeException):
|              do_something()
|      
|      The context manager keeps a reference to theexception as
|      the 'exception' attribute. This allows you toinspect the
|      exception after the assertion::
|      
|          withself.assertRaises(SomeException) as cm:
|             do_something()
|          the_exception =cm.exception
|         self.assertEqual(the_exception.error_code, 3)
|  
|  assertRaisesRegexp(self, expected_exception, expected_regexp,callable_obj=None, *args, **kwargs)
|      Asserts that the message in a raised exceptionmatches a regexp.
|      
|      Args:
|          expected_exception:Exception class expected to be raised.
|          expected_regexp: Regexp(re pattern object or string) expected
|                 to be found in error message.
|          callable_obj: Functionto be called.
|          args: Extra args.
|          kwargs: Extra kwargs.
|  
|  assertRegexpMatches(self, text, expected_regexp, msg=None)
|      Fail the test unless the text matches theregular expression.
|  
|  assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None)
|      An equality assertion for ordered sequences(like lists and tuples).
|      
|      For the purposes of this function, a validordered sequence type is one
one
|      which can be indexed, has a length, and has anequality operator.
|      
|      Args:
|          seq1: The firstsequence to compare.
|          seq2: The secondsequence to compare.
|          seq_type: The expecteddatatype of the sequences, or None if no
|                 datatype should be enforced.
|          msg: Optional messageto use on failure instead of a list of
|                 differences.
|  
|  assertSetEqual(self, set1, set2, msg=None)
|      A set-specific equality assertion.
|      
|      Args:
|          set1: The first set tocompare.
|          set2: The second set tocompare.
|          msg: Optional messageto use on failure instead of a list of
|                 differences.
|      
|      assertSetEqual uses ducktyping to supportdifferent types of sets, and
|      is optimized for sets specifically (parametersmust support a
|      difference method).
|  
|  assertTrue(self, expr, msg=None)
|      Check that the expression is true.
|  
|  assertTupleEqual(self, tuple1, tuple2, msg=None)
|      A tuple-specific equality assertion.
|      
|      Args:
|          tuple1: The first tupleto compare.
|          tuple2: The secondtuple to compare.
|          msg: Optional messageto use on failure instead of a list of
|                 differences.

3.8 搭建简易项目

前言
到unittest这里基本上可以搭建一个简易的项目框架了,我们可以用一条run_main.py脚本去控制执行所有的用例,并生成报告,发送邮件一系列的动作
 
一、新建工程
1.打开pycharm左上角File>New Project,在Location位置输入项目名称:D:\test\test_blog
2.创建之后,选择Opin in current window就可以了

二、项目结构
1.在测试工程下,创建文件夹,一定要选Python Package的方式创建,要不然后面导入自己写的模块会出现各种问题

2.在工程下创建以下几个文件
--test_case           这个文件夹放所有测试用例
----blog_home       可以按功能用例模块划分
---------test_home

---------test_home_1     测试用例以test开头命名
----blog_login
---------test_login
----blog_set
---------test_set
--test_report
--run_main.py          注意这个脚本放文件根目录

三、run_main
1.run_main.py这个脚本里面写主函数,控制执行所有的用例,最终我们只需要运行这个脚本就可以了

2.我们也可以在cmd里执行这个脚本文件,这样就不用依赖pycharm去执行了(后续用jenkins执行,也是同样道理,启动cmd执行脚本)
>>d:
>>cd test\test_blog
>>python run_main.py

 

 3.run_main.py源代码在下一章节。

3.8-1 生成报告的源码下载(兼容python2和3)

生成测试项目报告模板:

https://files.cnblogs.com/files/zidonghua/%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E9%A1%B9%E7%9B%AE%E6%A8%A1%E6%9D%BF.zip

测试报告搞不出来的看这里:

https://files.cnblogs.com/files/zidonghua/%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E6%90%9E%E4%B8%8D%E5%87%BA%E6%9D%A5%E7%9A%84%E7%9C%8B%E8%BF%99%E9%87%8C2.rar

 

3.9 run_main.py源码(兼容python2和3)

以下代码在python2和python3上都跑通过,python3只需注释掉上面红色框框区域代码就行(最后一步发送邮箱代码,我注释掉了)。

# coding=utf-8
import unittest
import time
import HTMLTestRunner
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
import os
####下面三行代码python2报告出现乱码时候可以加上####
import sys
reload(sys)
sys.setdefaultencoding('utf8')


# 这个是优化版执行所有用例并发送报告,分四个步骤
# 第一步加载用例
# 第二步执行用例
# 第三步获取最新测试报告
# 第四步发送邮箱 (这一步不想执行的话,可以注释掉最后面那个函数就行)

def add_case(case_path, rule):
    '''加载所有的测试用例'''
    testunit = unittest.TestSuite()
    # 定义discover方法的参数
    discover = unittest.defaultTestLoader.discover(case_path,
                                                  pattern=rule,
                                                  top_level_dir=None)
    # discover方法筛选出来的用例,循环添加到测试套件中
    # for test_suite in discover:
    #     for test_case in test_suite:
    #         testunit.addTests(test_case)
    #         print testunit
    testunit.addTests(discover)  # 直接加载discover
    print(testunit)
    return testunit

def run_case(all_case, report_path):
    '''执行所有的用例, 并把结果写入测试报告'''
    now = time.strftime("%Y_%m_%d %H_%M_%S")
    report_abspath = os.path.join(report_path, now+"result.html")
    # report_abspath = "D:\\web_project\\report\\"+now+"result.html"
    fp = open(report_abspath, "wb")
    runner = HTMLTestRunner.HTMLTestRunner(stream=fp,
                                           title=u'自动化测试报告,测试结果如下:',
                                           description=u'用例执行情况:')
    # 调用add_case函数返回值
    runner.run(all_case)
    fp.close()

def get_report_file(report_path):
    '''获取最新的测试报告'''
    lists = os.listdir(report_path)
    lists.sort(key=lambda fn: os.path.getmtime(os.path.join(report_path, fn)))
    print (u'最新测试生成的报告: '+lists[-1])
    # 找到最新生成的报告文件
    report_file = os.path.join(report_path, lists[-1])
    return report_file

def send_mail(sender, psw, receiver, smtpserver, report_file):
    '''发送最新的测试报告内容'''
    # 读取测试报告的内容
    with open(report_file, "rb") as f:
        mail_body = f.read()
    # 定义邮件内容
    msg = MIMEMultipart()
    body = MIMEText(mail_body, _subtype='html', _charset='utf-8')
    msg['Subject'] = u"自动化测试报告"
    msg["from"] = sender
    msg["to"] = psw
    # 加上时间戳
    # msg["date"] = time.strftime('%a, %d %b %Y %H_%M_%S %z')
    msg.attach(body)
    # 添加附件
    att = MIMEText(open(report_file, "rb").read(), "base64", "utf-8")
    att["Content-Type"] = "application/octet-stream"
    att["Content-Disposition"] = 'attachment; filename= "report.html"'
    msg.attach(att)
    # 登录邮箱
    smtp = smtplib.SMTP()
    # 连接邮箱服务器
    smtp.connect(smtpserver)
    # 用户名密码
    smtp.login(sender, psw)
    smtp.sendmail(sender, receiver, msg.as_string())
    smtp.quit()
    print('test report email has send out !')

if __name__ == "__main__":
    # 测试用例的路径、匹配规则
    case_path = "D:\\test\\newp\\case"
    rule = "test*.py"
    all_case = add_case(case_path, rule)   # 1加载用例
    # 生成测试报告的路径
    report_path = "D:\\test\\newp\\report"
    run_case(all_case, report_path)        # 2执行用例
    # 获取最新的测试报告文件
    report_file = get_report_file(report_path)  # 3获取最新的测试报告
    #邮箱配置
    sender = "yoyo@xxx.com"
    psw = "xxx"
    # 收件人多个时,中间用逗号隔开,如'a@xx.com,b@xx.com'
    receiver = "yoyo@xxx.com"
    smtp_server = 'smtp.xxx.com'
    # send_mail(sender, psw, receiver, smtp_server, report_file)  # 4最后一步发送报告,需要发邮件就取消注释。

3.10 练习题1:模块导入(登录方法)

以登录博客园为案例https://passport.cnblogs.com/user/signin
一、登录方法封装
1.我们可以把登录写成一个登录类,里面写个登录的方法,保存文件为login_pub.py

# coding:utf-8
'''
这里写了一个登录博客园的类,登录博客园方法
'''
class Login_Blog():
   '''登录类封装'''

   def __init__(self, driver):
       '''初始化driver参数'''
       self.driver = driver
   
   def input_user(self, username):
       '''输入用户名'''
       self.driver.find_element_by_id("input1").clear()
       self.driver.find_element_by_id("input1").send_keys(username)
   
   def input_psw(self,psw):
       '''输入密码'''
       self.driver.find_element_by_id("input2").clear()
       self.driver.find_element_by_id("input2").send_keys(psw)
       
   def click_button(self):
       '''点击登录按钮'''
       self.driver.find_element_by_id("signin").click()
       
   def login(self, username, psw):
       '''登录公共方法'''
       self.input_user(username)
       self.input_psw(psw)
       self.click_button()

2.调用登录公共方法

# coding:utf-8
from selenium import webdriver
import unittest
from login_pub import Login_Blog
login_url = "https://passport.cnblogs.com/user/signin"

class TetsLogin(unittest.TestCase):
   def setUp(self):
       self.driver = webdriver.Firefox()
       self.driver.get(login_url)
   def tearDown(self):
       self.driver.quit()
   def test_login(self):
       # 调用登录类里面的login方法
       Login_Blog(self.driver).login("xxx", "111")
       self.driver.find_element()  # 后面接着的操作省略了


if __name__ == "__main__":
   unittest.main()

3.11 练习题2:捕获异常

前言
在定位元素的时候,经常会遇到各种异常,为什么会发生这些异常,遇到异常又该如何处理呢?
本篇通过学习selenium的exceptions模块,了解异常发生的原因。
一、发生异常
1.打开博客首页,定位“新随笔”元素,此元素id="blog_nav_newpost"
2.为了故意让它定位失败,我在元素属性后面加上xx
3.运行失败后如下图所示,程序在查找元素的这一行发生了中断,不会继续执行click事件了

二、捕获异常
1.为了让程序继续执行,我们可以用try...except...捕获异常。捕获异常后可以打印出异常原因,这样以便于分析异常原因。

2.从如下异常内容可以看出,发生异常原因是:NoSuchElementException
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: {"method":"id","selector":"blog_nav_newpostxx"}

3.从selenium.common.exceptions 导入 NoSuchElementException类。

三、参考代码:

# coding:utf-8
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

driver = webdriver.Firefox()
driver.get("http://www.cnblogs.com/yoyoketang/")

# 定位首页"新随笔"
try:
    element = driver.find_element("id", "blog_nav_newpostxx")
except NoSuchElementException as msg:
    print u"查找元素异常%s"%msg
# 点击该元素
else:
    element.click()

 

四、selenium常见异常
1.NoSuchElementException:没有找到元素
2.NoSuchFrameException:没有找到iframe
3.NoSuchWindowException:没找到窗口句柄handle
4.NoSuchAttributeException:属性错误
5.NoAlertPresentException:没找到alert弹出框
6.ElmentNotVisibleException:元素不可见
7.ElementNotSelectableException:元素没有被选中
8.TimeoutException:查找元素超时

备注:其它异常与源码在Lib目录下:selenium/common/exceptions有兴趣的可以看看。

3.12 练习题3:异常后截图

前言
在执行用例过程中由于是无人值守的,用例运行报错的时候,我们希望能对当前屏幕截图,留下证据。
在写用例的时候,最后一步是断言,可以把截图的动作放在断言这里,那么如何在断言失败后截图呢?
 
一、截图方法
1.get_screenshot_as_file(self, filename)
--这个方法是获取当前window的截图,出现IOError时候返回False,截图成功返回True。
filename参数是保存文件的路径。

   Usage:
       driver.get_screenshot_as_file('/Screenshots/foo.png')
 
2.get_screenshot_as_base64(self)
--这个方法也是获取屏幕截图,保存的是base64的编码格式,在HTML界面输出截图的时候,会用到。
比如,想把截图放到html测试报告里。
   Usage:
       driver.get_screenshot_as_base64()
 
3.get_screenshot_as_png(self)
   --这个是获取屏幕截图,保存的是二进制数据,很少用到。

    Usage:
       driver.get_screenshot_as_png()

二、异常后截图
1.为了能抛异常,把定位登录按钮的id换了个错的id。
2.给图片命名时候加个时间戳,避免同一个文件名称被覆盖掉。
3.文件路径,这里直接写的文件名称,就是跟当前的脚本同一个路径。如果图片输出到其它文件路径,需要些文件的绝对路径了。
4.截图的结果,如果没截到图返回False,截图成功会返回True。

 

 

三、selenium实例
1.在unittest框架里写用例的时候,我们希望在断言失败的时候,对当前屏幕截图。
2.如果加try...except捕获异常后结果,此时所有的测试用例都是通过的了,会影响测试结果。解决办法其实很简单,再把异常抛出来就行了。

3.参考代码:

# coding:utf-8
from selenium import webdriver
import time,unittest
from selenium.webdriver.support import expected_conditions as EC

class Login(unittest.TestCase):
    def setUp(self):
        url_login = "https://passport.cnblogs.com/user/signin"
        self.driver = webdriver.Firefox()
        self.driver.get(url_login)
    def test_01(self):
        '''前面输入账号密码,让正确运行到assert这一步,断言故意设置为Fals
e不成功'''
        try:
            self.driver.find_element_by_id("input1").send_keys(u"上海-悠悠")
            self.driver.find_element_by_id("input2").send_keys("xxx")
            # 登录id是错的,定位会抛异常
            self.driver.find_element_by_id("signin").click()
            # 判断登录成功页面是否有账号:"上海-悠悠"
            time.sleep(3)
            locator = ("id", "lnk_current_user")
            result = EC.text_to_be_present_in_element(locator,u"上海-悠悠")(self.driver)
            self.assertFalse(result)
        except Exception as msg:
            print(u"异常原因%s"%msg)
            # 图片名称可以加个时间戳
            nowTime = time.strftime("%Y%m%d.%H.%M.%S")
            self.driver.get_screenshot_as_file('%s.jpg' % nowTime)
            raise
    def tearDown(self):
        self.driver.quit()

if __name__ == "__main__":
    unittest.main()

4.运行结果:
异常原因True is not false

Failure
Traceback (most recent call last):
  File "D:\test\yoyot\ketang\test01.py", line 22, in test_01
    self.assertFalse(result)
AssertionError: True is not false

3.13 练习题4:邮件发送(smtp)

前言
本篇总结了QQ邮箱和163邮箱发送邮件,邮件包含html中文和附件,可以发给多个收件人,专治各种不行,总之看完这篇麻麻再也不用担心我的邮件收不到了。
以下代码兼容python2和python3,运行无异常,放心大胆食用。
 
一、163邮箱
1.先导入smtplib库用来发送邮件,导入MIMEText库用来做纯文本的邮件模板
2.先准备几个跟发邮件相关的参数,每个邮箱的发件服务器都不一样,以163为例,百度搜到发件服务器为:smtp.163.com

3.接下来就是写邮件的主题和正文内容,正文这里用html格式的
4.最后调用SMTP发件服务

5.参考代码:

# coding:utf-8
import smtplib
from email.mime.text import MIMEText

# ----------1.跟发件相关的参数------
smtpserver = "smtp.163.com"            # 发件服务器
port = 0                                            # 端口
sender = "yoyo@163.com"                # 账号
psw = "**************"                         # 密码
receiver = "283340479@qq.com"        # 接收人


# ----------2.编辑邮件的内容------
subject = "这个是主题163"
body = '<p>这个是发送的163邮件</p>'  # 定义邮件正文为html格式
msg = MIMEText(body, "html", "utf-8")
msg['from'] = sender
msg['to'] = "283340479@qq.com"
msg['subject'] = subject

# ----------3.发送邮件------
smtp = smtplib.SMTP()
smtp.connect(smtpserver)                                  # 连服务器
smtp.login(sender, psw)                                     # 登录
smtp.sendmail(sender, receiver, msg.as_string())  # 发送
smtp.quit()                                                         # 关闭

二、QQ邮件

1.QQ邮箱是需要SSL认证的,这种邮箱跟上面的就有点不一样了。

2.找到QQ邮箱授权码,打开QQ邮箱-设置-账号-POP3开启服务-开启
(如果已经开启了,不知道授权码,就点温馨提示里面的‘生成授权码’)

 

 3.发验证短信获取授权码,照着提示发个短信,如何点我已发送,就会收到授权码了。

4.收到授权码后复制,保存下来,这个就可以当QQ邮箱的密码了。

 

 

 5.QQ邮箱发送邮件代码,跟163有点不一样,如下图红色框框:

6.参考代码:

# coding:utf-8
import smtplib
from email.mime.text import MIMEText

# ----------1.跟发件相关的参数------
# smtpserver = "smtp.163.com"         # 发件服务器
smtpserver = "smtp.qq.com"
port = 465                                        # 端口
sender = "283340479@qq.com"         # 账号
psw = "**************"                         # 密码
receiver = "283340479@qq.com"        # 接收人


# ----------2.编辑邮件的内容------
subject = "这个是主题QQ"
body = '<p>这个是发送的QQ邮件</p>'     # 定义邮件正文为html格式
msg = MIMEText(body, "html", "utf-8")
msg['from'] = sender
msg['to'] = "283340479@qq.com"
msg['subject'] = subject

# ----------3.发送邮件------
# smtp = smtplib.SMTP()
# smtp.connect(smtpserver)                                 # 连服务器
smtp = smtplib.SMTP_SSL(smtpserver, port)
smtp.login(sender, psw)                                      # 登录
smtp.sendmail(sender, receiver, msg.as_string())  # 发送
smtp.quit()                                                        # 关闭

三、兼容163和QQ邮箱
1.如果想兼容上面两种方式发送邮件,只需把第三块内容稍微改下,如下所示

四、发送带附件
1.上面的MIMEText只能发送正文,无法带附件,发送带附件的需要导入另外一个模块MIMEMultipart
2.先读取要发送文件的内容,file_path是路径的参数名
3.下图红色框框file_name参数是发送的附件重新命名

4.参考代码:

# coding:utf-8
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# ----------1.跟发件相关的参数------
smtpserver = "smtp.163.com"           # 发件服务器
port = 0                                           # 端口
sender = "yoyo@163.com"               # 账号
psw = "***********"                             # 密码
receiver = "283340479@qq.com"        # 接收人


# ----------2.编辑邮件的内容------
# 读文件
file_path = "result.html"
with open(file_path, "rb") as fp:
    mail_body = fp.read()
msg = MIMEMultipart()
msg["from"] = sender                             # 发件人
msg["to"] = receiver                               # 收件人
msg["subject"] = "这个我的主题"             # 主题
# 正文
body = MIMEText(mail_body, "html", "utf-8")
msg.attach(body)
# 附件
att = MIMEText(mail_body, "base64", "utf-8")
att["Content-Type"] = "application/octet-stream"
att["Content-Disposition"] = 'attachment; filename="test_report.html"'
msg.attach(att)

# ----------3.发送邮件------
try:
    smtp = smtplib.SMTP()
    smtp.connect(smtpserver)                      # 连服务器
    smtp.login(sender, psw)
except:
    smtp = smtplib.SMTP_SSL(smtpserver, port)
    smtp.login(sender, psw)                       # 登录
smtp.sendmail(sender, receiver, msg.as_string())  # 发送
smtp.quit()    

5.最后结果,有图有真相

五、发给多个收件人
1.上面都是发给一个收件人,那么如何一次发给多个收件人呢?只需改两个小地方
2.把receiver参数改成list对象,单个多个都是可以收到的
3.msg["to"]这个参数不能用list了,得先把receiver参数转化成字符串,如下图所示

4.参考代码:

# coding:utf-8
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# ----------1.跟发件相关的参数------
smtpserver = "smtp.163.com"           # 发件服务器
port = 0                              # 端口
sender = "yoyo@163.com"     # 账号
psw = "*********"                  # 密码
# receiver = ["xxxx@qq.com"]      # 单个接收人也可以是list
receiver = ["xxxx@qq.com", "yoyo@qq.com"]   # 多个收件人list对象


# ----------2.编辑邮件的内容------
# 读文件
file_path = "result.html"
with open(file_path, "rb") as fp:
    mail_body = fp.read()
msg = MIMEMultipart()
msg["from"] = sender                       # 发件人
msg["to"] = ";".join(receiver)             # 多个收件人list转str
msg["subject"] = "这个我的主题999"              # 主题
# 正文
body = MIMEText(mail_body, "html", "utf-8")
msg.attach(body)
# 附件
att = MIMEText(mail_body, "base64", "utf-8")
att["Content-Type"] = "application/octet-stream"
att["Content-Disposition"] = 'attachment; filename="test_report.html"'
msg.attach(att)

# ----------3.发送邮件------
try:
    smtp = smtplib.SMTP()
    smtp.connect(smtpserver)                      # 连服务器
    smtp.login(sender, psw)
except:
    smtp = smtplib.SMTP_SSL(smtpserver, port)
    smtp.login(sender, psw)                       # 登录
smtp.sendmail(sender, receiver, msg.as_string())  # 发送
smtp.quit()                                       # 关闭

六:邮件收不到的几种原因:
1.Subject和正文内容不要用hello、hehe、test等单词
2.from(发件人)和to(收件人)不要为空,
  (要不然会被认为是垃圾邮件)
3.找不到的话,先看下垃圾信箱,是不是跑到垃圾箱了
4.如果前几次可以收到,后来收不到了,需改下subject内容
  (因为每次都是一个subject,系统也会拒收的,把subject内容设置为动态的是最好的)
5.部分邮箱是ssl加密了的,所以无法发送,如:qq邮箱
(用授权码去登录)
6.要是按照上面的步骤来报错了,说明代码抄错了,多检查几次。

(以上代码均在python2和python3上都测试通过了)

3.14 unittest之skip

前言
当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候我们希望暂时跳过这些用例。
或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,如果第一步就失败了,后面的用例也就没必要去执行了,直接跳过就行,节省用例执行时间。
一、skip装饰器
skip装饰器一共有四个:

@unittest.skip(reason)

Unconditionally skip the decorated test. reason should describe why the test is being skipped.

翻译:无条件跳过用例,reason是说明原因。

@unittest.skipIf(condition, reason)

Skip the decorated test if condition is true.

翻译:condition为true的时候跳过。

@unittest.skipUnless(condition, reason)

Skip the decorated test unless condition is true.

翻译:condition为False的时候跳过。

@unittest.expectedFailure

Mark the test as an expected failure. If the test fails when run, the test is not counted as a failure.

翻译:断言的时候跳过。


二、skip案例

运行结果:
测试1
测试4
.ssx

----------------------------------------------------------------------
Ran 4 tests in 0.003s
OK (skipped=2, expected failures=1)
三、跳过整个测试类

四、参考代码:

# coding:utf-8
import unittest
class Test(unittest.TestCase):
    @unittest.skip(u"无条件跳过此用例")
    def test_1(self):
        print "测试1"
    @unittest.skipIf(True, u"为True的时候跳过")
    def test_2(self):
        print "测试2"
    @unittest.skipUnless(False, u"为False的时候跳过")
    def test_3(self):
       print "测试3"
    @unittest.expectedFailure
    def test_4(self):
        print "测试4"
        self.assertEqual(2, 4, msg=u"判断相等")
if __name__ == "__main__":
    unittest.main()

 

4.1 显示等待WebDriverWait

前言:
在脚本中加入太多的sleep后会影响脚本的执行速度,虽然implicitly_wait()这种隐式等待在一定程度上节省了很多时间。
但是一旦页面上某些js无法加载出来(其实界面元素已经出来了),左上角那个图标一直转圈,这时候会一直等待的。
一、参数解释
1.这里主要有三个参数:
class WebDriverWait(object):driver, timeout, poll_frequency
2.driver:返回浏览器的一个实例,这个不用多说
3.timeout:超时的总时长
4.poll_frequency:循环去查询的间隙时间,默认0.5秒

以下是源码的解释文档(案例一个是元素出现,一个是元素消失)
 

   def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
        """Constructor, takes a WebDriver instance and timeout in seconds.
           :Args:
            - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
            - timeout - Number of seconds before timing out
            - poll_frequency - sleep interval between calls
              By default, it is 0.5 second.
            - ignored_exceptions - iterable structure of exception classes ignored during calls.
              By default, it contains NoSuchElementException only.
           Example:
            from selenium.webdriver.support.ui import WebDriverWait \n
            element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n
            is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n
                        until_not(lambda x: x.find_element_by_id("someId").is_displayed())
        """

二、元素出现:until()
1.until里面有个lambda函数,这个语法看python文档吧
2.以百度输入框为例:

三、元素消失:until_not()
1.判断元素是否消失,是返回Ture,否返回False
备注:此方法未调好,暂时放这

 

四、参考代码:

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
# 等待时长10秒,默认0.5秒询问一次
WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("kw")).send_keys("yoyo")
# 判断id为kw元素是否消失
is_disappeared = WebDriverWait(driver, 10, 1).\
    until_not(lambda x: x.find_element_by_id("kw").is_displayed())
print is_disappeared

五、WebDriverWait源码

1.WebDriverWait主要提供了两个方法,一个是until(),另外一个是until_not()
以下是源码的注释,有兴趣的小伙伴可以看下:

# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
import time
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
POLL_FREQUENCY = 0.5  # How long to sleep inbetween calls to the method
IGNORED_EXCEPTIONS = (NoSuchElementException,)  # exceptions ignored during calls to the method
class WebDriverWait(object):
    def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
        """Constructor, takes a WebDriver instance and timeout in seconds.
           :Args:
            - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
            - timeout - Number of seconds before timing out
            - poll_frequency - sleep interval between calls
              By default, it is 0.5 second.
            - ignored_exceptions - iterable structure of exception classes ignored during calls.
              By default, it contains NoSuchElementException only.
           Example:
            from selenium.webdriver.support.ui import WebDriverWait \n
            element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n
            is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n
                        until_not(lambda x: x.find_element_by_id("someId").is_displayed())
        """
        self._driver = driver
        self._timeout = timeout
        self._poll = poll_frequency
        # avoid the divide by zero
        if self._poll == 0:
            self._poll = POLL_FREQUENCY
        exceptions = list(IGNORED_EXCEPTIONS)
        if ignored_exceptions is not None:
            try:
                exceptions.extend(iter(ignored_exceptions))
            except TypeError:  # ignored_exceptions is not iterable
                exceptions.append(ignored_exceptions)
        self._ignored_exceptions = tuple(exceptions)
    def __repr__(self):
        return '<{0.__module__}.{0.__name__} (session="{1}")>'.format(
            type(self), self._driver.session_id)
    def until(self, method, message=''):
        """Calls the method provided with the driver as an argument until the \
        return value is not False."""
        screen = None
        stacktrace = None
        end_time = time.time() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if value:
                    return value
            except self._ignored_exceptions as exc:
                screen = getattr(exc, 'screen', None)
                stacktrace = getattr(exc, 'stacktrace', None)
            time.sleep(self._poll)
            if time.time() > end_time:
                break
        raise TimeoutException(message, screen, stacktrace)
    def until_not(self, method, message=''):
        """Calls the method provided with the driver as an argument until the \
        return value is False."""
        end_time = time.time() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if not value:
                    return value
            except self._ignored_exceptions:
                return True
            time.sleep(self._poll)
            if time.time() > end_time:
                break
        raise TimeoutException(message)

4.2 定位方法参数化find_element()

前言
元素基本的定位有八种方法,这个能看到这一篇的小伙伴都知道了,那么有没有一种方法,可以把八种定位合为一种呢?也就是把定位的方式参数化,如id,name.css等设置为一个参数,这样只需维护定位方式的参数就行了。
一、find_element()
1.selenium元素定位里面其实是有这个方法的,只是大部分时候都是结合By方法使用,如下图:

二、查看find_element方法源码
1.find_element跟find_element_by_xxx到底有什么区别呢?好奇害死猫啊,找到这个路径:Lib\site-packages\selenium\webdriver\remote\utils.py
2.打开文件夹后发现,其实定find_element_by_xxx的方法都是返回的find_element方法,也就是说那八个定位方法其实就是八个小分支。

三、By定位方法
1.找到这个路径:Lib\site-packages\selenium\webdriver\common\by.py
2.打开by这个模块,其实里面很简单啊,就是几个字符串参数。
3.那么问题就简单了,其实压根可以不用绕这么大弯路去导入这个模块啊,说实话,我一点都不喜欢去导入这个By,总觉得太繁琐。

"""
The By implementation.
"""
class By(object):
    """
    Set of supported locator strategies.
    """

    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"

四、定位参数化
1.小编一直追求简单粗暴的方式,接下来就用最简单的方法去定位
2.总结下几种定位方法(字符串中间是空格需注意)

by_id= "id"
by_xpath = "xpath"
by_link_text = "link text"
by_partial_text = "partial link text"
by_name = "name"
by_tag_name = "tag name"
by_class_name = "class name"
by_css_selector = "css selector"

 

五、参考代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Firefox()
driver.get("https://www.baidu.com/")
driver.find_element("id", "kw").send_keys("yoyoketang")
driver.find_element('css selector', "#su").click()
# 其它定位参考 交流QQ群:232607095
# t1 = driver.find_element("link text", "糯米").text
# print t1
# t2 = driver.find_element("name", "tj_trnews").text
# print t2
# t3 = driver.find_element("class name", "bri").text
# print t3

4.3 参数化登录方法

前言

登录这个场景在写用例的时候经常会有,我们可以把登录封装成一个方法,然后把账号和密码参数化,这样以后用的登录的时候,只需调用这个方法就行了
一、登录方法
1.把输入账号、输入密码、点击登录按钮三个步骤写成一个方法
2.然后把输入的账号和密码参数化

二、用例
1.下面的用例可以调用前面写的登录方法,这样不用每次都去走登录流程
2.判断是否登录成功,我这里是取的登录成功后的账户名

三、判断方法封装
1.如果用上面的方法去判断的话,有个缺陷,当登录不成功的时候,页面是不会跳转的,所以查找元素会报异常:
NoSuchElementException: Message: Unable to locate element: {"method":"id","selector":"lnk_current_user"}

2.这个时候就简单封装下判断方法:获取到账户名返回Ture;没有获取到返回False
(这里封装思路仅供参考,勿照搬,后面参考二次封装的方法)

 

四、优化后案例
1.优化后的登录案例如下,这样看起来更清楚了

五、参考代码

# coding:utf-8
from selenium import webdriver
import unittest
import time
class Bolg(unittest.TestCase):

    u'''登录博客'''
    def setUp(self):
        self.driver = webdriver.Firefox()
        url = "https://passport.cnblogs.com/user/signin"
        self.driver.get(url)
        self.driver.implicitly_wait(30)

    def login(self, username, psw):
        u'''这里写了一个登录的方法,账号和密码参数化'''
        self.driver.find_element_by_id("input1").send_keys(username)
        self.driver.find_element_by_id("input2").send_keys(psw)
        self.driver.find_element_by_id("signin").click()
        time.sleep(3)

    def is_login_sucess(self):
        u'''判断是否获取到登录账户名称'''
        try:
            text = self.driver.find_element_by_id("lnk_current_user").text
            print text
            return True
        except:
            return False

    def test_01(self):
        u'''登录案例参考:账号,密码自己设置'''
        self.login(u"上海-悠悠", u"xxxx"# 调用登录方法
        # 判断结果
        result = self.is_login_sucess()
        self.assertTrue(result)
    def test_02(self):
        u'''登录案例参考:账号,密码自己设置'''
        self.login(u"上海-悠悠", u"xxxx"# 调用登录方法
        # 判断结果   # 交流QQ群:232607095
        result = self.is_login_sucess()
        self.assertTrue(result)

    # def test_01(self):
    #     u'''登录案例参考:账号,密码自己设置'''
    #     self.login(u"上海-悠悠", u"xxxx")  # 调用登录方法
    #     # 获取登录后的账号名称
    #     text = self.driver.find_element_by_id("lnk_current_user").text
    #     print text
    #     # 断言实际结果与期望结果一致
    #     self.assertEqual(text, u"上海-悠悠")
    #
    # def test_02(self):
    #     u'''登录案例参考:账号,密码自己设置'''
    #     self.login(u"上海-悠悠", u"oooo")  # 调用登录方法
    #     # 获取登录后的账号名称
    #     text = self.driver.find_element_by_id("lnk_current_user").text
    #     print text            # 断言实际结果与期望结果一致
    #     self.assertEqual(text, u"上海-悠悠")


    def tearDown(self):
        self.driver.quit()

if __name__ == "__main__":
    unittest.main()

4.4 封装读取excel方法

前言
当登录的账号有多个的时候,我们一般用excel存放测试数据,本节课介绍,python读取excel方法,并保存为字典格式。
 
一、环境准备
1.先安装xlrd模块,打开cmd,输入pip install xlrd在线安装
>>pip install xlrd

 

二、基本操作
1.exlce基本操作方法如下
# 打开exlce表格,参数是文件路径

data = xlrd.open_workbook('test.xlsx')
# table = data.sheets()[0]           #  通过索引顺序获取
# table = data.sheet_by_index(0)     #  通过索引顺序获取
table = data.sheet_by_name(u'Sheet1')  # 通过名称获取
nrows = table.nrows  # 获取总行数
ncols = table.ncols  # 获取总列数
# 获取一行或一列的值,参数是第几行
print table.row_values(0)  # 获取第一行值
print table.col_values(0)  # 获取第一列值

三、excel存放数据
1.在excel中存放数据,第一行为标题,也就是对应字典里面的key值,如:username,password
2.如果excel数据中有纯数字的一定要右键》设置单元格格式》文本格式,要不然读取的数据是浮点数
(先设置单元格格式后编辑,编辑成功左上角有个小三角图标)

 

四、封装读取方法
1.最终读取的数据是多个字典的list类型数据,第一行数据就是字典里的key值,从第二行开始一一对应value值
2.封装好后的代码如下:

# coding:utf-8
import xlrd

class ExcelUtil():
    def __init__(self, excelPath, sheetName):
        self.data = xlrd.open_workbook(excelPath)
        self.table = self.data.sheet_by_name(sheetName)
        # 获取第一行作为key值
        self.keys = self.table.row_values(0)
        # 获取总行数
        self.rowNum = self.table.nrows
        # 获取总列数
        self.colNum = self.table.ncols
    def dict_data(self):
        if self.rowNum <= 1:
            print("总行数小于1")
        else:
            r = []
            j=1
            for i in range(self.rowNum-1):
                s = {}
                # 从第二行取对应values值
                values = self.table.row_values(j)
                for x in range(self.colNum):
                    s[self.keys[x]] = values[x]
                r.append(s)
                j+=1
            return r

if __name__ == "__main__":
   # 注意:此代码if以上的勿乱改,调用此方法只需修改两个参数,一个是excelPath存放xlsx的路径,另外一个是sheetName的值
    filePath = "D:\\test\\web-project\\5ke\\testdata.xlsx"
    sheetName = "Sheet1"
    data = ExcelUtil(filePath, sheetName)
    print data.dict_data()

运行结果:
[{u'username': u'python\u7fa4', u'password': u'226296743'},{u'username': u'selenium\u7fa4', u'password': u'232607095'},{u'username': u'appium\u7fa4', u'password': u'512200893'}]

4.5 数据驱动ddt

前言
在设计用例的时候,有些用例只是参数数据的输入不一样,比如登录这个功能,操作过程但是一样的。如果用例重复去写操作过程会增加代码量,对应这种多组数据的测试用例,可以用数据驱动设计模式,一组数据对应一个测试用例,用例自动加载生成。
一、环境准备
1.安装ddt模块,打开cmd输入pip install ddt在线安装
>>pip install ddt

二、数据驱动原理
1.测试数据为多个字典的list类型
2.测试类前加修饰@ddt.ddt
3.case前加修饰@ddt.data()

4.运行后用例会自动加载成三个单独的用例

5.测试结果:
Testing started at 21:51 ...
start!

{'username': 'selenium\xe7\xbe\xa4', 'psw': '232607095'}
end!
start!
{'username': 'python\xe7\xbe\xa4', 'psw': '226296743'}
end!
start!
{'username': 'appium\xe7\xbe\xa4', 'psw': '512200893'}
end!

三、selenium案例
1.从上一篇4.4封装的excel方法里面读取数据,作为测试数据。
2.在之前写的4.3参数化登录那篇基础上做点修改,测试参数读取excel里的数据。

3.代码参考如下:

# 测试数据
testData = data.dict_data()
print testData
@ddt.ddt
class Bolg(unittest.TestCase):
    u'''登录博客'''
    def setUp(self):
        self.driver = webdriver.Firefox()
        url = "https://passport.cnblogs.com/user/signin"
        self.driver.get(url)
        self.driver.implicitly_wait(30)
    def login(self, username, psw):
        u'''这里写了一个登录的方法,账号和密码参数化'''
        self.driver.find_element_by_id("input1").send_keys(username)
        self.driver.find_element_by_id("input2").send_keys(psw)
        self.driver.find_element_by_id("signin").click()
        time.sleep(3)
    def is_login_sucess(self):
        u'''判断是否获取到登录账户名称'''
        try:
            text = self.driver.find_element_by_id("lnk_current_user").text
            print text
            return True
        except:
            return False

    @ddt.data(*testData)
    def test_login(self, data):
        u'''登录案例参考'''
        print ("当前测试数据%s"%data)
        # 调用登录方法
        self.login(data["username"], data["password"])
        # 判断结果
        result = self.is_login_sucess()
        self.assertTrue(result)

    def tearDown(self):
        self.driver.quit()

if __name__ == "__main__":
    unittest.main()

4.6 判断元素16种方法expected_conditions

前言
经常有小伙伴问,如何判断一个元素是否存在,如何判断alert弹窗出来了,如何判断动态的元素等等一系列的判断,在selenium的expected_conditions模块收集了一系列的场景判断方法,这些方法是逢面试必考的!!!


expected_conditions一般也简称EC,本篇先介绍下有哪些功能,后续更新中会单个去介绍。


一、功能介绍和翻译

title_is判断当前页面的title是否完全等于(==)预期字符串,返回布尔值。
title_contains 判断当前页面的title是否包含预期字符串,返回布尔值。
presence_of_element_located判断某个元素是否被加到了dom树里,并不代表该元素一定可见。
visibility_of_element_located判断某个元素是否可见. 可见代表元素非隐藏,并且元素的宽和高都不等于0。
visibility_of 跟上面的方法做一样的事情,只是上面的方法要传入locator,这个方法直接传定位到的element就好了。
presence_of_all_elements_located 判断是否至少有1个元素存在于dom树中。举个例子,如果页面上有n个元素的class都是'column-md-3',那么只要有1个元素存在,这个方法就返回True。
text_to_be_present_in_element 判断某个元素中的text是否 包含 了预期的字符串。
text_to_be_present_in_element_value 判断某个元素中的value属性是否 包含 了预期的字符串
frame_to_be_available_and_switch_to_it 判断该frame是否可以switch进去,如果可以的话,返回True并且switch进去,否则返回False。
invisibility_of_element_located 判断某个元素中是否不存在于dom树或不可见。
element_to_be_clickable 判断某个元素中是否可见并且是enable的,这样的话才叫clickable。
staleness_of 等某个元素从dom树中移除,注意,这个方法也是返回True或False。
element_to_be_selected 判断某个元素是否被选中了,一般用在下拉列表。
element_selection_state_to_be 判断某个元素的选中状态是否符合预期。
element_located_selection_state_to_be 跟上面的方法作用一样,只是上面的方法传入定位到的element,而这个方法传入locator。
alert_is_present 判断页面上是否存在alert。

 

二、查看源码和注释
1.打开python里这个目录l可以找到:Lib\site-packages\selenium\webdriver\support\expected_conditions.py

 

from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoSuchFrameException
from selenium.common.exceptions import StaleElementReferenceException
from selenium.common.exceptions import WebDriverException
from selenium.common.exceptions import NoAlertPresentException
"""
 * Canned "Expected Conditions" which are generally useful within webdriver
 * tests.
"""
class title_is(object):
    """An expectation for checking the title of a page.
    title is the expected title, which must be an exact match
    returns True if the title matches, false otherwise."""
    def __init__(self, title):
        self.title = title
    def __call__(self, driver):
        return self.title == driver.title
class title_contains(object):
    """ An expectation for checking that the title contains a case-sensitive
    substring. title is the fragment of title expected
    returns True when the title matches, False otherwise
    """
    def __init__(self, title):
        self.title = title
    def __call__(self, driver):
        return self.title in driver.title
class presence_of_element_located(object):
    """ An expectation for checking that an element is present on the DOM
    of a page. This does not necessarily mean that the element is visible.
    locator - used to find the element
    returns the WebElement once it is located
    """
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        return _find_element(driver, self.locator)
class visibility_of_element_located(object):
    """ An expectation for checking that an element is present on the D
OM of a
    page and visible. Visibility means that the element is not only displayed
    but also has a height and width that is greater than 0.
    locator - used to find the element
    returns the WebElement once it is located and visible
    """
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        try:
            return _element_if_visible(_find_element(driver, self.locator))
        except StaleElementReferenceException:
            return False
class visibility_of(object):
    """ An expectation for checking that an element, known to be present on the
    DOM of a page, is visible. Visibility means that the element is not only
    displayed but also has a height and width that is greater than 0.
    element is the WebElement
    returns the (same) WebElement once it is visible
    """
    def __init__(self, element):
        self.element = element
    def __call__(self, ignored):
        return _element_if_visible(self.element)
def _element_if_visible(element, visibility=True):
    return element if element.is_displayed() == visibility else False
class presence_of_all_elements_located(object):
    """ An expectation for checking that there is at least one element present
    on a web page.
    locator is used to find the element
    returns the list of WebElements once they are located
    """
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        return _find_elements(driver, self.locator)
class visibility_of_any_elements_located(object):
    """ An expectation for checking that there is at least one element visible
    on a web page.
    locator is used to find the element
    returns the list of WebElements once they are located
    """
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        return [element for element in _find_elements(driver, self.locator) if _element_if_visible(element)]
class text_to_be_present_in_element(object):
    """ An expectation for checking if the given text is present in the
    specified element.
    locator, text
    """
    def __init__(self, locator, text_):
        self.locator = locator
        self.text = text_
    def __call__(self, driver):
        try:
            element_text = _find_element(driver, self.locator).text
            return self.text in element_text
        except StaleElementReferenceException:
            return False
class text_to_be_present_in_element_value(object):
    """
    An expectation for checking if the given text is present in the element's
    locator, text
    """
    def __init__(self, locator, text_):
        self.locator = locator
        self.text = text_
    def __call__(self, driver):
        try:
            element_text = _find_element(driver,
                                         self.locator).get_attribute("value")
            if element_text:
                return self.text in element_text
            else:
                return False
        except StaleElementReferenceException:
                return False
class frame_to_be_available_and_switch_to_it(object):
    """ An expectation for checking whether the given frame is available to
    switch to.  If the frame is available it switches the given driver to the
    specified frame.
    """
    def __init__(self, locator):
        self.frame_locator = locator
    def __call__(self, driver):
        try:
            if isinstance(self.frame_locator, tuple):
                driver.switch_to.frame(_find_element(driver,
                                                     self.frame_locator))
            else:
                driver.switch_to.frame(self.frame_locator)
            return True
        except NoSuchFrameException:
            return False
class invisibility_of_element_located(object):
    """ An Expectation for checking that an element is either invisible or not
    present on the DOM.
    locator used to find the element
    """
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        try:
            return _element_if_visible(_find_element(driver, self.locator), False)
        except (NoSuchElementException, StaleElementReferenceException):
            # In the case of NoSuchElement, returns true because the element is
            # not present in DOM. The try block checks if the element is present
            # but is invisible.
            # In the case of StaleElementReference, returns true because stale
            # element reference implies that element is no longer visible.
            return True
class element_to_be_clickable(object):
    """ An Expectation for checking an element is visible and enabled such that
    you can click it."""
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        element = visibility_of_element_located(self.locator)(driver)
        if element and element.is_enabled():
            return element
        else:
            return False
class staleness_of(object):
    """ Wait until an element is no longer attached to the DOM.
    element is the element to wait for.
    returns False if the element is still attached to the DOM, true otherwise.
    """
    def __init__(self, element):
        self.element = element
    def __call__(self, ignored):
        try:
            # Calling any method forces a staleness check
            self.element.is_enabled()
            return False
        except StaleElementReferenceException:
            return True
class element_to_be_selected(object):
    """ An expectation for checking the selection is selected.
    element is WebElement object
    """
    def __init__(self, element):
        self.element = element
    def __call__(self, ignored):
        return self.element.is_selected()
class element_located_to_be_selected(object):
    """An expectation for the element to be located is selected.
    locator is a tuple of (by, path)"""
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        return _find_element(driver, self.locator).is_selected()
class element_selection_state_to_be(object):
    """ An expectation for checking if the given element is selected.
    element is WebElement object
    is_selected is a Boolean."
    """
    def __init__(self, element, is_selected):
        self.element = element
        self.is_selected = is_selected
    def __call__(self, ignored):
        return self.element.is_selected() == self.is_selected
class element_located_selection_state_to_be(object):
    """ An expectation to locate an element and check if the selection state
    specified is in that state.
    locator is a tuple of (by, path)
    is_selected is a boolean
    """
    def __init__(self, locator, is_selected):
        self.locator = locator
        self.is_selected = is_selected
    def __call__(self, driver):
        try:
            element = _find_element(driver, self.locator)
            return element.is_selected() == self.is_selected
        except StaleElementReferenceException:
            return False
class alert_is_present(object):
    """ Expect an alert to be present."""
    def __init__(self):
        pass
    def __call__(self, driver):
        try:
            alert = driver.switch_to.alert
            alert.text
            return alert
        except NoAlertPresentException:
            return False
def _find_element(driver, by):
    """Looks up an element. Logs and re-raises ``WebDriverException``
    if thrown."""
    try:
        return driver.find_element(*by)
    except NoSuchElementException as e:
        raise e
    except WebDriverException as e:
        raise e
def _find_elements(driver, by):
    try:
        return driver.find_elements(*by)
    except WebDriverException as e:
        raise e

本篇的判断方法和场景很多,先贴出来,后面慢慢更新,详细讲解每个的功能的场景和用法。
这些方法是写好自动化脚本,提升性能的必经之路,想做好自动化,就得熟练掌握。

4.7 判断title方法title_is

前言
获取页面title的方法可以直接用driver.title获取到,然后也可以把获取到的结果用做断言。
本篇介绍另外一种方法去判断页面title是否与期望结果一种,用到上一篇提到的expected_conditions模块里的title_is和title_contains两种方法。

一、源码分析
1.首先看下源码,如下:

class title_is(object):
    """An expectation for checking the title of a page.
    title is the expected title, which must be an exact match

    returns True if the title matches, false otherwise."""
    '''翻译:检查页面的title与期望值是都完全一致,如果完全一致,返回Ture,否则返回Flase'''
    def __init__(self, title):
        self.title = title
    def __call__(self, driver):
        return self.title == driver.title

2.注释翻译:检查页面的title与期望值是都完全一致,如果完全一致,返回True,否则返回Flase。
3.title_is()这个是一个class类型,里面有两个方法。
4.__init__是初始化内容,参数是title,必填项。
5.__call__是把实例变成一个对象,参数是driver,返回的是self.title == driver.title,布尔值。

二、判断title:title_is()
1.首先导入expected_conditions模块。
2.由于这个模块名称比较长,所以为了后续的调用方便,重新命名为EC了(有点像数据库里面多表查询时候重命名)。
3.打开博客首页后判断title,返回结果是True或False。

三、判断title包含:title_contains
1.这个类跟上面那个类差不多,只是这个是部分匹配(类似于xpath里面的contains语法)。
2.判断title包含'上海-悠悠'字符串。

四、参考代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
driver.get("http://www.cnblogs.com/yoyoketang")
# 判断title完全等于
title = EC.title_is(u'上海-悠悠 - 博客园')
print title(driver)
# 判断title包含
title1 = EC.title_contains(u'上海-悠悠')
print title1(driver)
# 另外一种写法,交流QQ群:232607095
r1 = EC.title_is(u'上海-悠悠 - 博客园')(driver)
r2 = EC.title_contains(u'上海-悠悠')(driver)
print r1
print r2

4.8 判断文本text_to_be_present_in_element

前言
在做结果判断的时候,经常想判断某个元素中是否存在指定的文本,如登录后判断页面中是账号是否是该用户的用户名。
在前面的登录案例中,写了一个简单的方法,但不是公用的,在EC模块有个方法是可以专门用来判断元素中存在指定文本的:text_to_be_present_in_element。
另外一个差不多复方法判断元素的value值:text_to_be_present_in_element_value。

一、源码分析

class text_to_be_present_in_element(object):
    """ An expectation for checking if the given text is present in the
    specified element.
    locator, text
    """
    '''翻译:判断元素中是否存在指定的文本,参数:locator, text'''
    def __init__(self, locator, text_):
        self.locator = locator
        self.text = text_
    def __call__(self, driver):
        try:
            element_text = _find_element(driver, self.locator).text
            return self.text in element_text
        except StaleElementReferenceException:
            return False

1.翻译:判断元素中是否存在指定的文本,两个参数:locator, text
2.__call__里返回的是布尔值:Ture和False

二、判断文本
1.判断百度首页上,“糯米”按钮这个元素中存在文本:糯米

 

 

2.locator参数是定位的方法
3.text参数是期望的值

 

三、失败案例
1.如果判断失败,就返回False

 

四、判断value的方法

class text_to_be_present_in_element_value(object):
    """
    An expectation for checking if the given text is present in the element's
    locator, text

    """
    def __init__(self, locator, text_):
        self.locator = locator
        self.text = text_
    def __call__(self, driver):
        try:
            element_text = _find_element(driver,
                                         self.locator).get_attribute("value")
            if element_text:
                return self.text in element_text
            else:
                return False

        except StaleElementReferenceException:
                return False

1.这个方法跟上面的差不多,只是这个是判断的value的值
2.这里举个简单案例,判断百度搜索按钮的value值
 
五、参考代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
url = "https://www.baidu.com"
driver.get(url)
locator = ("name", "tj_trnuomi")
text = u"糯米"
result = EC.text_to_be_present_in_element(locator, text)(driver)
print result
# 交流QQ群:232607095
# 下面是失败的案例
text1 = u"糯米网"
result1 = EC.text_to_be_present_in_element(locator, text1)(driver)
print result1
locator2 = ("id", "su")
text2 = u"百度一下"
result2 = EC.text_to_be_present_in_element_value(locator2, text2)(drive
r)
print result2

4.9 判断弹出框alert_is_present

系统弹窗这个是很常见的场景,有时候它不弹出来去操作的话,会抛异常。那么又不知道它啥时候会出来,那么就需要去判断弹窗是否弹出了。
本篇接着讲expected_conditions这个模块

一、判断alert源码分析

class alert_is_present(object):
    """ Expect an alert to be present."""
    """判断当前页面的alert弹窗"""
    def __init__(self):
        pass
    def __call__(self, driver):
        try:
            alert = driver.switch_to.alert
            alert.text
            return alert
        except NoAlertPresentException:
            return False

1.这个类比较简单,初始化里面无内容
2.__call__里面就是判断如果正常获取到弹出窗的text内容就返回alert这个对象(注意这里不是返回Ture),没有获取到就返回False

二、实例操作

1.前面的操作步骤优化了下,为了提高脚本的稳定性,确保元素出现后操作,这里结合WebDriverWait里的方法
2.实现步骤如下,这里判断的结果返回有两种:没找到就返回False;找到就返回alert对象
3.先判断alert是否弹出,如果弹出就点确定按钮accept()

三、参考代码

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains 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
driver = webdriver.Firefox()
url = "https://www.baidu.com"
driver.get(url)
mouse = WebDriverWait(driver, 10).until(lambda x: x.find_element("link text", "设置"))
ActionChains(driver).move_to_element(mouse).perform()
WebDriverWait(driver, 10).until(lambda x: x.find_element("link text", "搜索设置")).click()
# 选择设置项
s = WebDriverWait(driver, 10).until(lambda x: x.find_element("id", "nr"))
Select(s).select_by_visible_text("每页显示50条")
# 点保存按钮
js = 'document.getElementsByClassName("prefpanelgo")[0].click();'
driver.execute_script(js)
# 判断弹窗结果 交流QQ群: 232607095
result = EC.alert_is_present()(driver)
if result:
    print result.text
    result.accept()
else:
    print "alert 未弹出!"

4.10 二次封装(click/sendkeys)

前言
我们学了显示等待后,就不需要sleep了,然后查找元素方法用参数化去定位,这样定位方法更灵活了,但是这样写起来代码会很长了,于是问题来了,总不能每次定位一个元素都要写一大堆代码吧?这时候就要学会封装啦!
 
一、显示等待
1.如果你的定位元素代码,还是这样:driver.find_element_by_id("kw").send_keys("yoyo"),那说明你还停留在小学水平,如何让代码提升逼格呢?
2.前面讲过显示等待相对于sleep来说更省时间,定位元素更靠谱,不会出现一会正常运行,一会又报错的情况,所以我们的定位需与WebDriverWait结合
3.以百度的搜索为例

二、封装定位方法
1.从上面代码看太长了,每次定位写一大串,这样不方便阅读,写代码的效率也低,于是我们可以把定位方法进行封装
2.定位方法封装后,我们每次调用自己写的方法就方便多了

三、封装成类
1.我们可以把send_keys()和click()方法也一起封装,写到一个类里
2.定位那里很多小伙伴弄不清楚lambda这个函数,其实不一定要用这个,我们可以用EC模块的presence_of_element_located()这个方法,参数直接传locator就可以了
3.以下是presence_of_element_located这个方法的源码:

class presence_of_element_located(object):

    """ An expectation for checking that an element is present on the DOM
    of a page. This does not necessarily mean that the element is visible.
    locator - used to find the element
    returns the WebElement once it is located
    """
    def __init__(self, locator):
        self.locator = locator
    def __call__(self, driver):
        return _find_element(driver, self.locator)

四、参考代码

1.把get、find_element、click、send_keys封装成类

# coding:utf-8
from selenium import webdriver
from selenium.common.exceptions import *   # 导入所有的异常类
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class Yoyo(object):
    """基于原生的selenium框架做了二次封装."""
    def __init__(self):
        """启动浏览器参数化,默认启动firefox."""
        self.driver = webdriver.Firefox()
    def get(self, url):
        '''使用get打开url'''
        self.driver.get(url)
    def find_element(self, locator, timeout=10):
        '''定位元素方法封装'''
        element = WebDriverWait(self.driver, timeout, 1).until(EC.presence_of_element_located(locator))
        return element
    def click(self, locator):
        '''点击操作'''
        element = self.find_element(locator)
        element.click()
    def send_keys(self, locator, text):
        '''发送文本,清空后输入'''
        element = self.find_element(locator)
        element.clear()
        element.send_keys(text)
if __name__ == "__main__":
    d = Yoyo()  # 启动firefox
    d.get("https://www.baidu.com")
    input_loc = ("id", "kw")
    d.send_keys(input_loc, "yoyo")   # 输入搜索内容
    button_loc = ("id", "su")
    d.click(button_loc)           # 点击搜索按钮

4.11 二次封装(完整版)

先上代码吧!后面有空再细化整理

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.select import Select
from selenium.common.exceptions import *   # 导入所有的异常类
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

'''
下面这三行代码是为了避免python2中文乱码问题,python3忽略
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
'''

def browser(browser='firefox'):
   """
   打开浏览器函数,"firefox"、"chrome"、"ie"、"phantomjs"
   """
   try:
       if browser == "firefox":
           driver = webdriver.Firefox()
           return driver
       elif browser == "chrome":
           driver = webdriver.Chrome()
           return driver
       elif browser == "ie":
           driver = webdriver.Ie()
           return driver
       elif browser == "phantomjs":
           driver = webdriver.PhantomJS()
           return driver
       else:
           print("Not found this browser,You can enter 'firefox', 'chrome', 'ie' or 'phantomjs'")
   except Exception as msg:
       print "%s" % msg

class Yoyo(object):
   """
   基于原生的selenium框架做了二次封装.
   """
   def __init__(self, driver):
       """
       启动浏览器参数化,默认启动firefox.
       """
       self.driver = driver
   def open(self, url, t='', timeout=10):
       '''
       使用get打开url后,最大化窗口,判断title符合预期
       Usage:
       driver = Yoyo()
       driver.open(url,t='')
       '''
       self.driver.get(url)
       self.driver.maximize_window()
       try:
           WebDriverWait(self.driver, timeout, 1).until(EC.title_contains(t))
       except TimeoutException:
           print("open %s title error" % url)
       except Exception as msg:
           print("Error:%s" % msg)
   def find_element(self, locator, timeout=10):
       '''
       定位元素,参数locator是元祖类型
       Usage:
       locator = ("id","xxx")
       driver.find_element(locator)
       '''
       element = WebDriverWait(self.driver, timeout, 1).until(EC.presence_of_element_located(locator))
       return element
   def find_elements(self, locator, timeout=10):
       '''定位一组元素'''
       elements = WebDriverWait(self.driver, timeout, 1).until(EC.presence_of_all_elements_located(locator))
       return elements
   def click(self, locator):
       '''
       点击操作
       Usage:
       locator = ("id","xxx")
       driver.click(locator)
       '''
       element = self.find_element(locator)
       element.click()
   def send_keys(self, locator, text):
       '''
       发送文本,清空后输入
       Usage:
       locator = ("id","xxx")
       driver.send_keys(locator, text)
       '''
       element = self.find_element(locator)
       element.clear()
       element.send_keys(text)
   def is_text_in_element(self, locator, text, timeout=10):
       '''
       判断文本在元素里,没定位到元素返回False,定位到返回判断结果布尔值
       result = driver.text_in_element(locator, text)
       '''
       try:
           result = WebDriverWait(self.driver, timeout, 1).until(EC.text_to_be_present_in_element(locator, text))
       except TimeoutException:
           print "元素没定位到:"+str(locator)
           return False
       else:
           return result
   def is_text_in_value(self, locator, value, timeout=10):
       '''
       判断元素的value值,没定位到元素返回false,定位到返回判断结果布尔值
       result = driver.text_in_element(locator, text)
       '''
       try:
           result = WebDriverWait(self.driver, timeout, 1).until(EC.text_to_be_present_in_element_value(locator, value))
       except TimeoutException:
           print "元素没定位到:"+str(locator)
           return False
       else:
           return result
   def is_title(self, title, timeout=10):
       '''判断title完全等于'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.title_is(title))
       return result
   def is_title_contains(self, title, timeout=10):
       '''判断title包含'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.title_contains(title))
       return result
   def is_selected(self, locator, timeout=10):
       '''判断元素被选中,返回布尔值,'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.element_located_to_be_selected(locator))
       return result
   def is_selected_be(self, locator, selected=True, timeout=10):
       '''判断元素的状态,selected是期望的参数true/False
       返回布尔值'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.element_located_selection_state_to_be(locator, selected))
       return result
   def is_alert_present(self, timeout=10):
       '''判断页面是否有alert,有返回alert(注意这里是返回alert,不是True)
       没有返回False'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.alert_is_present())
       return result
   def is_visibility(self, locator, timeout=10):
       '''元素可见返回本身,不可见返回Fasle'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.visibility_of_element_located(locator))
       return result
   def is_invisibility(self, locator, timeout=10):
       '''元素可见返回本身,不可见返回True,没找到元素也返回True'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.invisibility_of_element_located(locator))
       return result
   def is_clickable(self, locator, timeout=10):
       '''元素可以点击is_enabled返回本身,不可点击返回Fasle'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.element_to_be_clickable(locator))
       return result
   def is_located(self, locator, timeout=10):
       '''判断元素有没被定位到(并不意味着可见),定位到返回element,没定位到返回False'''
       result = WebDriverWait(self.driver, timeout, 1).until(EC.presence_of_element_located(locator))
       return result
   def move_to_element(self, locator):
       '''
       鼠标悬停操作
       Usage:
       locator = ("id","xxx")
       driver.move_to_element(locator)
       '''
       element = self.find_element(locator)
       ActionChains(self.driver).move_to_element(element).perform()
   def back(self):
       """
       Back to old window.
       Usage:
       driver.back()
       """
       self.driver.back()
   def forward(self):
       """
       Forward to old window.
       Usage:
       driver.forward()
       """
       self.driver.forward()
   def close(self):
       """
       Close the windows.
       Usage:
       driver.close()
       """
       self.driver.close()
   def quit(self):
       """
       Quit the driver and close all the windows.
       Usage:
       driver.quit()
       """
       self.driver.quit()
   def get_title(self):
       '''获取title'''
       return self.driver.title
   def get_text(self, locator):
       '''获取文本'''
       element = self.find_element(locator)
       return element.text
   def get_attribute(self, locator, name):
       '''获取属性'''
       element = self.find_element(locator)
       return element.get_attribute(name)
   def js_execute(self, js):
       '''执行js'''
       return self.driver.execute_script(js)
   def js_focus_element(self, locator):
       '''聚焦元素'''
       target = self.find_element(locator)
       self.driver.execute_script("arguments[0].scrollIntoView();", target)
   def js_scroll_top(self):
       '''滚动到顶部'''
       js = "window.scrollTo(0,0)"
       self.driver.execute_script(js)
   def js_scroll_end(self):
       '''滚动到底部'''
       js = "window.scrollTo(0,document.body.scrollHeight)"
       self.driver.execute_script(js)
   def select_by_index(self, locator, index):
       '''通过索引,index是索引第几个,从0开始'''
       element = self.find_element(locator)
       Select(element).select_by_index(index)
   def select_by_value(self, locator, value):
       '''通过value属性'''
       element = self.find_element(locator)
       Select(element).select_by_value(value)
   def select_by_text(self, locator, text):
       '''通过文本值定位'''
       element = self.find_element(locator)
       Select(element).select_by_value(text)

if __name__ == '__main__':
   # if下面的代码都是测试调试的代码,自测内容
  driver = browser()
   driver_n = Yoyo(driver)  # 返回类的实例:打开浏览器
   driver_n.open("http://www.cnblogs.com/yoyoketang/")  # 打开url,顺便判断打开的页面对不对
   input_loc = ("id", "kw")
   print driver_n.get_title()
   # el = driver_n.find_element(input_loc)
   # driver_n.send_keys(input_loc, "yoyo")
   # button_loc = ("id", "su")
   # driver_n.click(button_loc)
   # print driver_n.text_in_element(("name", "tj_trmap"), "地图")
   # set_loc = ("link text", "设置")
   # driver_n.move_to_element(set_loc)

4.12 PageObject设计模式

一、PageObect
PagetObect设计模式就是把web的每一个页面写成一个page类(继承前面封装的)。

定位元素方法和操作元素方法分离开,元素定位全部放一起,每一个操作元素动作写成一个方法。

二、定位方法对应参照表
    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"
三、参考代码如下
1.新建以下脚本。

二、定位方法对应参照表
    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"
三、参考代码如下
1.新建以下脚本

# coding:utf-8
from xxx.yoyo_selenium import Yoyo  # 导入4.11二次封装的类
login_url = "https://passport.cnblogs.com/user/signin"
class LoginPage(Yoyo):  
    # 定位器,定位页面元素
    username_loc = ("id", 'input1')  # 输入账号
    password_loc = ("id", 'input2')
    submit_loc = ("id", 'signin')
    remember_loc = ('id', 'remember_me')
    retrieve_loc = ('link text', '找回')
    reset_loc = ('link text', '重置')
    register_loc = ('link text', '立即注册')
    feedback_loc = ('link text', '反馈问题')
    def input_username(self, username):
        '''输入账号框'''
        self.send_keys(self.username_loc, username)
    def input_password(self, password):
        '''输入密码框'''
        self.send_keys(self.password_loc, password)
    def click_submit(self):
        '''登录按钮'''
        self.click(self.submit_loc)
    def click_remember_live(self):
        '''下次记住登录'''
        self.click(self.remember_loc)
    def click_retrieve(self):
        '''找回密码'''
        self.click(self.retrieve_loc)
    def click_reset(self):
        '''重置密码'''
        self.click(self.reset_loc)
    def click_register(self):
        '''注册新账号'''
        self.click(self.register_loc)
    def click_feedback(self):
        '''反馈问题'''
        self.click(self.feedback_loc)
    def login(self, username, password):
        '''登录方法'''
        self.input_username(username)
        self.input_password(password)
        self.click_submit()

四、用例参考
1.导入上面的page类

# coding:utf-8
import unittest
from xxx.yoyo_selenium import browser
from xxx.xxx import LoginPage, login_url
class Login_test(unittest.TestCase):
    u'''登录页面的case'''
    def setUp(self):
        self.driver = browser()
        self.login= LoginPage(self.driver)  #login参数是LoginPage的实例
        self.login.open(login_url)
    def login_case(self, username, psw, expect=True):
        '''登录用例的方法,'''
        # 第1步:输入账号
        self.login.input_username(username)
        # 第2步: 输入密码
        self.login.input_password(psw)
        # 第3步:点登录按钮
        self.login.click_submit()
        # 第4步:测试结果,判断是否登录成功
        result = self.login.is_text_in_element(("id","lnk_current_user"),"上海-悠悠")
        # 第5步:期望结果
        expect_result = expect
        self.assertEqual(result, expect_result)
    def test_login01(self):
        u'''输入正确账号密码'''
        self.login_case("xx", "xx", True)
    def test_login02(self):
        u'''输入错误账号密码'''
        self.login_case("xx", "xx", False)
    def tearDown(self):
        self.driver.quit()
if __name__ == "__main__":
    unittest.main()

(备注:不要copy后直接运行,注意思考!!!直接copy运行报错不解决)

4.13 装饰器之异常后截图

前言
对于用例失败截图,很多小伙伴都希望用例执行失败的时候能自动截图,想法是很好的,实现起来并不是那么容易,这里小编分享下最近研究装饰器,打算用装饰器来实现自动截图
 
一、函数作为形参
1.函数的参数也可以是另外一个函数,也就是说传的参数不仅可以是常见的字符串、数字等,也可以是一个函数
2.定义aaa为一个加法函数,bbb为减法函数
3.calculate这个函数传三个参数,第一个参数是一个函数,另外两个参数是函数的两个参数

二、万能装饰器
1.由于不知道我们被调用的函数到底有几个参数,这时候就可以写一个万能的装饰器,传可变参数
2.这个装饰器实现一个简单功能:运行一个函数,运行不抛异常,就打印pass;运行函数抛异常就打印fail

三、实现百度搜索功能

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
# 截图功能
def get_screen():
    '''截图'''
    import time
    nowTime = time.strftime("%Y_%m_%d_%H_%M_%S")
    driver.get_screenshot_as_file('%s.jpg' % nowTime)
# 自动截图装饰器
def screen(func):
    '''截图装饰器'''
    def inner(*args, **kwargs):
        try:
            f = func(*args, **kwargs)
            return f
        except:
            get_screen()  # 失败后截图
           raise
    return inner
@screen
def search(driver):
    driver.get("https://www.baidu.com")
    driver.find_element_by_id("kw11").send_keys("python"# 此行运行失败的
    driver.find_element_by_id("su").click()
search(driver)  # 执行search

这里介绍的是简单的装饰器函数,下一篇会介绍复杂一点的带参数的装饰器。

4.14 装饰器之用例失败后截图

前言:

装饰器其实就是一个以函数作为参数并返回一个替换函数的可执行函数。
上面讲到用装饰器解决异常后自动截图,不过并没有与unittest结合,这篇把截图的装饰器改良了下,可以实现用例执行失败自动截图。

一、不带变量的装饰器
1.参考资料:http://www.artima.com/weblogs/viewpost.jsp?thread=240845,这里这篇讲的很好,可以看下原文。
2.这个是不带变量的装饰器__init__里是初始化参数,__call__里面是原函数参数。

Decorators without Arguments
If we create a decorator without arguments, the function to be decorated 
is passed to the constructor, and the __call__() method is called 
whenever the decorated function is invoked:
class decoratorWithoutArguments(object):
    def __init__(self, f):
        """
        If there are no decorator arguments, the function
        to be decorated is passed to the constructor.
        """
        print "Inside __init__()"
        self.f = f
    def __call__(self, *args):
        """
        The __call__ method is not called until the
        decorated function is called.
        """
        print "Inside __call__()"
        self.f(*args)
        print "After self.f(*args)"
@decoratorWithoutArguments
def sayHello(a1, a2, a3, a4):
    print 'sayHello arguments:', a1, a2, a3, a4

 

二、带变量的装饰器。
1.这个是带变量的参数,参数写到__init__里。

Decorators with Arguments

Now let's modify the above example to see what happens when we add arguments to the decorator:
class decoratorWithArguments(object):
    def __init__(self, arg1, arg2, arg3):
        """
        If there are decorator arguments, the function
        to be decorated is not passed to the constructor!
        """
        print "Inside __init__()"
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3
    def __call__(self, f):

   """
        If there are decorator arguments, __call__() is only called
        once, as part of the decoration process! You can only give
        it a single argument, which is the function object.
        """
        print "Inside __call__()"
        def wrapped_f(*args):
            print "Inside wrapped_f()"
            print "Decorator arguments:", self.arg1, self.arg2, self.arg3
            f(*args)
            print "After f(*args)"
        return wrapped_f

@decoratorWithArguments("hello", "world", 42)
def sayHello(a1, a2, a3, a4):
    print 'sayHello arguments:', a1, a2, a3, a4

 

三、截图装饰器
有了上面的参考文档,依着葫芦画瓢就行,最大的麻烦就是driver参数处理,这里放到__init__里就可以了。

四、参考案例

# coding:utf-8
from selenium import webdriver
class Screen(object):
    u'''这个应该截图功能的装饰器'''
    def __init__(self, driver):
        self.driver = driver
    def __call__(self, f):
        def inner(*args):
            try:
                return f(*args)
            except:
                import time
                nowTime = time.strftime("%Y_%m_%d_%H_%M_%S")
                self.driver.get_screenshot_as_file('%s.jpg' % nowTime)
                raise
        return inner
# 以下是装饰器与unittest结合的案例
import unittest
class Test(unittest.TestCase):
    driver = webdriver.Firefox()  # 全局参数driver
    def setUp(self):
        self.driver.get("https://www.baidu.com")
    @Screen(driver)
    def test01(self):
        u'''这个是失败的案例'''
        self.driver.find_element_by_id("11kw").send_keys("python")
        self.driver.find_element_by_id("su").click()
    @Screen(driver)
    def test_02(self):
        u'''这个是通过的案例'''
        self.driver.find_element_by_id("kw").send_keys("yoyo")
        self.driver.find_element_by_id("su").click()
    def tearDown(self):
        self.driver.quit()
if __name__ == "__main__":
    unittest.main()

4.15 练习题1:多个浏览器之间的切换

前言
有时候一些业务的功能涉及到多个系统,需要在web系统1打开造一些数据,然后用到某些参数是动态生成的,需要调用web系统2里面的参数。
举个简单例子:在做某些业务的时候,需要手机短信验证码,我不可能去搞个手机连着电脑吧,那样太傻,我们的目的是获取短信验证码,短信验证码都有短信平台去查询。
当然能直接操作数据库最简单了,直接通过sql去查就行。
 
一、启动两个driver
1.如果我想启动2个火狐,一个火狐打开百度,另外一个火狐打开博客园,我们只需用2个实例driver去控制就行

(注意:不要两个都叫driver,要不然后面的会覆盖前面的,导致无法继续操作前面那个浏览器窗口了)

2.运行后结果,桌面启动2个窗口,一个打开了百度,一个打开了上海-悠悠 博客园

二、关掉窗口
1.driver1是控制第一个浏览器窗口的实例参数,driver2是控制第二个窗口的实例参数,如果想关掉第一个,driver1.quit()就行了

2.quit掉第一个浏览器窗口后,前面那个浏览器窗口就无法操作了,这里可以接着操作第二个浏览器窗口。

# coding:utf-8
from selenium import webdriver
import time
# 启动第一个浏览器
driver1 = webdriver.Firefox()
driver1.get("https://www.baidu.com")
print(driver1.title)
# 启动第二个浏览器
driver2 = webdriver.Firefox()
driver2.get("http://www.cnblogs.com/yoyoketang/")
print(driver2.title)
# 关掉第一个浏览器窗口
driver1.quit()
# 点首页"博客园"按钮
driver2.find_element_by_id("blog_nav_sitehome").click()
time.sleep(2)
print(driver2.title)

三、封装启动浏览器方法
1.如果涉及到不同的浏览器(如Firefox、chrome)之间的切换,我们可以专门写一个函数去启动不同浏览器

四、参考代码

# coding:utf-8
from selenium import webdriver
def browser(browser='firefox'):
    '''
    open browser "firefox"、"chrome"、"ie"、"phantomjs"
    usage:
    driver = broswer("chrome")
    '''
    try:
        if browser == "firefox":
            driver = webdriver.Firefox()
            return driver
        elif browser == "chrome":
            driver = webdriver.Chrome()
            return driver
        elif browser == "ie":
            driver = webdriver.Ie()
            return driver
        elif browser == "phantomjs":
            driver = webdriver.PhantomJS()
            return driver
        else:
            print("Not found browser!You can enter 'firefox', 'chrome', 'ie' or 'phantomjs'")
    except Exception as msg:
        print "open browser error:%s" % msg
if __name__ == "__main__":
    # 默认启动firefox
    driver_firefox = browser()
    driver_firefox.get("https://www.baidu.com")
    print("open browser:%s" % driver_firefox.name)
    print(driver_firefox.title)
    # 启动第phantomjs
    driver_pj = browser("phantomjs")
    driver_pj.get("http://www.cnblogs.com/yoyoketang/")
    print("open browser:%s" % driver_pj.name)
    print(driver_pj.title)

5.1 分布式(Grid)

Selenium grid是用来分布式执行测试用例脚本的工具,比如测试人员经常要测试多浏览器的兼容性,那就可以用到grid了。下面就来介绍如何在多个浏览器上运行同一份脚本。
使用grid所需要的文件:1.Selenium server(即selenium-server-standalone-x.xx.x.jar);2.grid配置文件(该文件负责提供主机和浏览器信息);3.测试脚本。
一、先来看看grid配置文件的内容:
def grid():    d={'http://127.0.0.1:4444/wd/hub' : 'firefox',        'http://127.0.0.1:5555/wd/hub' : 'internet explorer',        }    return d
该文件定义了一个方法,该方法存放了一个字典,分别给本机分配了2个不同的端口并指定了不同的浏览器(4444是grid hub的默认端口,5555这个是一个node的端口,后续会介绍)。
二、再来看看测试脚本: 

该脚本是写了一个百度搜索关键词并做了简单断言的脚本,导入的grid_module就是第一步中的grid配置文件,循环体中写的是从字典中取出主机名和浏览器名赋给下面的参数,这样的话该测试脚本就会接连调用本地的2个指定浏览器并运行。
3.然后就启server了,从http://selenium-release.storage.googleapis.com/index.html上下载对应版本的Selenium server:

 

 

 

下载下来后打开cmd,输入Java -jar selenium-server-standalone-x.xx.x.jar -role hub,这是万恶之源,先启它才能干后面的事。启动之后再打开一个cmd,输入java -jar selenium-server-standalone-x.xx.x.jar -role node -port 5555,这是启动第一个node,指定端口5555,与grid配置文件中所写的端口一致。
hub和node启动好之后我们在浏览器里输入http://127.0.0.1:4444/grid/console,打开grid的控制台: 

我们能看到有一个端口为5555的node已经启动起来了,此处的IP就是本机IP。


第6章  selenium phantomjs页面解析使用

我们都知道Selenium是一个Web的自动化测试工具,可以在多平台下操作多种浏览器进行各种动作,比如运行浏览器,访问页面,点击按钮,提交表单,浏览器窗口调整,鼠标右键和拖放动作,下拉框和对话框处理等,我们抓取时选用它,主要是Selenium可以渲染页面,运行页面中的JS,以及其点击按钮,提交表单等操作。

from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get("http://www.xxxxxx.com")
data = driver.title
print data

我们为什么要用phantomjs呢?

介绍

PhantomJS是一个基于webkit的JavaScript API。任何你可以在基于webkit浏览器做的事情,它都能做到。它不仅是个隐形的浏览器(没有UI界面的浏览器),提供了诸如CSS选择器、支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,同时也提供了处理文件I/O的操作,从而使你可以向操作系统读写文件等。PhantomJS的用处可谓非常广泛,诸如前端无界面自动化测试(需要结合Jasmin)、网络监测、网页截屏等。

windows下进行安装:

pip install selenium

phantomjs使用简单的使用方式:

from selenium import webdriver
browser = webdriver.PhantomDS('D:\phantomjs.exe') #浏览器初始化;Win下需要设置phantomjs路径,linux下置空即可
url = 'http://www.xxxxxx.com' # 设置访问路径地址
browser.get(url) # 打开网页
title = browser.find_elements_by_xpath('xxxxxx') #用xpath获取元素
for t in title: # 遍历输出
  print t.text #输出其中文本
  print t.get_attribute(’class’)# 输出属性值
browser.qiiit() #关闭浏览器。当出现异常时记得在任务浏览器中关闭

我们进行一个简单的对比操作,首先请回顾一下selenium webdriver的操作

from selenium import webdriver
driver = webdriver.Firefox()
driver.get("https: //www.xxxxxx.com/")
dniver.find_element_by_id('xxxxxxxx').send_keys("nxxxxxx")
dniver.find_element_by_id("xxxxxxxx").click()
driver.quit()

使用phantomjs

from selenium import webdriver
driver = webdriver.PhantomJS()
driver.set_window_size(xxx,xxx) #浏览器大小
driver.get ("https: //www.xxx.com/")
dniver.find_element_by_id('xxxx').send_keys("xxxx")
dniver.find_element_by_id("xxxxxx").click()
print driver.current_url
driver.quit()

通过以上两个案例大家应该可以看出相关的一个区别所在!!
编写一个简单的断言来判断phantomjs获取得到的URL是否正确的呢:

import unittest
from selenium import webdriver
class TestOne(unittest.TestCase):
    def setUp(self):
        self.driver = webdniver.PhantomDS()
        self.driver.set_window_size(xxx, xxx)
    def test_url(self):
        self.driver.get("https://www.xxx.com")
        self.driver.find_element_by_id('xxxxxx').send_keys("xxxx")
        self.driver.find_element_by_id("xxxxx").click()
        self.assentln("https://www.xxx.com", self.driver.current_url)
    def tearDown(self):
        self.driver.quit()

if __name__ == "__main__":
    unittest.main()                  

那么你会发现通过以上的单元测试进行断言后是完全可以通过的。
使用PhantomJS在浏览器的一个主要优点是测试通常要快得多。

import unittest
from selenium import webdriver
import time

class TestThree(unittest.TestCase):
  def setUp(self):
    self.startTime = time.time()
  def test_unl_fire(self):
    time.sleep(2)
    self.driver = webdniver.Firefox()
    self.driver.get("https://www.xxx.com")
    button = self.driver.find_element_by_id("xxx").get_attribute("xxxx")
    self.assentEquals('xxxxx', button)
  def test_unl_phantom(self):
    time.sleep(l)
    self.driver = webdniver.PhantomDS()
    self.driver.get("https://www.xxx.com")
    button = self.driver.find_element_by_id("xxxx").get_attribute("xxxx")
    self.assentEquals('xxxxx', button)
  def tearDown(self):
    t = time.time() - self.startTime
            print "%s: %.3f"% (self.id(), t)
            self.driver.quit()

if __name__== '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(TestThree)
    unittest.TextTestRunner(verbosity=0).run(suite)                

 

通过两个时间上的一个对比你会发现使用phantomjs速度有多快
内容拓展:

# coding:utf-8
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 nose.tools as nose

#帐户
email = 'user'
password = 'password'

# phantomjs

# user agent
user_agent = 'Mozilla/5.0 (Windows NT 5.1)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/29.0.1547.66 Safari/537.36'

# PhantomUS的路径
pjs_path = 'xx/node_modules/phantomjs/bin/phantomjs
dcap = {"phantomjs.page.settings.userAgent":
user_agent,
'marionette' : True
}

driver = webdriver.PhantomJS(executable_path=pjs_path,
desired_capabilities=dcap)
# 5秒
wait = WebDriverWait(driver, 5)
#获取html登录页面
login_page_url = 'http://xxx'
driver.get(login_page_url)
#等到页面加载
wait.until(ec.presence_of_all_elements_located)
#检查当前网址
nose.eq_('http://xxx', driver.current_url)

# login

# button click
show_signin = driver.find_element_by_id('xxx')
show_signin.click()

# email
login_xpath = 'xxx"]'

#等待对象元素
wait.until(ec.visibility_of_element_located((By.XPATH, login_xpath)))

login_id_form =driver.find_element_by_xpath(login_xpath)
login_id_form.clean()
login_id_form.send_keys(email)

# password
password_xpath = 'xxxx'
#等待对象元素
wait.until(ec.visibility_of_element_located((By.XPATH, password_xpath)))
# password
password_form = driver.find_element_by_xpath(passwond_xpath)
password_form.clean()
password_form.send_keys(password)
# submit
submit_xpath = 'xxxx'
dniver.find_element_by_xpath(submit_xpath).click()
# result
driver.get('http://xxx')
#等到页面加载
wait.until(ec.presence_of_all_elements_located)
#检查当前网址
nose.eq_('http://xxx', driver.current_url)
user_email = driver.find_element_by_xpath('xxx').get_attribute(
"XXX")
nose.eq_(email, user_email)

 (第7章):Page Object模式

什么是Page ObjectModel模式
Page Objects是selenium的一种测试设计模式,主要将每个页面看作是一个class。class的内容主要包括属性和方法,属性不难理解,就是这个页面中的元素对象,比如输入用户名的输入框,输入登陆密码的输入框,登陆按钮,这个页面的url等,而方法,主要是指这个页面可以提供的具体功能。
为什么选择POM?
我们先看一段简单的代码如下:

from selenium import webdriver
import time
 
driver = webdriver.Firefox()
driver.implicitly_wait(30)
 
# 启动浏览器,访问百度
driver.get("http://www.baidu.com")
 
# 定位百度搜索框,并输入selenium
driver.find_element_by_id("kw").send_keys("selenium")
 
# 定位百度一下按钮并单击进行搜索
driver.find_element_by_id("su").click()
time.sleep(5)

driver.quit()

这是一个简单的小脚本。脚本维护看起来很简单。但随着时间测试套件的增长。随着你在代码中添加越来越多的行,事情变得艰难。
脚本维护的主要问题是,如果10个不同的脚本使用相同的页面元素,并且该元素中的任何更改,则需要更改所有10个脚本。这是耗时且容易出错的。
更好的脚本维护方法是创建一个单独的类文件,它可以找到Web元素,填充或验证它们。该类可以在使用该元素的所有脚本中重用。将来,如果web元素有变化,我们需要在1个类文件中进行更改,而不是10个不同的脚本。
什么是POM?
页面对象模型  是 为Web UI元素创建Object Repository的设计模式  。
在这个模型下,对于应用程序中的每个网页,应该有相应的页面类。
此Page类将会找到该Web页面的WebElements,并且还包含对这些WebElements执行操作的页面方法。
这些方法的名称应该按照他们正在执行的任务给出,即如果一个加载程序正在等待支付网关出现,POM方法名称可以是waitForPaymentScreenDisplay()。

下图为非POM和POM对比图:

技术分享

在自动化测试中,引入了Page Object Model(POM):页面对象模式来解决,POM能让我们的测试代码变得可读性更好,高可维护性,高复用性。
POM的优势
1.    POM提供了一种在UI层操作、业务流程与验证分离的模式,这使得测试代码变得更加清晰和高可读性。

2.    对象库与用例分离,使得我们更好的复用对象,甚至能与不同的工具进行深度结合应用。

3.    可复用的页面方法代码会变得更加优化。

4.    更加有效的命名方式使得我们更加清晰的知道方法所操作的UI元素。例如我们要回到首页,方法名命名为: gotoHomePage(),通过方法名即可清晰的知道具体的功能实现。

案例说明:
以下是简单普通的登录测试用例:

def test_login_mail(self):
 driver = self.driver
 driver.get("http://www.xxx.xxx.com")
 driver.find_element_by_id("idInput").clear()
 driver.find_element_by_id("xxxxxxx").send_keys("xxxxx")
 driver.find_element_by_id("xxxxxxx").clear()
 driver.find_element_by_id("xxxxxxx").send_keys("xxxxxx")
 driver.find_element_by_id("loginBtn").click()

那我们如何进行一个改造升级呢?
改造案例思路:
第一, 我们要分离测试对象(元素对象)和测试脚本(用例脚本),那么我们分别创建两个脚本文件,分别为: LoginPage.py 用于定义页面元素对象,每一个元素都封装成组件(可以看做存放页面元素对象的仓库)  CaseLoginTest.py 测试用例脚本。
第二, 设计实现思想,一切元素和元素的操作组件化定义在Page页面,用例脚本页面,通过调用Page中的组件对象,进行拼凑成一个登录脚本。

BasePage.py:

#-*- coding: utf-8-*-
from selenium.webdriver.support.wait importWebDriverWait
from seleniumimport webdriver
classAction(object):
"""
 BasePage封装所有页面都公用的方法,例如driver, url ,FindElement等
"""
#初始化driver、url、等

def __init__(self,selenium_driver, base_url, pagetitle):
    self.base_url = base_url
    self.pagetitle = pagetitle
    self.driver = selenium_driver
    #打开页面,校验页面链接是否加载正确

def _open(self,url, pagetitle):
    #使用get打开访问链接地址
    self.driver.get(url)
    self.driver.maximize_window()

#使用assert进行校验,打开的链接地址是否与配置的地址一致。调用on_page()方法
    assertself.on_page(pagetitle), u"打开开页面失败 %s"% url
 
#重写元素定位方法
def find_element(self,*loc):
    #returnself.driver.find_element(*loc)
try:
WebDriverWait(self.driver,10).until(lambdadriver: driver.find_element(*loc).is_displayed())
return self.driver.find_element(*loc)

except:
print u"%s 页面中未能找到 %s 元素"%(self, loc)
 
#重写switch_frame方法
def switch_frame(self, loc):
return self.driver.switch_to_frame(loc)
#定义open方法,调用_open()进行打开链接
def open(self):
 self._open(self.base_url, self.pagetitle)
 
#使用current_url获取当前窗口Url地址,进行与配置地址作比较,返回比较结果(True False)
def on_page(self,pagetitle):
return pagetitlein self.driver.title
 
#定义script方法,用于执行js脚本,范围执行结果
def script(self,src):
 self.driver.execute_script(src)

#重写定义send_keys方法
def send_keys(self, loc, vaule, clear_first=True, click_first=True):
try:
 loc = getattr(self,"_%s"% loc)
if click_first:
 self.find_element(*loc).click()
if clear_first:
 self.find_element(*loc).clear()
 self.find_element(*loc).send_keys(vaule)
exceptAttributeError:
print u"%s 页面中未能找到 %s 元素"%(self, loc)

LoginPage.py:

#-*- coding: utf-8-*-
from selenium.webdriver.common.by importBy
import BasePage
#继承BasePage类
class LoginPage(BasePage.Action):
#定位器,通过元素属性定位元素对象
 username_loc=(By.ID,"idInput")
 password_loc =(By.ID,"pwdInput")
 submit_loc =(By.ID,"loginBtn")
 span_loc=(By.CSS_SELECTOR,"div.error-tt>p")
 dynpw_loc =(By.ID,"lbDynPw")
 userid_loc =(By.ID,"spnUid")

#Action
def open(self):
#调用page中的_open打开连接
self._open(self.base_url,self.pagetitle)
#调用send_keys对象,输入用户名
def input_username(self, username):
 self.find_element(*self.username_loc).send_keys(username)
#调用send_keys对象,输入密码
def input_password(self, password):
 self.find_element(*self.password_loc).send_keys(password)
#调用send_keys对象,点击登录

def click_submit(self):
 self.find_element(*self.submit_loc).click()
#用户名或密码不合理是Tip框内容展示
def show_span(self):
returnself.find_element(*self.span_loc).text
#切换登录模式为动态密码登录(IE下有效)
def swich_DynPw(self):
 self.find_element(*self.dynpw_loc).click()
#登录成功页面中的用户ID查找
def show_userid(self):
returnself.find_element(*self.userid_loc).text
Caselongintest.py

#-*- coding: utf-8-*-
import sys
reload(sys)
sys.setdef aultencoding(utf-8)
import unittest
from POimportLoginPage
from seleniumimport webdriver
classCaselogin126mail(unittest.TestCase):
"""
登录case
 """
@classmethod
def setUpClass(cls):
 cls.driver = webdriver.Chrome()
 cls.driver.implicitly_wait(30)
 
 cls.url ="http://xxxx.xxx.com"
 cls.username ="xxxxx"
 cls.password ="xxxxx"
 
#用例执行体
def test_login_mail(self):
#声明LoginPage类对象
login_page=LoginPage.LoginPage(self.driver, self.url, u”xxxxx”)
 
#调用打开页面组件
login_page.open()
#调用用户名输入组件
login_page.input_username(self.username)
#调用密码输入组件
login_page.input_password(self.password)
#调用点击登录按钮组件
login_page.click_submit()
@classmethod
def tearDownClass(cls):
 cls.driver.quit()
 
if __name__=="__main__":
 unittest.main()

使用POM进行重新构造代码结构后,发现代码测试用例代码的可读性提高很多,元素写成组件的方式,不需要每次都写findElement直接在脚本中调用组件就可以使用。
在CaseLoginTest脚本用例执行体中,一旦我们输入 login_page并敲入一个点时,LoginPage页面中的元素对象组件都显示出来。并且定义好的PageObject组件可以重复在其它的脚本中进行使用,减少了代码的工作量,也方便对脚本进行后期的维护管理,当元素属性发生变化时,我们只需要对一个PageObaject页面中的对象组件定义进行更改即可。
最后做个总结,所有代码请手动输入,不要直接拷贝。
再次对POM进行小结:

1.    POM是selenium webdriver自动化测试实践对象库设计模式
2.    POM使得测试脚本更易于维护
3.    POM通过对象库方式进一步优化了元素、用例、数据的维护组织


(第8章) :多线程

前戏:线程的基础

运行多个线程同时运行几个不同的程序类似,但具有以下优点:
进程内共享多线程与主线程相同的数据空间,如果他们是独立的进程,可以共享信息或互相沟通更容易.
线程有时称为轻量级进程,他们并不需要多大的内存开销,他们关心的不是过程便宜.
一个线程都有一个开始,执行顺序,并得出结论。它有一个指令指针,保持它的上下文内正在运行的跟踪.
(1)、它可以是抢占(中断)
(2)、它可以暂时搁置(又称睡眠),而其他线程正在运行
看一下以下的小案例:

import thread
from time import sleep, ctime

def loop0():
     print "loop 0开始时间:",ctime() #第一个函数loop0开始时间
     sleep(4) # 休眠4秒
     print "loop 0 结束时间:_’,ctime()

def loopl():
     print "loop 1 开始时间:",ctime()
     sleep(2)
     print "loop 1 结束时间:_’,ctime()

def main():
     print "程序开始时间:",ctime()
     thread.start_new_thread(loop0,()) # 第二个参数是必不可少的,即使loope没有传递参数,仍然要写一个空元组
     thread.stant_new_thnead(loopl,())
     sleep(6) #这里休眠6秒的原因是确保两个线程己经执行完毕,主线程才接着执行下面的语句
     print "程序结束时间:",ctime()

if __name__ == '__main__':
     main()

在web测试中,不可避免的一个测试就是浏览器兼容性测试,在没有自动化测试前,我们总是苦逼的在一台或多台机器上安装N种浏览器,然后手工在不同的浏览器上验证主业务流程和关键功能模块功能,以检测不同浏览器或不同版本浏览器上,我们的web应用是否可以正常工作。如果我们使用selenium webdriver,那我们就能够自动的在IE、firefox、chrome、等不同浏览器上运行测试用例。为了能在同一台机器上不同浏览器上同时执行测试用例,我们需要多线程技术。下面我们基于python的多线程技术来尝试同时启动多个浏览器进行selenium自动化测试。