python+selenium自动化软件测试

2020年07月23日 阅读数:70
这篇文章主要向大家介绍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浏览器的,这点很是方便。javascript

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

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


接下来的内容以selenium2为主。html


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

python 2.7.12java

selenium 2.53.6node

firefox 44python

 

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

 

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

 

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
 
1、安装python
1.安装python这个简单,下载版本后傻瓜式安装就好了。
2.安装好以后,看下这个目录D:\python\Scripts,有没pip.exe和easy_install.exe(通常都有,没有的话得从新安装一次了)
3.将D:\python和D:\python\Scripts,添加到环境变量path下

 

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

 

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

 

4、检查selenium环境

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

 

5、遇到第一个坑:'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根目录)
 
6、遇到第二坑: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下。

 

7、遇到第三坑: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、遇到问题
1.在使用脚本打开浏览器时候,发现右上角原来下载的插件firebug不见了,到底去哪了呢?
2.用脚本去打开浏览器时候,实际上是从新打开了一个进程,跟手动打开浏览器不是一个进程。
因此没主动加载插件,不过selenium里面其实提供了对应的方法去打开,只是不多有人用到。

 

 

2、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,启动的是一个新的,有的话就加载指定的路径。

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

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

 

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

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

 

5、参考代码:

# 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、认识多窗口
1.打开赶集网:http://bj.ganji.com/,点击招聘求职按钮会发现右边多了一个窗口标签

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

 

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

 

 2、获取当前窗口句柄

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

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

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

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

 

 

4、切换句柄

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

方法一(不推荐):

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

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

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

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

方法二:

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

5、关闭新窗口,切回主页
   1.close是关闭当前窗口,由于此时有两个窗口,用close能够关闭其中一个,quit是退出整个进程(若是当前有两个窗口,会一块儿关闭)。
   2.切换到首页句柄:h
   3.打印当前页面的title,看是否切换到首页了

 

6、参考代码

# 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、定位搜索结果
    1.在百度搜索框输入关键字“测试部落”后,用firebug查看页面元素,能够看到这些搜索结果有共同的属性。

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

 

2、确认定位结果
    1.前面的定位策略只是一种猜测,并不必定真正获取到本身想要的对象的,也行会定位到一些不想要的对象。
    2.因而能够获取对象的属性,来验证下是否是定位准确了。这里能够获取href属性,打印出url地址。

 

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

 

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

 

5、经过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

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

2、案例操做:163登陆界面
1.打开http://mail.163.com/登陆页面

2.用firebug定位登陆框

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

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

 3、切换iframe
1.因为登陆按钮是在iframe上,因此第一步须要把定位器切换到iframe上
2.用switch_to_frame方法切换,此处有id属性,能够直接用id定位切换

 

 

4、若是iframe没有id怎么办?
1.这里iframe的切换是默认支持id和name的方法的,固然实际状况中会遇到没有id属性和name属性为空的状况,这时候就须要先定位iframe元素对象
2.定位元素仍是以前的八种方法一样适用,这里我能够经过tag先定位到,也能达到一样效果

 

5、释放iframe
1.当iframe上的操做完后,想从新回到主页面上操做元素,这时候,就能够用switch_to_default_content()方法返回到主页面

 

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

 

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

 

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


8、参考代码以下:

# 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下拉框相关的操做方法。

1、认识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>

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

 

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

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

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

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

 

4、Select模块(index)

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

5、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")

 

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

 

7、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()   :返回全部的选项

 

8、整理代码以下:

# 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上没有输入框

1、认识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>  

2、alert操做

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

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

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

5、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()

 

 6、最终代码

# 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、认识单选框和复选框
    1.先认清楚单选框和复选框长什么样

    2.各位小伙伴看清楚哦,上面的单选框是圆的;下图复选框是方的,这个是业界的标准,要是开发小伙伴把图标弄错了,能够先抽他了。
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> 

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

 

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

 

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

  2.那么问题来了:若是想所有勾选上呢?

