selenium自学笔记

元素定位基本的有八种
通过id定位 find_element_by_id()
通过name定位 find_element_by_name()
通过class定位 find_element_by_class_name()
通过tag定位 find_element_by_tag_name()
通过link定位 find_element_by_link_text()
通过partial link定位 find_element_by_partial_link_text()
通过XPath定位 find_element_by_xpath()
通过CSS定位 find_element_by_css_selector()


find_element()方法只用于定位元素。
find_element()方法只用于定位元素。
它需要两个参数,第一个参数是定位方式,这个由By 提供;
第二个参数是定位的值。

在使用By 时需要将By 类导入。

from selenium.webdriver.common.by import By


find_element(By.ID,"kw")
find_element(By.NAME,"wd")
find_element(By.CLASS_NAME,"s_ipt")

常规操作
1.clear()清空输入框
# 清空输入框信息 driver.find_element_by_name("email").clear()

2.send_keys()输入文本信息
# 输入框输入文本信息
driver.find_element_by_name("email").send_keys("adb")
driver.find_element_by_name("password").send_keys("123")

3.click()模拟单击鼠标
# 通过click()模拟鼠标点击事件
driver.find_element_by_id("dologin").click()

4.submit()模拟回车操作
# 也可以通过submit()模拟回车键(这里回车时候,光标是在密码框时候回车的)
driver.find_element_by_name("password").submit()

鼠标事件
1.鼠标事件:
from selenium.webdriver.common.action_chains import ActionChains
context_click() 右击
double_click() 双击
drag_and_drop(source, target)拖动
move_to_element() 鼠标悬停

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
time.sleep(3)
mouse = driver.find_element_by_link_text("设置")
ActionChains(driver).move_to_element(mouse).perform()
time.sleep(1)
driver.find_element_by_link_text("搜索设置").click()


# coding:utf-8
from selenium import webdriver
# 导入ActionChains 类
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Firefox()
# 定位元素的源位置
element = driver.find_element_by_id("xxx")
# 定位元素要移动到的目标位置
target = driver.find_element_by_id("xxx")
# 执行元素的拖放操作
ActionChains(driver).drag_and_drop(element, target).perform()


3.键盘事件

from selenium.webdriver.common.keys import Keys
send_keys(Keys.BACK_SPACE) 删除键(BackSpace)
send_keys(Keys.SPACE) 空格键(Space)
send_keys(Keys.TAB) 制表键(Tab)
send_keys(Keys.ESCAPE) 回退键(Esc)
send_keys(Keys.ENTER) 回车键(Enter)
send_keys(Keys.CONTROL,'a') 全选(Ctrl+A)
send_keys(Keys.CONTROL,'c') 复制(Ctrl+C)
send_keys(Keys.CONTROL,'x') 剪切(Ctrl+X)
send_keys(Keys.CONTROL,'v') 粘贴(Ctrl+V)
send_keys(Keys.F1) 键盘F1
……
Send_keys(Keys.F5)键盘F5

send_keys(Keys.F12) 键盘F12

from selenium import webdriver
# 导入Keys 模块
from selenium.webdriver.common.keys import Keys
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
# 输入框输入内容
driver.find_element_by_id("kw").send_keys("seleniumm")
# 删除多输入的一个m
driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)
# 输入空格键+“教程”
driver.find_element_by_id("kw").send_keys(Keys.SPACE)
driver.find_element_by_id("kw").send_keys(u"教程")
# ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
# ctrl+x 剪切输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')
# ctrl+v 粘贴内容到输入框
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'v')

 

一、iframe切换
1.通过id属性或name属性
switch_to_frame(name_or_id_or_frame_element )

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
url = "http://mail.126.com/"
driver.get(url)
# 这里用到implicitly_wait(),它的作用是全局的,也就是只用一次就可以了,只在find_element时候起作用
driver.implicitly_wait(10)
# 切换iframe,此处id="x-URS-iframe"
driver.switch_to_frame("x-URS-iframe")
driver.find_element_by_name("email").send_keys("adb")
driver.find_element_by_name("password").send_keys("123")
driver.find_element_by_id("dologin").click()

2.通过frame_element
switch_to_frame(name_or_id_or_frame_element)

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
url = "http://mail.126.com/"
driver.get(url)
driver.implicitly_wait(10)
# 先定位iframe
frame = driver.find_element_by_xpath('//iframe[@id="x-URS-iframe"]')
# 再通过frame_elenment切换iframe
driver.switch_to_frame(frame)
driver.find_element_by_name("email").send_keys("adb")
driver.find_element_by_name("password").send_keys("123")