5、所有勾选:
    1.所有勾选,能够用到定位一组元素,从上面源码能够看出,复选框的type=checkbox,这里能够用xpath语法:.//*[@type='checkbox']

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


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

 

7、参考代码:

# 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表格如何定位。
1、认识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> 

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

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

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

 

 5、参考代码:

# 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配置

1、加载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/")

2、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.打开博客园写随笔,首先须要登陆,这里为了不透露我的帐户信息,我直接加载配置文件,免登陆了。
      

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

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

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

 

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

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

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

5、参考代码:

# 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.
 
1、SendKeys安装
1.pip安装SendKeys
>pip install SendKeys

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

2、参考代码
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、识别上传按钮
1.点开博客园编辑器里的图片上传按钮,弹出”上传本地图片”框。
2.用firebug查看按钮属性,这种上传图片按钮有个很明显的标识,它是一个input标签,而且type属性的值为file。只要找到这两个标识,咱们就能够直接用send_keys()方法上传文件了。

 

 

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

 

4.能够经过标签订位全部的iframe标签,而后取对应的第几个就能够了。

 

3、文件上传

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 获取元素属性

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

 

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

2、获取元素的文本
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获取到文本

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

 

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

5、获取输入框内的文本值
一、若是在百度输入框输入了内容,这里输入框的内容也是能够获取到的

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

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

# 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方法能够获取到页面源码。
1、page_source
1.selenium的page_source方法能够直接返回页面源码
2.从新赋值后打印出来

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

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

4、参考代码

# 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而后下个帐号登陆
1、获取cookies:get_cookies()
1.获取cookies方法直接用:get_cookies()
2.先启动浏览器,获取cookies,打印出来发现是空:[]
3.打开博客首页后,从新获取cookies,打印出来,就有值了

 

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

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

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

5、清除全部cookies:delete_all_cookies()
1.清除全部cookies后登陆状态也失效了,cookies为空[]

6、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的值
(第五个方法能够用于绕过验证码登陆,下篇详细介绍)

 7、参考代码

# 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的方法。
1、fiddler抓包
1.前一篇讲到,登陆后会生成一个已登陆状态的cookie,那么只须要直接把这个值添加到cookies里面就能够了。
2.能够先手动登陆一次,而后抓取这个cookie,这里就须要用抓包工具fiddler了
3.先打开博客园登陆界面,手动输入帐号和密码(不要点登陆按钮)

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

 

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

2、添加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...'}

3、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。

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

5、参考代码:

# 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的脚本。

1、JavaScript简介

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

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

2、控制滚动条高度
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是最底部。

3、横向滚动条
1.有时候浏览器页面须要左右滚动(通常屏幕最大化后,左右滚动的状况已经不多见了)。

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

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

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

4、Chrome浏览器
1.以上方法在Firefox上是能够的,可是用Chrome浏览器,发现无论用。
谷歌浏览器就是这么任性,不听话,因而用如下方法解决谷歌浏览器滚动条的问题。
2.Chrome浏览器解决办法:

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

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

 

 

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

8、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)

9、参考代码以下:

# 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、加载配置
    1.打开博客园写随笔,首先须要登陆,这里为了不透露我的帐户信息,我直接加载配置文件,免登陆了。
      不懂如何加载配置文件的,看加载firefox配置

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

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

 

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

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

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

5、参考代码:

# 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、日历控件
    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">

 

 2、去掉readonly属性

    1.很明显这种元素的属性是readonly,输入框是没法直接输入的,这时候须要先去掉元素的readonly属性,而后就能够输入啦。

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

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

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

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

 

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

6、参考代码以下:

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、内嵌滚动条
    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>

2、纵向滚动

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

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

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

 

3、横向滚动

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

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

有时候不少元素属性都同样时候,就能够用复数定位,取对应的第几个就能够了。

2.25 js处理多窗口

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

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

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

 

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

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

5、参考代码

# 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、遇到的问题
1.在练习百度的搜索设置按钮时,点保存设置按钮,alert弹出没弹出(代码没报错,只是获取alert失败),相信不仅是我一我的遇到过。

2、点击父元素
1.遇到这种问题,应该是前面操做select后致使的后遗症(由于我注释掉select那段是能够点击成功的)。
2.第一种解决办法,先点击它的父元素一次,而后再点击这个元素。

3.实现代码以下

3、js直接点击

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

4、参考代码

# 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、十八种定位方法

前八种是你们都熟悉的,常常会用到的

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)

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

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

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

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

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

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 页面的,还能够在服务器上以浏览器的方式呈现!
1、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里面不知道到底有多少个函数或方法,那如何查看呢?

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

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

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

 

4、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自动化测试的时候势必要处理这些弹窗,这里就介绍一下目前前端界两种弹窗的处理方法。
1、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()

2、自定义弹窗 
因为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、定位输入框联想词
1.首先在百度输入框输入关键词,如:博客,而后输入框下方会自动匹配出关键词。
2.这时候能够用firebug工具定位到联想出来的词,能够看到下方匹配出来的词都有共同的class属性,这时候就能够所有定位到了。

2、打印所有匹配出来的词
1.经过get_attribute()方法获取到文本信息

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

3、参考代码

# 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输入框输入文本
 
1、如下总结了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标准的选择器语法
 
2、id定位
1.定位博客首页的管理按钮:id="blog_nav_contact"

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

 

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

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

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

5、参考代码:

# 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
这个报错意思是说定位语法错了。
 
1、定位带空格的class属性
1.以126邮箱为例:http://mail.126.com/,定位帐号输入框

 

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

 

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

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

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

5、class属性不惟一怎么办
1.若是这个class的多个属性都不是惟一的咋办呢,元素不惟一也不用怕,能够用复数定位,把全部的相同元素定位出来,按下标取第几个就行。

 

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

 

7、参考代码

# 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,简直逆天了!
 
1、jquery搜索元素
1.按F12进控制台
2.点所有按钮
3.右侧若是没出现输入框,就点下小箭头按钮
4.输入框输入jquery定位语法,如:$("#input1")

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

 

2、jquery定位语法
1.jquery语法能够学下w3school的教程:http://www.w3school.com.cn/jquery/jquery_syntax.asp

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

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

4、参考脚本

# 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、案例分析
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()

2、执行结果

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

 

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

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


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

4、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、新建测试项目
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,接下来用这个脚本去批量执行全部的用例。

2、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>]>

3、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、装饰器
1.用setUp与setUpClass区别

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

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

 

2、执行顺序
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

3、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)
 
1、导入HTMLTestRunner
1.这个模块下载不能经过pip安装了,只能下载后手动导入,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html

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

2、demo解析

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

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

4、测试报告详情
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.从新运行后查看测试报告

 

5、参考代码:
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、中文乱码
1.测试报告中,msg自定义异常内容有中文状况会出现乱码,以下图所示

 

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

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

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

3、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、新建工程
1.打开pycharm左上角File>New Project,在Location位置输入项目名称:D:\test\test_blog
2.建立以后,选择Opin in current window就能够了

2、项目结构
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          注意这个脚本放文件根目录

3、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、登陆方法封装
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、发生异常
1.打开博客首页,定位“新随笔”元素,此元素id="blog_nav_newpost"
2.为了故意让它定位失败,我在元素属性后面加上xx
3.运行失败后以下图所示,程序在查找元素的这一行发生了中断,不会继续执行click事件了

2、捕获异常
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类。

3、参考代码:

# 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()

 

4、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、截图方法
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()

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

 

 

3、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,运行无异常,放心大胆食用。
 
1、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()                                                         # 关闭

2、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()                                                        # 关闭

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

4、发送带附件
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.最后结果,有图有真相

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