3.通过页面上iframe的索引定位
driver.switch_to.frame(0) # 用frame的index来定位,第一个是0
定位箭头指向的这个就是第3个 driver.switch_to.frame(3)


5.多个iframe问题
# 从Top Windows切换到框架f1
driver.switch_to_frame('f1')
# 从框架f1到框架f2
driver.switch_to_frame('f2')
# 跳出所有frame,回到主界面
driver.switch_to_default_content()
# 返回上一级
driver.switch_to.parent_frame()

-----------------------------------------------------------------
多个窗口时候,该如何识别出想操作的窗口,操作页面元素呢
#打印当前句柄
c_handle = driver.current_window_handle
print c_handle

2.获取所有的句柄
all_handle = driver.window_handles
print all_handle

---------------------多窗口切换句柄--------------------------

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
driver = webdriver.Firefox()
driver.get("http://bj.ganji.com/")
time.sleep(3)
handle = driver.current_window_handle
print handle
# 第一个窗口的句柄
print driver.title
print type(handle)
driver.find_element_by_link_text("小本创业").click()
handles = list(driver.window_handles)
# 获取所有的
print handles
print type(handles)
handle_new = handles[1]
取新窗口的句柄
# 切换到新的窗口去了
driver.switch_to.window(handle_new)
time.sleep(2)
print driver.title
driver.close() # 关掉当前的窗口
# driver.quit()
# 退出整个driver进程
# 回到第一个窗口上
driver.switch_to.window(handles[0])
time.sleep(2)
print driver.title
#close 和 quit


-----------------隐式等待implicitly_wait--------------------------
1.Sleep 线程休眠
---一次有效

2. implicitly_wait 隐式等待
(不是万能的,当页面切换时,页面卡顿的话,它的焦点还在上一页面
Js报错,页面左上角转圈圈时,它会浪费时间)


# coding:utf-8
from selenium import webdriver import time
# 打开浏览器
driver = webdriver.Firefox()
# 打开赶集网页面 driver.get("http://bj.ganji.com/")
# 等待页面加载完成,作用是全局的
driver.implicitly_wait(10)
driver.find_element_by_link_text("小本创业").click()

----------select下拉框------------------
百度搜索设置的这种下拉框该如何定位呢?
1.第一种方式
# 直接通过xpath定位
driver.find_element_by_xpath(".//*[@id='nr']/option[3]").click()
# 二次定位
driver.find_element_by_id("nr").find_element_by_xpath("//option[@value='50']").click()

# 二次定位
driver.find_element_by_id("nr").find_element_by_xpath("//option[@value='50']").click()

 

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.select import Select
import time driver = webdriver.Firefox(
url = "https://www.baidu.com"
driver.get(url)
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)
# driver.find_element_by_xpath(".//*[@id='nr']/option[3]").click()
# parent = driver.find_element_by_id("nr")
# parent.find_element_by_xpath('.//option[@value="20"]').click()
abc = driver.find_element_by_id("nr")
# Select(abc).select_by_index(0)  # index
Select(abc).select_by_value("50")
# Select(abc).select_by_visible_text("每页显示20条")

 


选项框的另外一种形式
(这种不叫select,跟普通定位一样)

# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
time.sleep(3)
# 先让选项框弹出来
mouse = driver.find_element_by_link_text("设置")
ActionChains(driver).move_to_element(mouse).perform()
# 再点击选项框里面的元素
driver.find_element_by_link_text("搜索设置").click()


二、Alert弹窗
不是所有的弹出框都叫alert!!!!!!!!!!
表明上是弹出框,但不叫alert!!!
--- 实际上是一个窗口,需切换句柄
(但并不是说所有的都是窗口,有的就是普通的一层div,直接定位就行了)
switch_to_alert()
获取弹出框文本:text
确定:accept()
取消:
dismiss()
输入值:
send_keys()


# 点击保存设置按钮
driver.find_element_by_class_name("prefpanelgo").click()
# 等待两秒,等弹出框弹出来
time.sleep(2)
# 切换到弹出框
a = driver.switch_to_alert()
# 打印弹出框文本信息
print a.text
# 确定按钮
a.accept()

--------------------------------xpath常规属性----------------------------------
1.通过id定位
driver.find_element_by_xpath("//*[@id='kw']").send_keys("hao")
2.通过tag(标签)定位
*号匹配任何标签
driver.find_element_by_xpath("//*[@id='kw']")
也可以指定标签名称
driver.find_element_by_xpath("//input[@id='kw']")
3.通过class定位
driver.find_element_by_xpath("//input[@class='s_ipt']").send_keys("hao")
4.通过name定位
driver.find_element_by_xpath("//input[@name='wd']").send_keys("hao")
5.其它属性
driver.find_element_by_xpath("//input[@autocomplete='off']").send_keys("hao")
6.多个属性组合(逻辑运算)
driver.find_elements_by_xpath("//input[@type='text' and @name='wd']")
7.对于文本属性,语法:.//*[text()='文本内容']
除了这个文本属性匹配是.//*[text()=‘文本’]这种格式(无@)
其它的属性,如id,name,class等都是.//*[@id=‘xxx’] .//*[@name=‘xxx’]这种格式

 

xpath层级关系
1.相对路径:层级关系
driver.find_element_by_xpath("//form[@id='form']/span/input")
2.索引:如定位搜索选项框
driver.find_element_by_xpath("//*[@id='nr']/option[3]")
3.同一父级多个子元素
如果同一父级下,有多个相同的子元素,下标从1开始
.//*[@id='u1']/a[2]
也可以这样:.//*[@id='u1']/a[@class="mnav"][1]
4.多个相同元素不是一个父亲
如过页面上定位到多个相同的元素,但是父亲不是同一个
(这种情况就不能用下标了, /a[1])
driver.find_elements_by_xpath(“.//*[@id='sidebar_toptags']/div/ul/li/a”)[0]
5.通过儿子找父亲
两个点代表父级
.//*[@id='blog_nav_sitehome']/../..
6.模糊匹配
1.contains模糊匹配text
如下,通过模糊匹配text属性,找到百度首页的“糯米”网站超链接
driver.find_element_by_xpath("//a[contains(text(),'糯')]").click()
2.模糊匹配某个属性
xpath("//input[contains(@id,‘xx')]")
driver.find_element_by_xpath("//input[contains(@class,'s_ip')]").send_keys("hao")
3.模糊匹配以xx开头
xpath("//input[starts-with(@id,‘xx') ]")
driver.find_element_by_xpath("//input[starts-with(@class,'s_ip')]").send_keys("hao")


----------------------定位表格-------------------------------
Table表格固定格式:.//*[@id=‘表格id’]/tbody/tr[行数]/td[列数]/a
.//*[@id='bugList']/tbody/tr[6]/td[4]/a

Xpath定位方法传入可变参数

from selenium import webdriver
import time driver = webdriver.Firefox()
url = "http://127.0.0.1/zentao/user-login.html"
driver.get(url)
# 登陆
driver.find_element_by_id("account").send_keys("admin")
driver.find_element_by_name("password").send_keys("123456")
driver.find_element_by_id("submit").click()
time.sleep(2)
buglist = "http://127.0.0.1/zentao/bug-browse-1.html"
driver.get(buglist)
# 参数化行和列
x = 6
y = 4
table = ".//*[@id='bugList']/tbody/tr[%s]/td[%s]/a" % (x, y )
driver.find_element_by_xpath(table).click()

如何根据表格名称点后面按钮
解决思路:

1.先通过bug的标题名称找到这一行
2.再找到这一行的父节点
3.通过父节点往下搜(编辑按钮都是固定位置)
text = "上传多个附件"
t = './/*[text()="%s"]/../../td[@class="text-right"]/a[@title="编辑"]' % text
driver.find_element_by_xpath(t).click()

css

1.通过id定位
driver.find_element_by_css_selector("#kw").send_keys("hao")

2.通过class定位
driver.find_element_by_css_selector(".s_ipt").send_keys("hao")

3.通过标签、
driver.find_element_by_css_selector("input").send_keys("hao")

4.其它属性
driver.find_element_by_css_selector("[name='kw']")
driver.find_element_by_css_selector("[autocomplete='off']")

1.父子关系
driver.find_element_by_css_selector("span>input")
2.组合定位
driver.find_element_by_css_selector("form.fm>span>input.s_ipt")
driver.find_element_by_css_selector("form#form>span>input#kw")


定位到一组,取第几个:
:nth-child(2)

例:
.mnav:nth-child(2)

同时满足2个属性

class="form-control"][name="account"]

#account[class="form-control"]


1.获取对象返回值

# coding:utf-8 from selenium import webdriver
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get("https://www.baidu.com")
print(driver.title)


2.获取元素属性值get_attribute(“属性”)
t = driver.find_element_by_id(“su”).get_attribute("属性名称")
print(t)

3.判断元素是显示还是隐藏(返回布尔值)
d = driver.find_element_by_id("su").is_displayed()
print(d)