前言
当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候咱们但愿暂时跳过这些用例。
或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,若是第一步就失败了,后面的用例也就不必去执行了,直接跳过就行,节省用例执行时间。
1、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.

翻译:断言的时候跳过。


2、skip案例

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

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

4、参考代码:

# 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、参数解释
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())
        """

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

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

 

4、参考代码:

# 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

5、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等设置为一个参数,这样只需维护定位方式的参数就好了。
1、find_element()
1.selenium元素定位里面实际上是有这个方法的,只是大部分时候都是结合By方法使用,以下图:

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

3、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"

4、定位参数化
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"

 

5、参考代码

# 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、登陆方法
1.把输入帐号、输入密码、点击登陆按钮三个步骤写成一个方法
2.而后把输入的帐号和密码参数化

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

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

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

 

4、优化后案例
1.优化后的登陆案例以下,这样看起来更清楚了

5、参考代码

# 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、环境准备
1.先安装xlrd模块,打开cmd,输入pip install xlrd在线安装
>>pip install xlrd

 

2、基本操做
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)  # 获取第一列值

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

 

4、封装读取方法
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、环境准备
1.安装ddt模块,打开cmd输入pip install ddt在线安装
>>pip install ddt

2、数据驱动原理
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!

3、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,本篇先介绍下有哪些功能,后续更新中会单个去介绍。


1、功能介绍和翻译

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。

 

2、查看源码和注释
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、源码分析
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,布尔值。

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

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

4、参考代码

# 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。

1、源码分析

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

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

 

 

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

 

3、失败案例
1.若是判断失败,就返回False

 

4、判断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值
 
5、参考代码

# 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这个模块

1、判断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

2、实例操做

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

3、参考代码

# 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、显示等待
1.若是你的定位元素代码,仍是这样:driver.find_element_by_id("kw").send_keys("yoyo"),那说明你还停留在小学水平,如何让代码提高逼格呢?
2.前面讲过显示等待相对于sleep来讲更省时间,定位元素更靠谱,不会出现一会正常运行,一会又报错的状况,因此咱们的定位需与WebDriverWait结合
3.以百度的搜索为例

2、封装定位方法
1.从上面代码看太长了,每次定位写一大串,这样不方便阅读,写代码的效率也低,因而咱们能够把定位方法进行封装
2.定位方法封装后,咱们每次调用本身写的方法就方便多了

3、封装成类
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)

4、参考代码

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设计模式

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

定位元素方法和操做元素方法分离开,元素定位所有放一块儿,每个操做元素动做写成一个方法。

2、定位方法对应参照表
    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"
3、参考代码以下
1.新建如下脚本。

2、定位方法对应参照表
    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"
3、参考代码以下
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()

4、用例参考
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、函数做为形参
1.函数的参数也能够是另一个函数,也就是说传的参数不只能够是常见的字符串、数字等,也能够是一个函数
2.定义aaa为一个加法函数,bbb为减法函数
3.calculate这个函数传三个参数,第一个参数是一个函数,另外两个参数是函数的两个参数

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

3、实现百度搜索功能

# 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、不带变量的装饰器
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

 

2、带变量的装饰器。
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

 

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

4、参考案例

# 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去查就行。
 
1、启动两个driver
1.若是我想启动2个火狐,一个火狐打开百度,另一个火狐打开博客园,咱们只需用2个实例driver去控制就行

(注意:不要两个都叫driver,要否则后面的会覆盖前面的,致使没法继续操做前面那个浏览器窗口了)

2.运行后结果,桌面启动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)

3、封装启动浏览器方法
1.若是涉及到不一样的浏览器(如Firefox、chrome)之间的切换,咱们能够专门写一个函数去启动不一样浏览器

4、参考代码

# 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.测试脚本。
1、先来看看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的端口,后续会介绍)。
2、再来看看测试脚本: 

该脚本是写了一个百度搜索关键词并作了简单断言的脚本,导入的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、HTML五、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秒的缘由是确保两个线程己经执行完毕