4.获取浏览器名称
print(driver.name)

5.获取元素size
s = driver.find_element_by_id("su").size
print(s)

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


unittest

注意:
1.每条用例设计必须是独立的,单独拿出来可以运行(不依赖其它用例)
2.一个功能点的相同操作(只是输入参数不一样),可以归为一个类
3.一个类里面输入不同参数的操作,可以为一个测试点
4.每个Testcase都有(非必须)前置条件(如果没有,可以写pass)
5.每个Testcase都有(非必须)后置条件(如果没有,可以写pass)
6.每个Testcase都必须有断言(Assert)

# coding=utf-8
from selenium import webdriver
import unittest
import time
class Baidu(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.get("https://www.baidu.com")
def test_001(self):
'''搜索:selenium'''
self.driver .xxx
def test_002(self):
''' 搜索:python'''
self.driver.xxx
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main()


注意:
1.测试类,继承单元测试TestCase这个类
2.(非必须)setUp(),无操作直接写pass
3. .(非必须) tearDown(),无操作直接写pass
4.测试用例必须以test开头
5.用例执行顺序是按ascii码顺序


脚本执行顺序是按ascii顺序来执行的
注意:1.用例之间不要存在依赖关系,每个用例都能单独运行

2.用例不要互相调用,需要调用的公共方法可以写成方法去调用

# coding:utf-8
import unittest
class Test(unittest.TestCase):
def setUp(self):
print("start")
def tearDown(self):
print("end")
def test_01(self):
print("111")
a = "abc"
b = "abcdef"
self.assertEqual(3, a+b) # 判断相等
def test_02(self):
print("22222222")
a = 3
b = 4
self.assertEqual(1, b-a)
def test_03(self):
print("33333333")
self.assertEqual(1, 1)
if __name__ == "__main__":
unittest.main()

setUpClass只打开一次浏览器,必须要加@classmethod修饰

# coding:utf-8
import unittest
import time
class Test1(unittest.TestCase):
'''测试类,是多个测试用例的集合,可以把一些相同的操作写成一个类'''
# def setUp(self):
# print "-----start!!!----" #
# def tearDown(self):
# print "-----end-------"
@classmethod
def setUpClass(cls):
print "-----打开浏览器----"
@classmethod
def tearDownClass(cls):
print "-----关掉浏览器------"

断言是一个测试用例的灵魂!!! 用例没断言,就是行尸走肉!
Assert是用来检查一个条件,如果它为真,就不做任何事。
如果它为假,则会抛出AssertError并且包含错误信息

1.返回值是否相等
self.assertEqual(a, b)
# 判断 a == b
self.assertNotEqual(a, b)
# 判断 a != b
2.返回值是布尔值
self.assertTrue(x)
# 判断布尔值 bool(x) is True
self.assertFalse(x)
# 判断布尔值 bool(x) is False
3.返回值是否为空
self.assertIsNone(x)
# 判断是否为空 x is None 2.7
self.assertIsNotNone(x)
# 判断是否为空 x is not None 2.7

4.返回值是否包含某值
self.assertIn(a, b)
# 判断a在b里面 a in b 2.7
self.assertNotIn(a, b)
# 判断a不在b里面 a not in b 2.7

用unittest框架必须遵循框架以下规范

-- 1.每个用例都是独立的,不要第二个用例调用第一个用例
(所以不要问我,第一个用例执行完了,第二个用例怎么接着第一个用例)


-- 2.用例全部是def test开头的,不要乱命名

 

-- 3.用例执行不需要实例化 if __name__ 下写个unittest.main()就行了,不要搞复杂了


-- 4.用例class类里面不要用def __ init__这种东西,这个是类里面的


-- 5.用例是用例,方法是方法,2个不同的概念

 

一、unittest框架
'TestCase':所有测试用例的基本类,给一个测试方法的名字,返回一个测试用例实例
 'TestSuite':组织测试用例的实例,支持测试用例的添加和删除,最终将传testRunner进行测试执行
‘TextTestRunner’:进行测试用例执行的实例,其中Text的意思是以文本形式显示测试结果。
'defaultTestLoader':测试用例加载器,其包括多个加载测试用例的方法。返回一个测试套件


# coding:utf-8
import unittest
# 用例地址
test_dir = "D:\\test\\p2yoyo\\yoyo\\case“
# 测试套件收集用例
# discover方法加载多个用例集合
discover = unittest.defaultTestLoader.discover(start_dir=test_dir,
pattern="test*.py",
top_level_dir=None)
print(discover)

start_dir = 测试文件的路径
Pattern = “test*.py” 匹配规则,*匹配多个字符
Top_level_dir = None
None 表示该文件就是顶层目录(这个参数一般不用动)

1.测试脚手架(test fixture)
-----测试准备前要做的工作和测试执行完后要做的工作.
包括setUp()和tearDown()

2.测试用例(test case)
-----最小的测试单元

3.测试套件(test suite)
-----测试案例的集合class.

4.测试运行器(test runner)
-----测试执行的组件


二次封装
1. 三种等待
1.sleep:进程休眠
--- 傻傻的等,会浪费时间


2. implicitly_wait(30):等待页面完全加载完成
--页面加载完成的标志是左上角转圈结束
--如果页面元素加载完成了,某些js加载失败,页面左上角一直转圈,会耗费时间
-- 全局的,只写一次就行了
-- 缺点:页面有跳转的时候,它不知道去等跳转后的页面


3. WebDriverWait(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None)
--driver:打开浏览器的一个实例参数,这个不用多说
--timeout:超时的总时长30s
--poll_frequency:循环去查询的间隙时间,默认0.5秒
--ignored_exceptions:忽略异常,默认忽略NoSuchElementException


1.10秒内每隔0.5秒循环查找一次元素,找到元素就返回元素本身,找不到抛超时异常

2.30秒内每隔1秒循环查找元素,判断元素有没有消失(不显示),
找不到元素返回Ture,找到抛异常(忽略元素不可见异常TimeoutException)


# coding:utf-8
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import *
driver = webdriver.Firefox()
driver.get(“http://www.baidu.com”)
# 等待时长10秒,默认0.5秒询问一次
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id(“kw”))
element.send_keys(“yoyo”)# 判断页面上元素消失(不显示)
is_disappeared = WebDriverWait(driver, 10, 1,(ElementNotVisibleException)).\
until_not(lambda x: x.find_element_by_id("kw11").is_displayed())
print is_disappeared

3. 定位方法参数化
# 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(By.ID, "kw")
driver.find_element("id", "kw").send_keys("yoyoketang")
driver.find_element('css selector', "#su").click()


1.用By.ID方法和直接输入参数方法实现效果是一样的

2.对应参数参考右侧
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"

对selenium原生的查找元素方法进行二次封装,
30秒内循环查找页面上有没有某个元素

这样封装的好处:
1.可以有效提高查找元素的效率,避免元素没加载完成而抛异常
2.相对于sleep和implicitly_wait更节省时间
3.大大的减少重复代码,使得用例书写更简洁

class Base():
def __init__(self, driver):
self.driver = driver
def findElement (self, *locator, timeout=30):
'''
locator 参数是定位方式,如("id", "kw"),把两个参数合并为一个
*号是把两个参数分开传值
Usage:
locator = ("id", "kw")
driver.find_element(*locator)
'''
element = WebDriverWait(self.driver, timeout, 1).until(lambda x: x.find_element(*locator))
return element

如果我想点一个元素,抽象封装后,只需要知道元素定位方式by, 和对应value值就可以了
By ----通过什么定位方式
Value ----对应的值

Loctor = (By, Value)

class Base():
def __init__(self, driver):
self.driver = driver
self.timeout = 30
self.poll = 0.5
def findElement(self, *loctor):
''' args: loctor 传元祖,如("id","xx") '''
element = WebDriverWait(self.driver, self.timeout, self.poll).until(lambda x: x.find_element(*loctor))
return element
def click(self, *loctor):
ele = self.findElement(*loctor)
ele.click()

sendKeys传loctor和text两个参数

def sendKeys(self, *loctor, text):
ele = self.findElement(*loctor)
ele.send_keys(text)
def clear(self, loctor):
ele = self.findElement(loctor)
ele.clear()

输入内容的时候,可以加个开关,is_clear=True
def sendKeys(self, *loctor, text, is_clear_first=False):
''' is_clear_first默认为False,不清空输入框 '''
ele = self.findElement(*loctor)
if is_clear_first:
ele.clear()
# is_clear_first 为True的时候执行
ele.send_keys(text)

先导入base类
from web.base import Base
from selenium import webdriver
import time driver = webdriver.Firefox()
url = "http://47.98.214.128/zentao/"
driver.get(url)
# 调用封装的类
zentao = Base(driver)
loc1 = ("id", "account")
loc2 = ("name", "password")
loc3 = ("id", "submit")

zentao.sendKeys(loc1, "tests")
zentao.sendKeys(loc2, "admin123..")
zentao.click(loc3)

定位方法也可以通过By的方式去定位

from common.base import Base
from selenium import webdriver
from selenium.webdriver.common.by import By
import time driver = webdriver.Firefox()
url = "http://47.98.214.128/zentao/"
driver.get(url)
# 调用封装的类
zentao = Base(driver)
loc1 = (By.ID, "account")
oc2 = (By.NAME, "password")
loc3 = (By.ID, "submit")

zentao.sendKeys(loc1, "tests")
zentao.sendKeys(loc2, "admin123..")
zentao.click(loc3)

判断元素
元素在页面上有三种状态:

1.不在dom里,也就是不存在这个元素
2.在dom里,隐藏元素,也不是不显示is_dispaly ---False
3.在dom里,显示元素,页面上能看到is_dispaly ---True
# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui 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()
time.sleep(1)
# 判断搜索设置的属性
s = driver.find_element_by_link_text("搜索设置")
print(s.is_displayed()) # 判断元素显示出来了


.is_selected()
判断是否被select, 返回True和False
(注意是针对于select下拉框情况)
# 百度搜索设置
select = driver.find_element_by_xpath(".//*[@id='nr']")
# 判断第三个选项是否被selected
r1 = driver.find_element_by_xpath(".//*[@id='nr']/option[3]")
print(r1.is_selected())
# False
Select(select).select_by_index(2)
# 选第三
select.click()
# 判断第三个选项是否被selected
time.sleep(2)
r2 = driver.find_element_by_xpath(".//*[@id='nr']/option[3]")
print(r2.is_selected())
# True

Radio也就是平常见到的单选
# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("file:///C:/Users/dell/Desktop/checkbox.html")
boy = driver.find_element_by_id("boy")
r1 = boy.is_selected() # False
print(r1)
boy.click()
print(boy.is_selected()) # True


Checkbox就是复选框了
# coding:utf-8
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get("file:///C:/Users/dell/Desktop/checkbox.html")
boxs = driver.find_elements_by_css_selector('[type="checkbox"]')
# 选第一个
b1 = boxs[0].is_selected()
# False
print(b1)
boxs[0].click()
print(boxs[0].is_selected())
# True time.sleep(5)
# 全部选中
for i in boxs:
if i.is_selected():
pass
else:
i.click()

is_enabled 判断元素可以被点击,可以输入文本

# coding:utf-8
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("file:///C:/Users/dell/Desktop/checkbox.html")
boy = driver.find_element_by_id("boy")
print(boy.is_enabled())

异常分析
1. NoSuchElementException
当页面上元素没定位到的时候,会抛出异常:NoSuchElementException

页面没找到这个元素原因:

1.检查你的定位方法是不是写错了,先在浏览器上调试,确定定位方法

2.页面没加载完,元素此时不在当前屏幕,用WebDriverWait()多等几次

 

2. 点击元素几种异常
1.UnexpectedAlertPresentException 页面用alert弹出来了


2. ElementNotVisibleException 元素不可见


3. ElementNotSelectableException 不是select,不能用select方法


1.由于元素定位不到会抛异常,所以根据是否用异常来判断
def is_element_exsit(self, *locator):
'''查找元素的时候,存在返回element,不存在的时候Timeout异常了'''
try:
self.findElement(*locator)
return True
except:
return False

判断元素是否存在2
def findElements(self, *loctor):
''' args: loctor 传元祖,如("id","xx") '''
elements = WebDriverWait(self.driver, self.timeout, self.poll).until(lambda x: x.find_elements(*loctor))
return elements
def is_element_exist1(self):
els = self.findElements()
count = len(els) # 计算元素个数
if count == 0:
return False
elif count == 1:
return True
else:
print("定位到元素个数:%s" %count)
return True

判断方法

1. expected_conditions
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 : 判断某个元素是否被选中了,一般用在select下拉列表
element_selection_state_to_be : 判断某个元素的选中状态是否符合预期
element_located_selection_state_to_be : 跟上面的方法作用一样,只是上面的方法传入定位到的element,而这个方法传入locator
alert_is_present : 判断页面上是否存在alert

 

title_is:判断title完全等于

title_contains :判断title包含


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

 

text_to_be_present_in_element :判断元素文本

text_to_be_present_in_element_value :判断元的value属性

例:
# coding:utf-8
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
url = "https://www.baidu.com"
driver.get(url)
# 判断文本text
locator = ("name", "tj_trnuomi")
text = u"糯米"
result = EC.text_to_be_present_in_element(locator, text)(driver)
print result
# 判断元素的value属性
locator2 = ("id", "su")
text2 = u"百度一下"
result2 = EC.text_to_be_present_in_element_value(locator2, text2)(driver)
print result2

alert_is_present:当前是否有alert弹出框

例:
# 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 未弹出!"

10秒内循环判断元素中是否包含文本,循环判断成功返回True,失败会抛超时异常

Until后面可以接16种判断方法,非常灵活,实用性很强
def text_in_element(self, locator, text, timeout=10):
try:
WebDriverWait(self.driver, timeout, 1).until(EC.text_to_be_present_in_element(locator, text))
return True
except TimeoutException:
return False

判断是否有alert

没有返回False


有alert返回alert对象
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 move_to_element(self, locator):
'''鼠标悬停操作'''
element = self.find_element(locator)
ActionChains(self.driver).move_to_element(element).perform()

 


PO项目架构

分层设计:
1.base:
公共方法,二次封装,或其它模块如excel模块封装

2.page:
页面元素抓取,页面上公共操作,如登录,退出
1.写页面page类,继承base类
2.元素抓取:xxx_loc
3.操作方法封装

3. case:
写用例
1.先把功能用例的详细步骤写清楚!!!
(有些不会写自动化用例的是因为功能步骤都没想清楚)
2.把对应操作翻译成脚本
3.判断方法就用EC模块的17个判断方法
4.断言:实际结果,与期望结果对比

 

分层设计好处:
1.底层封装,避免重复代码,减少代码量
2.页面层元素抓取,单个页面写一个脚本(模块),修改元素定位方便,便于元素定位的维护工作
3.用例层只做用例设计的事情,调用page页面模块的方法,简单粗暴
4.可以便于多人同时并行开发工作,也可以调用别人写的page页面模块

封装鼠标事件

def move_to_element(self, *locator):
''' 鼠标悬停操作
Usage:
locator = ("id","xxx")
driver.move_to_element(locator) '''
try:
element = self.find_element(locator)
except TimeoutException:
print("element not found:%s" % locator)
else:
ActionChains(self.driver).move_to_element(element).perform()

.封装select方法
def select_by_index(self, *locator, index=0):
'''通过索引,index是索引第几个,从0开始,默认选第一个'''
element = self.findElement(*locator)
Select(element).select_by_index(index)
element.click()

def select_by_value(self, *locator, value):
'''通过value属性'''
element = self.findElement(locator)
Select(element).select_by_value(value)

def select_by_text(self, locator, text):
'''通过文本值定位'''
element = self.findElement(locator)
Select(element).select_by_visible_text(text)

page object model : 顾名思义页面对象模式
- 把每个页面(page)当成一个对象(object)
- 封装页面上会用到的操作方法,比如:输入账号,输入密码,点登陆
(一些行为事件,后面用例会用到的)
- 写用例的时候,操作哪个页面,就去调用哪个页面的方法

 

 

编程核心思想,6个字:高内聚,低耦合

 

page层代码

# coding:utf-8
from common.base import Base
host = "http://127.0.0.1:81"
login_url = host+"/zentao/user-login.html"
class Login(Base): # 继承 # 定位登陆
loc1 = ("id", "account")
loc2 = ("css selector", "[name='password']")
loc3 = ("xpath", "//*[@id='submit']")
loc_user = ("css selector", "#userMenu>a")
def login(self, user="admin", psw="123456"):
self.driver.get(login_url)
self.sendKeys(self.loc1, user)
self.sendKeys(self.loc2, psw)
self.click(self.loc3)
def get_login_user(self):
user = self.get_text(self.loc_user)
return user

 

 

case代码例:

登录用例代码
# coding:utf-8
from selenium import webdriver
import time, unittest
from pages.login_page import Login, login_url
class LoginTest(unittest.TestCase):
'''登录类的案例'''
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Firefox()
cls.a = Login(cls.driver)
def setUp(self):
self.driver.get(login_url)
self.a.alert_accept()
self.driver.delete_all_cookies()# 退出登录
self.driver.refresh()
def test_01(self):
'''登录成功的案例'''
time.sleep(2)
self.a.login("admin", "123456")
t = self.a.get_login_user() # 判断是否登陆成功
print("获取的结果:%s"%t)
self.assertTrue(t == "admin")
def test_02(self):
'''登录失败的案例'''
time.sleep(2)
self.a.login("admin1", "") # 错误账号和密码
# 判断是否登陆成功
t = self.a.get_login_user()
print("登录失败,获取结果:%s"%t)
self.assertTrue(t == "")
@classmethod
def tearDownClass(cls):
cls.driver.quit() # 编辑器问题
if __name__ == "__main__":
unittest.main()


执行用例前先登录,可以把login写到setUpClass

import unittest
from selenium import webdriver
from pages.add_bug_page import AddBug
from pages.login_page import Login
import time class Test_Add_Bug(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Firefox()
cls.bug = AddBug(cls.driver)
cls.a = Login(cls.driver)
cls.a.login() # 用例前先登录
def test_add_bug(self):
timestr = time.strftime("%Y_%m_%d_%H_%M_%S")
title = "测试提交BUG"+timestr
self.bug.add_bug(title)
result = self.bug.is_add_bug_sucess(title)
print(result)
self.assertTrue(result)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
if __name__ == "__main__":
unittest.main()

 

参数化,数据分离


def login_case(self, username, psw, expect=True):
'''参数化登录用例的方法'''
self.driver.input_username(username)
self.driver.input_password(psw)
self.driver.click_submit()
# 判断页面账号名称
user_loc = ("id", "lnk_current_user")
result = self.driver.text_in_element(user_loc, username)
expect_result = expect
self.assertEqual(result, expect_result)
def test_login_01(self):
'''测试数据:正确账号,正确密码'''
self.login_case(u"上海-悠悠", u“11111", True)
def test_login_02(self): '''测试数据:正确账号,错误密码'''
self.login_case(u"上海-悠悠", u"xxx", False)

数据用for循环弊端
1.多组数据应该生成多个用例结果,而for循环只生成一个测试结果
2.一旦其中一个抛异常,后面的用例都无法执行了
3.其中一个用例失败,最终测试报告整个登录模块都是Fail(统计不准

.ddt数据驱动
# coding:utf-8
import ddt
import unittest
d12 =[{"username": u"上海-悠悠", "psw": u"zhangxxx", "expect": True}, {"username": "123", "psw": "xxx", "expect": False}]
@ddt.ddt
class Test(unittest.TestCase):
@ddt.data({"username": u"11", "psw": u"22", "expect": True},{"username": "1", "psw": "2", "expect": False})
def test_01(self, testdata):
print testdata
@ddt.data(*d12)
def test_02(self, testdata):
print testdata
if __name__ == "__main__":
unittest.main()


有两个地方需要加修饰符
1.测试类前面:@ddt.ddt

2.测试用例前:@ddt.data

(注:网上有部分教程会在 @ddt.data下加@ddt.unpack(元组数据类型用到)


Excel数据读取

# coding:utf-8
import xlrd
# 打开exlce表格
data = xlrd.open_workbook('testdata.xlsx')
# table = data.sheets()[0]
# 通过索引顺序获取
# table = data.sheet_by_index(0)
# 通过索引顺序获取
table = data.sheet_by_name(u'Sheet1')
# 通过名称获取
nrows = table.nrows
# 获取总行数
ncols = table.ncols
# 获取总列数
print table.row_values(0)
# 获取整行值
print table.col_values(0)
# 获取整列值

封装的读取excel文件,以多个字典的list格式输出

# coding:utf-8
import xlrd
class ExcelUtil():
def __init__(self, excelPath, sheetName="Sheet1"):
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__":
filepath = "D:\\test\\web-project\\5ke\\testdata.xlsx"
sheetName = "Sheet1"
data = ExcelUtil(filepath, sheetName)
print data.dict_data()


# coding:utf-8
from selenium import webdriver
import unittest
from blog.blog_login_page import LoginPage, login_url
from common.yoyo_excel import ExcelUtil
import ddt
readexcel = ExcelUtil("testdata.xlsx")
print readexcel.dict_data()
d = readexcel.dict_data()
@ddt.ddt
class Login_test(unittest.TestCase):
u'''登录页面的case'''
user_loc = ("id", "lnk_current_user")
def setUp(self):
self.driver = LoginPage()
self.driver.open(login_url)
def login_case(self, username, psw, expect=True):
'''参数化登录用例的方法'''
self.driver.input_username(username)
self.driver.input_password(psw)
self.driver.click_submit()
# 判断页面账号名称
user_loc = ("id", "lnk_current_user")
result = self.driver.text_in_element(user_loc, username)
if expect == "True": expect_result = True
else: expect_result = False
self.assertEqual(result, expect_result)
@ddt.data(*d)
def test_login_01(self, data):
'''测试数据:'''
self.login_case(data["username"], data["psw"], data["expect"])
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main()

posted @ 2020-09-01 17:35  自学随笔  阅读(151)  评论(0)    收藏  举报