[智能等待]
# 用于实现智能等待页面元素的出现
# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import difflib
class Smart_Waiting:
def __init__(self, driver):
self.locationTypeDict = {
"xpath": By.XPATH,
"id": By.ID,
"name": By.NAME,
"css_selector": By.CSS_SELECTOR,
"class_name": By.CLASS_NAME,
"tag_name": By.TAG_NAME,
"link_text": By.LINK_TEXT,
"partial_link_text": By.PARTIAL_LINK_TEXT
}
self.driver = driver
self.wait = WebDriverWait(self.driver, 30)
def presence_of_element_located(self, location_type, locator_expression, *args):
"""
显示等待页面元素出现在DOM中,但并不一定可见,存在则返回该页面元素对象
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
if location_type.lower() in self.locationTypeDict:
# if self.locationTypeDict.has_key(locatorMethod.lower()):
self.wait.until(
EC.presence_of_element_located((
self.locationTypeDict[location_type.lower()], locator_expression)))
else:
raise TypeError(u"未找到定位方式,请确认定位方法是否正确")
except Exception as e:
raise e
def frame_to_be_available_and_switch_to_it(self, location_type, locator_expression, *args):
"""
判断该frame是否可以switch进去,如果可以的话,返回True并且switch进去,否则返回False
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
self.wait.until(
EC.frame_to_be_available_and_switch_to_it((
self.locationTypeDict[location_type.lower()], locator_expression)))
except Exception as e:
# 抛出异常信息给上层调用者
raise e
def visibility_element_located(self, location_type, locator_expression, *args):
"""
判断某个元素是否被添加到了dom里并且可见,可见代表元素可显示且宽和高都大于0
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element = self.wait.until(
EC.visibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))
return element
except Exception as e:
raise e
def wait_title_is(self, string, location_type, locator_expression, *args):
"""
当页面标题是string时对页面元素进行定位,并返回页面元素对象
:param string:
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
if self.driver.title == string:
element = self.wait.until(
EC.visibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))
return element
except Exception as e:
raise e
def wait_title_contain(self, string, location_type, locator_expression, *args):
"""
当页面标题与string进行比较相似度大于0.8时对页面元素进行定位,并返回页面元素对象
:param string:
:param location_type:
:param locator_expression:
:param args:
:return:
"""
page_tile = driver.title
similarity = difflib.SequenceMatcher(None, page_tile, string).quick_ratio()
try:
if similarity >= 0.8:
element = self.wait.until(
EC.visibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))
return element
except Exception as e:
raise e
def visibility_of_element(self, location_type, locator_expression, *args):
"""
判断元素是否可见,如果可见就返回这个元素
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element = self.wait.until(
EC.visibility_of((self.locationTypeDict[location_type.lower()], locator_expression)))
return element
except Exception as e:
raise e
def presence_of_all_elements_located(self, location_type, locator_expression, *args):
"""
判断是否至少有1个元素存在于dom树中,如果定位到就返回列表
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element_list = self.wait.until(
EC.presence_of_all_elements_located((self.locationTypeDict[location_type.lower()], locator_expression)))
return element_list
except Exception as e:
raise e
def visibility_of_any_elements_located(self, location_type, locator_expression, *args):
"""
判断是否至少有一个元素在页面中可见,如果定位到就返回列表
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element_list = self.wait.until(
EC.visibility_of_any_elements_located((self.locationTypeDict[location_type.lower()], locator_expression)))
return element_list
except Exception as e:
raise e
def text_to_be_present_in_element(self, string, location_type, locator_expression, *args):
"""
判断指定的元素中是否包含了预期的字符串,返回布尔值
:param string:
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
self.wait.until(
EC.text_to_be_present_in_element((self.locationTypeDict[location_type.lower()], locator_expression), string))
except Exception as e:
raise e
def text_to_be_present_in_element_value(self, string, location_type, locator_expression, *args):
"""
判断指定元素的属性值中是否包含了预期的字符串,返回布尔值
:param string:
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
self.wait.until(
EC.text_to_be_present_in_element_value((self.locationTypeDict[location_type.lower()], locator_expression), string))
except Exception as e:
raise e
def invisibility_of_element_located(self, location_type, locator_expression, *args):
"""
判断某个元素在是否存在于dom或不可见,如果可见返回False,不可见返回这个元素
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element = self.wait.until(
EC.invisibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))
return element
except Exception as e:
raise e
def element_to_be_clickable(self, location_type, locator_expression, *args):
"""
判断某个元素中是否可见并且是enable的,代表可点击
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element = self.wait.until(
EC.element_to_be_clickable((self.locationTypeDict[location_type.lower()], locator_expression)))
return element
except Exception as e:
raise e
def element_to_be_selected(self, location_type, locator_expression, *args):
"""
判断某个元素是否被选中了,一般用在下拉列表
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element = self.wait.until(
EC.element_to_be_selected((self.locationTypeDict[location_type.lower()], locator_expression)))
return element
except Exception as e:
raise e
def element_selection_state_to_be(self, location_type, locator_expression, *args):
"""
判断某个元素的选中状态是否符合预期
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element = self.wait.until(
EC.element_selection_state_to_be((self.locationTypeDict[location_type.lower()], locator_expression), True))
return element
except Exception as e:
raise e
def element_located_selection_state_to_be(self, location_type, locator_expression, *args):
"""
判断某个元素的定位状态是否符合预期
:param location_type:
:param locator_expression:
:param args:
:return:
"""
try:
element = self.wait.until(
EC.element_located_selection_state_to_be((self.locationTypeDict[location_type.lower()], locator_expression), True))
return element
except Exception as e:
raise e
def alert_is_present(self):
"""
判断页面上是否存在alert,如果有就切换到alert并返回alert的内容
"""
try:
alert_instance = self.wait.until(EC.alert_is_present())
print(alert_instance.text)
alert_instance.accept()
except Exception as e:
raise e
if __name__ == "__main__":
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("http://mail.126.com")
# 实例化WaitUtil类
waitUtil = Smart_Waiting(driver)
# 判断如果id = x-URS-iframe的iframe存在则切换进去
wf = waitUtil.frame_to_be_available_and_switch_to_it("id", "x-URS-iframe")
# 等待页面元素xpath = //input[@name='email']的出现
wv = waitUtil.visibility_element_located("xpath", "//input[@name='email']")
# 显示等待页面元素出现在DOM中,但并不一定可见,存在则返回该页面元素对象
wp = waitUtil.presence_of_element_located("xpath", "//input[@name='email']")
driver.quit()
[Win32模拟键盘]
在实际的自动化代码调试过程中,往往Selenium提供的方法不能满足于自动化任务,例如定位某个按钮要完成点击操作,定位正确但就是无法完成点击,此时如果掌握模拟键盘的方法便可以为我们的自动化提供很大的帮助。
安装Pywin32
要模拟键盘操作,需要先为Python安装相关模块,启动命令行,然后在命令行执行命令“pip install –U pywin32”,执行结果如下
C:\Users\davieyang>pip install pywin32
Collecting pywin32
Using cached https://files.pythonhosted.org/packages/a3/8a/eada1e7990202cd27e58eca2a278c344fef190759bbdc8f8f0eb6abeca9c/pywin32-224-cp37-cp37m-win_amd64.whl
Installing collected packages: pywin32
Successfully installed pywin32-224
看到Successfully installed pywin32-xxx则表示安装成功。
无法引入模块
如果使用pip安装后,出现无法将win32api
和win32con
引入项目的情况,还可以直接下载安装文件,下载地址为
pywin32,必须指出的是在这个地址只能找到Build 222
之前的版本,新的版本移步到了pywin32新,文件是.exe的,直接双击安装即可
方法封装
在PO项目中的Util路径下新建一个Python文件并命名为Keyboard_Simulation,然后在文件中写入如下代码。
# 用于实现模拟键盘单个或多个组合键操作
# encoding = utf-8
import win32api
import win32con
class Simulate_Keyboard:
"""
定义字典,字典内容为键盘上的按键与VkCode的键值对
"""
VK_CODE = {
'backspace': 0x08,
'tab': 0x09,
'clear': 0x0C,
'enter': 0x0D,
'shift': 0x10,
'ctrl': 0x11,
'alt': 0x12,
'pause': 0x13,
'caps_lock': 0x14,
'esc': 0x1B,
'spacebar': 0x20,
'page_up': 0x21,
'page_down': 0x22,
'end': 0x23,
'home': 0x24,
'left_arrow': 0x25,
'up_arrow': 0x26,
'right_arrow': 0x27,
'down_arrow': 0x28,
'select': 0x29,
'print': 0x2A,
'execute': 0x2B,
'print_screen': 0x2C,
'ins': 0x2D,
'del': 0x2E,
'help': 0x2F,
'0': 0x30,
'1': 0x31,
'2': 0x32,
'3': 0x33,
'4': 0x34,
'5': 0x35,
'6': 0x36,
'7': 0x37,
'8': 0x38,
'9': 0x39,
'a': 0x41,
'b': 0x42,
'c': 0x43,
'd': 0x44,
'e': 0x45,
'f': 0x46,
'g': 0x47,
'h': 0x48,
'i': 0x49,
'j': 0x4A,
'k': 0x4B,
'l': 0x4C,
'm': 0x4D,
'n': 0x4E,
'o': 0x4F,
'p': 0x50,
'q': 0x51,
'r': 0x52,
's': 0x53,
't': 0x54,
'u': 0x55,
'v': 0x56,
'w': 0x57,
'x': 0x58,
'y': 0x59,
'z': 0x5A,
'numpad_0': 0x60,
'numpad_1': 0x61,
'numpad_2': 0x62,
'numpad_3': 0x63,
'numpad_4': 0x64,
'numpad_5': 0x65,
'numpad_6': 0x66,
'numpad_7': 0x67,
'numpad_8': 0x68,
'numpad_9': 0x69,
'multiply_key': 0x6A,
'add_key': 0x6B,
'separator_key': 0x6C,
'subtract_key': 0x6D,
'decimal_key': 0x6E,
'divide_key': 0x6F,
'F1': 0x70,
'F2': 0x71,
'F3': 0x72,
'F4': 0x73,
'F5': 0x74,
'F6': 0x75,
'F7': 0x76,
'F8': 0x77,
'F9': 0x78,
'F10': 0x79,
'F11': 0x7A,
'F12': 0x7B,
'num_lock': 0x90,
'scroll_lock': 0x91,
'left_shift': 0xA0,
'right_shift ': 0xA1,
'left_control': 0xA2,
'right_control': 0xA3,
'left_menu': 0xA4,
'right_menu': 0xA5,
'browser_back': 0xA6,
'browser_forward': 0xA7,
'browser_refresh': 0xA8,
'browser_stop': 0xA9,
'browser_search': 0xAA,
'browser_favorites': 0xAB,
'browser_start_and_home': 0xAC,
'volume_mute': 0xAD,
'volume_Down': 0xAE,
'volume_up': 0xAF,
'next_track': 0xB0,
'previous_track': 0xB1,
'stop_media': 0xB2,
'play/pause_media': 0xB3,
'start_mail': 0xB4,
'select_media': 0xB5,
'start_application_1': 0xB6,
'start_application_2': 0xB7,
'attn_key': 0xF6,
'crsel_key': 0xF7,
'exsel_key': 0xF8,
'play_key': 0xFA,
'zoom_key': 0xFB,
'clear_key': 0xFE,
'+': 0xBB,
',': 0xBC,
'-': 0xBD,
'.': 0xBE,
'/': 0xBF,
'`': 0xC0,
';': 0xBA,
'[': 0xDB,
'\\': 0xDC,
']': 0xDD,
"'": 0xDE,
}
@staticmethod
def press_key (keyName):
# 按下按键
win32api.keybd_event(Simulate_Keyboard.VK_CODE[keyName], 0, 0, 0)
@staticmethod
def release_key (keyName):
# 释放按键
win32api.keybd_event(Simulate_Keyboard
.VK_CODE[keyName], 0, win32con.KEYEVENTF_KEYUP, 0)
@staticmethod
def click_onekey(key):
# 模拟单个按键
Simulate_Keyboard.press_key(key)
Simulate_Keyboard.release_key(key)
@staticmethod
def click_twokey(first_key, second_key):
# 模拟两个组合键
Simulate_Keyboard.press_key(first_key)
Simulate_Keyboard.press_key(second_key)
Simulate_Keyboard.release_key(second_key)
Simulate_Keyboard.release_key(first_key)
方法调用
当需要调用其中的方法的时候,将其引入到测试代码中,调用类中的方法时,将想要的按键传给相应方法即可,在test_advanced_application文件中新增如下测试方法,验证封装的方法是否可用,代码如下所示。
from Util.Keyboard_Simulation import Simulate_Keyboard
def test_simulate_keyboard(self):
Simulate_Keyboard.oneKey('enter')
Simulate_Keyboard.oneKey('ctrl', 'v')
Simulate_Keyboard.oneKey('enter')
[PyUserInput模拟键盘]
PyUserInput安装
在Python3.7版本下安装PyUserInput需要先安装PyHook,用浏览器打开链接PyHook,这个页面里能找到很多Python的第三方扩展,读者朋友不妨保存起来。我们找到pyHook兼容Python3.7版本的链接,直接点击链接即可下载
然后启动命令行并将命令行引导到下的文件所在路径下,执行命令pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl,如下执行过程则表示安装成功。
C:\Users\Administrator\Downloads>pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl
Processing c:\users\administrator\downloads\pyhook-1.5.1-cp37-cp37m-win_amd64.whl
Installing collected packages: pyHook
Successfully installed pyHook-1.5.1
安装完pyHook后,便可以安装PyUserInput模块,继续在命令行执行pip install PyUserInput,如下所示则表示PyUserInput安装成功。
C:\Users\Administrator\Downloads>pip install PyUserInput
Collecting PyUserInput
Usingcachedhttps://files.pythonhosted.org/packages/d0/09/17fe0b16c7eeb52d6c14e904596ddde82503aeee268330120b595bf22d7b/PyUserInput-0.1.11.tar.gz
Requirement already satisfied: pyHook in c:\python37\lib\site-packages (from PyUserInput) (1.5.1)
Requirement already satisfied: pywin32 in c:\python37\lib\site-packages (from PyUserInput) (223)
Installing collected packages: PyUserInput
Running setup.py install for PyUserInput ... done
Successfully installed PyUserInput-0.1.11
模拟键盘
启动命令行工具,并进入到Python命令行,将pykeyboard类引入到环境中,然后调用PyKeyboard()函数,它返回键盘对象,将其赋值给pk
>>> import pykeyboard
>>> pk = pykeyboard.PyKeyboard()
当我们有了鼠标和键盘对象后,就可以模拟一些实际的鼠标键盘操作了,在键盘上敲字母”D”
pk.press_key('D')
pk.release_key('D')
我们看到敲击一个字母需要使用两个方法一个是press_key()另一个是release_key(),正如我们键盘上的操作按下按钮和松开按钮是一样,我们还可以使用一个方法tap_key()代替press_key()和release_key()
pk.tap_key('D')
同时tap_key()还支持在指定间隔时间情况的多次敲击,如下命令所示,其中10表示敲击键盘的次数,而1表示敲击间隔
pk.tap_key('D', 10, 1)
并且还可以使用方法type_string()模拟敲击整个字符串
pk.type_string('__davieyang__')
接下来看一下如何完成组合键和功能键的模拟,如下命令行所示,敲击Ctrl+A。
pk.press_key(pk.control_key)
pk.tap_key(‘a’)
pk.release_key(pk.control_key)
敲击功能键F5
pk.tap_key(pk.function_keys[5])
敲击小键盘上的Home键
pk.tap_key(pk.numpad_keys['Home'])
敲击小键盘上的3,敲8次
pk.tap_key(pk.numpad_keys[3], n=8)
我们还可以使用press_keys()方法,然后传给他一个列表,完成组合键的敲击,如下代码所示,敲击键盘的Ctrl+A
pk.press_keys([pk.control_key,'a'])
[PyUserInput模拟鼠标]
PyUserInput安装
在Python3.7版本下安装PyUserInput需要先安装PyHook,用浏览器打开链接:PyHook,这个页面里能找到很多Python的第三方扩展,读者朋友不妨保存起来。我们找到pyHook兼容Python3.7版本的链接,直接点击链接即可下载
然后启动命令行并将命令行引导到下的文件所在路径下,执行命令pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl,如下执行过程则表示安装成功。
C:\Users\Administrator\Downloads>pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl
Processing c:\users\administrator\downloads\pyhook-1.5.1-cp37-cp37m-win_amd64.whl
Installing collected packages: pyHook
Successfully installed pyHook-1.5.1
安装完pyHook后,便可以安装PyUserInput模块,继续在命令行执行pip install PyUserInput,如下所示则表示PyUserInput安装成功。
C:\Users\Administrator\Downloads>pip install PyUserInput
Collecting PyUserInput
Usingcachedhttps://files.pythonhosted.org/packages/d0/09/17fe0b16c7eeb52d6c14e904596ddde82503aeee268330120b595bf22d7b/PyUserInput-0.1.11.tar.gz
Requirement already satisfied: pyHook in c:\python37\lib\site-packages (from PyUserInput) (1.5.1)
Requirement already satisfied: pywin32 in c:\python37\lib\site-packages (from PyUserInput) (223)
Installing collected packages: PyUserInput
Running setup.py install for PyUserInput ... done
Successfully installed PyUserInput-0.1.11
模拟鼠标
启动命令行工具,并进入到Python命令行,将pymouse类引入到环境中,然后调用PyMouse()函数,它返回鼠标对象,我们将其赋值给pm
>>> import pymouse
>>> pm = pymouse.PyMouse()
获取鼠标指针当前所在位置的坐标
>>> mouse_position = pm.position()
>>> print(mouse_position)
(849, 589)
获取了当前位置,模拟鼠标从当前所在位置按住鼠标左键滑动到坐标(300,400)
>>> pm.drag(300,400)
模拟鼠标移动到坐标(300,500)
>>> pm.move(300,500)
模拟鼠标在坐标(300,500)点住左键不放,其中1表示左键,2表示右键,3表中中间键
>>> pm.press(300,500,1)
模拟了按住不放,就要有释放按键的方法
>>> pm.release(300,500,1)
模拟鼠标滚轮滚动,如下命令行所示,其中参数vertical为负数表示向下滚动反之正数表示向上滚动,而horizontal为负数表示向左滚动反之为向右。
pm.scroll(vertical = -30, horizontal = -40)
模拟鼠标在坐标为(300,500)点击鼠标右键5次,如下命令行所示,2表示鼠标右键,左键用1表示中间键用3表示,命令行中的5表示点击次数,默认为1。
>>> pm.click(300,500,2,5)
获取屏幕尺寸
>>> screen_x, screen_y = pm.screen_size()
>>> print(screen_x, screen_y)
3360 1080
模拟鼠标在坐标(300,500)点击左右键或者点击滚轮,如下两条命令等效
>>> pm.click(300,500, 1|2)
>>> pm.click(300,500, 3)
[ActionChains模拟鼠标]
在实际的测试中鼠标的操作也是频繁发生的,与封装控制浏览器相关方法是相同的思想,本节笔者将详细介绍如何封装模拟鼠标操作的方法以及如何调用我们封装好的方法。
方法封装
在实际的自动化测试中往往需要模拟一些鼠标的操作来辅助我们来完成页面上一些特殊的操作,例如有些需要鼠标拖拽页面元素,挪动页面元素,鼠标悬停在页面元素上等等, 因此我们封装一些工具类以便于我们在写测试代码中直接调用。
# encoding = utf-8
from selenium.webdriver.common.action_chains import ActionChains
class Simulate_Mouse:
def __init__(self, driver):
self.driver = driver
self.actions = ActionChains(self.driver)
# 单击鼠标左键
def left_click(self, element):
self.actions.click(element).perform()
# 双击鼠标左键
def double_left_click(self, element):
self.actions.double_click(element).perform()
# 单击鼠标右键
def right_click(self, element):
self.actions.context_click(element).perform()
# 移动鼠标到element
def move_mouse(self, element):
self.actions.move_to_element(element).perform()
# 从source移动鼠标到target
def move_mouse_source_target(self, source, target):
self.actions.drag_and_drop(source, target).perform()
# 从source移动鼠标到target
def move_source_target(self, source, target):
self.actions.click_and_hold(source).release(target).perform()
# 拖拽元素到坐标xy
def drag_element(self, element, x, y):
self.actions.click_and_hold(element).move_by_offset(x, y).release().perform()
# 点击并且不释放
def click_hold(self, element):
self.actions.click_and_hold(element)
方法调用
当需要调用其中的方法的时候,将其引入到测试代码中,调用类中的方法时,根据方法所需参数传参即可。
在test_advanced_application文件中新增如下测试方法,验证封装的方法是否可用,代码如下所示。
from Util.Mouse_Simulation import Simulate_Mouse # 引入我们封装好的工具类
import time # 引入time模块用于等待
def test_simulate_mouse(self): # 定义测试方法
chr_driver = webdriver.Chrome() # 启动谷歌浏览器
chr_driver.get(self.url) # 打开url
element = chr_driver.find_element_by_link_text("设置") # 获取页面元素
Simulate_Mouse(chr_driver).move_mouse(element) # 鼠标悬停动作
time.sleep(5) # 强制等待5秒
[兼容性测试]
在实际的自动化测试过程中,有些产品必须进行兼容性测试,那就意味着在不同的环境中执行相同的测试用例,而这应该是发挥自动化测试优势的非常重要的战地。
自动化在编写兼容性测试用例的时候,稍微有所不同,需要我们定义好一个测试方法,然后执行不同环境时调用该方法,从而实现在不同的环境中执行相同的测试,如下代码所示。
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
import unittest
class Compatibility_Test(unittest.TestCase):
def setUp(self):
self.base_url = "https://admin.leadscloud.com/Front-breeze/#/home"
def login_leadscloud(self, driver):
'''
定义测试方法
:param driver:
:return:
'''
driver.get(self.base_url)
sleep(5)
driver.find_element_by_xpath("//*[@id='main']/div/div[1]/div/div[2]/form/div[1]/div/div/input").send_keys('xxxxxx')
driver.find_element_by_xpath("//*[@id='main']/div/div[1]/div/div[2]/form/div[2]/div/div/input").send_keys('xxxxxx')
driver.find_element_by_xpath("//*[@id='main']/div/div[1]/div/div[2]/form/div[3]/div/button").click()
driver.quit()
def test_chrome(self):
'''
启动chrome浏览器执行测试用例
:return:
'''
chrome_driver = webdriver.Chrome()
self.login_leadscloud(chrome_driver)
def test_firefox(self):
'''
启动firefox执行测试用例
:return:
'''
firefox_driver = webdriver.Firefox()
self.login_leadscloud(firefox_driver)
def test_ie(self):
'''
启动IE执行测试用例
:return:
'''
ie_driver = webdriver.Ie()
self.login_leadscloud(ie_driver)
if __name__ == '__main__':
unittest.main(verbosity=2)
[杀浏览器进程]
Webdriver虽然有quit()方法和close()可以关闭浏览器,但有些时候浏览器进程并不能彻底关闭,我们需要掌握杀进程的方法,代码实例如下:
# encoding = utf-8
from selenium import webdriver
import unittest
import os
from time import sleep
class Test_Kill_Browser(unittest.TestCase):
def test_kill_browser_process(self):
# 启动浏览器
chrome_driver = webdriver.Chrome()
sleep(5)
firefox_driver = webdriver.Firefox()
sleep(5)
ie_driver = webdriver.Ie()
sleep(5)
# 杀chrome浏览器进程
code = os.system("taskkill /F /iM chrome.exe")
if code ==0:
print(u"Kill Firefox Successfully")
else:
print(u"Kill Firefox Failed")
# 杀firefox浏览器进程
code = os.system("taskkill /F /iM firefox.exe")
if code ==0:
print(u"Kill Firefox Successfully")
else:
print(u"Kill Firefox Failed")
# 杀ie浏览器进程
code = os.system("taskkill /F /iM ie.exe")
if code ==0:
print(u"Kill Firefox Successfully")
else:
print(u"Kill Firefox Failed")
if __name__ == '__main__':
unittest.main(verbosity=2)
执行结果
在执行结果中出现乱码,是因为我们的代码文件的File Encodings设置是默认UTF-8的,想解决这乱码问题,我们可以修改Pycharm的设置,找到Settings,或者直接使用快捷键Ctrl+Alt+S打开Settings
然后在检索栏中检索encoding,便可以找到File Encodings的选项
将Global Encoding修改为GBK后,在此执行代码,执行结果如图
[浏览器静默模式启动]
在实际的自动化测试中,为了不让浏览器频繁起动关闭,可以采用静默模式执行,代码示例如下。
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
# 创建chrome的Option对象
chrome_options = Options()
# 添加静默参数
chrome_options.add_argument('--headless')
for i in range(100000):
# 静默模式启动浏览器
chrome_driver = webdriver.Chrome(options=chrome_options)
# 打开页面
chrome_driver.get("http://www.yialife.co.za/contact.html")
chrome_driver.maximize_window()
chrome_driver.find_element_by_class_name("xhl-button-text").click()
# 获取当前时间
current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
chrome_driver.find_element_by_id("messageText").send_keys("audio notification testing at " + current_time)
time.sleep(1)
chrome_driver.find_element_by_id("sendBtn").click()
print(current_time)
time.sleep(5)
# 删掉所有cookie
chrome_driver.delete_all_cookies()
chrome_driver.quit()
[处理Cookies]
在某些场景下是需要处理浏览器cookie的,比如经常能看到有些网站都提供了页面内部的咨询窗口,点开窗口可以和客服对话,然而当第一次跟客服对话的时候,客服方显示对话名称假设为访客A,当半小时后再次打开该网站继续聊天的话客服方依然还会显示是访客A,但如果清理了cookie,在打开网站去和客服对话,客服方显示我们可能就是一个新的访客。
笔者只是举一个互联网产品系统常见的一个场景,而这种场景如果需要自动化测试的话,无疑我们要掌握处理cookie的方法,本节笔者将介绍封装操作cookie的方法,以及如何使用封装好的方法来处理cookie。
方法封装
def delete_current_cookie(self): # 封装删除当前所有cookie的方法
"""
删除所有cookie
:return:
"""
self.driver.delete_all_cookies()
def get_current_cookies(self): # 封装获取当前cookies的方法
"""
获取当前cookies
:return:
"""
current_cookie = self.driver.get_cookies()
return current_cookie
def get_current_cookie_value(self, key): # 获取当前name为key的cookie信息
"""
获取key为key的cookie信息
:param key:
:return:
"""
key_cookie = self.driver.get_cookie(key)
return key_cookie
def add_key_value_to_cookie(self, cookie_dict): # 添加cookie,参数为字典
"""
添加cookie
:return:
"""
self.driver.add_cookie(cookie_dict)
方法调用
然后看如何调用它,在test_advanced_application文件中新增如下测试方法,验证封装的方法是否可用,代码如下所示。
def test_cookies(self): # 定义新的测试方法
cookie_dict = {'name': 'name_yang', 'value': 'cookie_yang'} # 定义字典
chrome_driver = webdriver.Chrome() # 启动浏览器
chrome_driver.get("https://www.baidu.com")
time.sleep(10)
# 获取当前所有cookie
current_cookie = Browser_Controller(chrome_driver).get_current_cookies()
# 打印当前cookie
print(current_cookie)
# 将之前定义的字典添加到cookie中去
Browser_Controller(chrome_driver).add_key_value_to_cookie(cookie_dict)
# 获取name为name_yang的cookie信息
key_cookie = Browser_Controller(chrome_driver).get_current_cookie_value('name_yang')
# 打印cookie信息
print(key_cookie)
# 删除当前cookie
Browser_Controller(chrome_driver).delete_current_cookie()
# 删除后再次获取cookie
current_cookie_2 = Browser_Controller(chrome_driver).get_current_cookies()
# 将当前cookie转换成字符串打印到控制台
print(str(current_cookie_2) + "只有这几个字没有cookie了")
[处理iframe]
如果页面存在iframe,那么我们是不能直接定位到iframe节点下的页面元素的,需要先切换到iframe里边去,然后再对iframe中的页面元素进行定位,而如果切换进iframe中后也是定位不到iframe外的元素的,还需要切换出去才能进行iframe外的元素的定位。
在经历过上前边多种操作的封装后,iframe的封装就简单了很多,接下来笔者将介绍封装后的方法以及如何调用。
方法封装
def switch_to_iframe(self, frame):
"""
用于切换进页面的iframe控件
:param iframe:
:return:
"""
self.driver.switch_to.frame(frame)
def switch_to_default_content(self):
"""
从iframe中切换回主页页面
:return:
"""
self.driver.switch_to.default_content()
方法调用
def test_switch_iframe(self): # 定义测试方法
chrome_driver = webdriver.Chrome()
chrome_driver.get("https://mail.163.com")
time.sleep(10)
frame = chrome_driver.find_element_by_xpath("//*[@id='loginDiv']/iframe")
# 调用封装好的方法切换进iframe控件
Browser_Controller(chrome_driver).switch_to_iframe(frame)
time.sleep(5)
chrome_driver.find_element_by_name("email").send_keys("邮箱账号")
chrome_driver.find_element_by_name("password").send_keys("邮箱密码")
chrome_driver.find_element_by_id("dologin").click()
[处理弹窗]
我们常见的弹窗一般分为3个样式,分别成为alert/prompt/confirm,同样的要定位弹窗控件中的元素或者操作控件都必须先切换进控件内
被测页面
<html>
<head>
<title>For Test Alert</title>
</head>
<body>
<input id = "alert" value = "alert" type = "button" onclick = "alert('您点击了alert按钮');"/>
<input id = "confirm" value = "confirm" type = "button" onclick = "confirm('您点击了confirm按钮');"/>
<input id = "prompt" value = "prompt" type = "button" onclick = "var name = prompt('您点击了prompt按钮:','Prompt'); document.write(name) "/>
</body>
</html>
方法封装
def switch_to_alert(self):
"""
切换进alert控件
:return:
"""
pop_dailog = self.driver.switch_to.alert
return pop_dailog
方法调用
def test_switch_to_alert(self):
chrome_driver = webdriver.Chrome()
# 浏览器打开我们刚才新建的html文件
chrome_driver.get("file:///C:/Users/davieyang/Desktop/test_alert.html")
time.sleep(3)
# 点击alert按钮
chrome_driver.find_element_by_id("alert").click()
time.sleep(3)
# 调用我们封装好的方法
al = Browser_Controller(chrome_driver).switch_to_alert()
print(al.text) # 打印弹窗中的文本
# 相当于点击弹窗中的确定按钮,但实际并不是点击只是弹窗对象提供的方法,效果一样
al.accept()
def test_switch_to_confirm(self):
chrome_driver = webdriver.Chrome()
# 浏览器打开我们刚才新建的html文件
chrome_driver.get("file:///C:/Users/davieyang/Desktop/test_alert.html")
time.sleep(3)
# 点击alert按钮
chrome_driver.find_element_by_id("confirm").click()
time.sleep(3)
# 调用我们封装好的方法
al = Browser_Controller(chrome_driver).switch_to_alert()
print(al.text) # 打印弹窗中的文本
# 相当于点击弹窗中的取消按钮,但实际并不是点击只是弹窗对象提供的方法,效果一样
al.dismiss()
def test_switch_to_prompt(self):
chrome_driver = webdriver.Chrome()
# 浏览器打开我们刚才新建的html文件
chrome_driver.get("file:///C:/Users/davieyang/Desktop/test_alert.html")
time.sleep(3)
# 点击alert按钮
chrome_driver.find_element_by_id("prompt").click()
time.sleep(3)
# 调用我们封装好的方法
al = Browser_Controller(chrome_driver).switch_to_alert()
print(al.text) # 打印弹窗中的文本
# 相当于点击弹窗中的确定按钮,但实际并不是点击只是弹窗对象提供的方法,效果一样
al.accept()
[处理下拉菜单]
Selenium为选择下拉菜单中的选项提供了3中方法,接下来分别将这三种方法进行封装然后调用。
方法封装
from selenium.webdriver.support.select import Select
def select_by_index(self, element, index):
"""
通过下拉菜单的索引,完成对选项的选择
:param element:
:param value:
:return:
"""
Select(element).select_by_index(index)
def select_by_value(self, element, value):
"""
通过选项值,完成对选项的选择
:param element:
:param value:
:return:
"""
Select(element).select_by_value(value)
def select_by_text(self, element, text):
"""
通过选项的文本,完成对选项的选择
:param element:
:param text:
:return:
"""
Select(element).select_by_visible_text(text)
方法调用
def test_select(self):
chrome_driver = webdriver.Chrome()
chrome_driver.get("http://www.baidu.com")
chrome_driver.implicitly_wait(30)
mouse = chrome_driver.find_element_by_link_text("设置")
ActionChains(chrome_driver).move_to_element(mouse).perform()
chrome_driver.find_element_by_link_text("搜索设置").click()
time.sleep(5)
chrome_driver.find_element_by_name("NR").click()
time.sleep(5)
select = chrome_driver.find_element_by_name("NR")
Browser_Controller(chrome_driver).select_by_value(select, "20")
time.sleep(5)
Browser_Controller(chrome_driver).select_by_index(select, 1)
time.sleep(5)
Browser_Controller(chrome_driver).select_by_text(select, "每页显示50条")
time.sleep(5)
方法扩展
实际上Selenium提供的处理下拉菜单选项的不止我们封装的三种方法,还有如下所示取消选项选择的方法
deselect_by_index(index) # 根据索引取消选择
deselect_by_value(value) # 根据value取消选择
deselect_by_visible_text(text) # 根据文本取消选择
deselect_all() # 取消所有选择
[上传文件]
上传附件是我们在测试BS系统的时候经常遇到的功能,然而在处理上传附件的自动化代码并不总是有效的,因此需要掌握多种上传附件的手段,本节笔者将介绍几种上传附件的方法,应该可以满足绝大多数的情况。
被测页面
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>上传文件</title>
</head>
<body>
<div class="row-fluid">
<div class="span6 well">
<h3>选择文件</h3>
<input type="file" name="fileupload" />
</div>
</div>
</body>
</html>
测试代码
def test_upload_by_sendkeys(self):
chrome_driver = webdriver.Chrome()
chrome_driver.get("file:///C:/Users/Administrator/Desktop/fileupload.html")
chrome_driver.find_element_by_name("fileupload").send_keys("E:\\test_upload_file.txt")
time.sleep(10)
chrome_driver.quit()
借助AutoIt实现上传
如果页面标签非input类型,可以通过第三方工具来完成上传操作。
- 首先第一步下载AutoIt工具,浏览器访问https://www.autoitscript.com/files/autoit3/autoit-v3-setup.exe,即可直接下载,下载完成后双击autoit-v3-setup.exe文件,默认选项安装即可,安装完成后,在操作系统的开始菜单中能看到相关菜单项
- 用浏览器打开上一小节创建的fileupload.html文件,然后在打开的页面中点击“选择文件”此时选择文件的窗口将弹出
- 在开始菜单中点击AutoIt Window Info,改程序存在两个版本(x86)表示32位版本,(x64)表示64位版本,读者朋友根据自己的操作系统版本启动相应的AutoIt版本即可,启动成功后
- 在AutoIt Window Info窗口中间部分有几个标签,然后拖拽Finder Tool到“打开”按钮上,便可获取该控件的窗口信息
- 启动SciTE Script Editor,在开始菜单AutoIt v3路径里的可以找到他
- 编写脚本,在SciTE Script Editor中写入如下内容,然后在选择文件的窗口打开的情况下,在SciTE Script Editor窗口按键盘上F5键,执行脚本,脚本运行正常,即可保存到我们PO项目下的Util路径中,命名为upload_file,保存成功后,会生成一个upload_file.au3的文件
; ControlFocus("title", "text", "ClassnameNN") ControlFocus函数的用法
ControlFocus("打开", "", "Edit1")
; 等待10秒
WinWait("[CLASS:#32770]", "", 10)
; 在文件名控件里设置要上传的文件全路径
ControlSetText("打开", "", "Edit1", "E:\test_upload_file.txt")
Sleep(2000)
; 点击打开按钮
ControlClick("打开", "", "Button1")
- 然而这个upload_file.au3文件并不能被Python执行,需要将其编译成.exe文件以供Python调用,启动Compile Script to .exe,在开始菜单Auto v3路径里可以找到它,启动(x86)或者(x64)根据自己的操作系统版本对应选择即可
- 选择之前保存的au3文件,点击Convert按钮,将其转换为.exe文件
- Python脚本调用该.exe完成文件的上传
import os # 引入os模块用于调用.exe文件执行
def test_upload_by_autoit(self): # 定义测试方法
chrome_driver = webdriver.Chrome() # 启动浏览器
#打开我们的html文件
chrome_driver.get("file:///C:/Users/Administrator/Desktop/fileupload.html")
chrome_driver.find_element_by_name("fileupload").click()
os.system("E:\\PO\\Util\\upload_file.exe") # 调用我们编译好的.exe文件
time.sleep(10) # 强制等待10秒
chrome_driver.quit()
模拟键盘实现上传
封装操作剪切板方法
# encoding = utf-8
import win32clipboard as wc
import win32con
class Simulate_Clipboard:
# 读取剪切板
@staticmethod
def get_clipboard():
# 打开剪切板
wc.OpenClipboard()
# 获取剪切板中的数据
data = wc.GetClipboardData(win32con.CF_TEXT)
# 关闭剪切板
wc.CloseClipboard()
# 返回剪切板数据给调用者
return data
# 设置剪切板内容
@staticmethod
def set_clipboard(content):
# 打开剪切板
wc.OpenClipboard()
# 清空剪切板
wc.EmptyClipboard()
# 将数据astring写入剪切板
wc.SetClipboardData(win32con.CF_UNICODETEXT, content)
# 关闭剪切板
wc.CloseClipboard()
方法调用
# 将模拟剪切板的类引入到测试代码文件中
from Util.Clipboard_Simulation import Simulate_Clipboard
def test_simulate_clipboard(self): # 定义测试方法
Simulate_Clipboard.set_clipboard("set clipboard") # 设置剪切板内容
str = Simulate_Clipboard.get_clipboard() # 获取剪切板内容并赋给str
print(str) # 将剪切板内容打印到控制台
截切板配合键盘实现上传
def test_upload_by_simulation(self): # 定义测试方法
# 设置剪切板内容,将文件全路径放到剪切板中
Simulate_Clipboard.set_clipboard("E:\\test_upload_file.txt")
chrome_driver = webdriver.Chrome() # 启动浏览器
# 打开我们的html文件
chrome_driver.get("file:///C:/Users/Administrator/Desktop/fileupload.html")
chrome_driver.find_element_by_name("fileupload").click()
time.sleep(5)
Simulate_Keyboard.click_twokey('ctrl', 'v') # 模拟键盘Ctrl+V组合键,黏贴剪切板内容
time.sleep(5)
Simulate_Keyboard.click_onekey('enter') # 模拟键盘回车键
time.sleep(20)
[JS完成任务]
有些时候Selenium并不能帮我们完成页面上的所有操作,例如滚动条的控制就比较难处理,而且有些时候click()方法也会失灵即便我们定位按钮没问题也有点击不了的情况,这些情况下我们就可以借助Python可以执行JS的机制,借助JS来辅助我们完成一些任务。
方法封装
class JS_Assistance: # 定义类
def __init__(self, driver):
self.driver = driver
def single_click(self, element):
try:
# 判断页面元素状态
if element.is_enabled() and element.is_displayed():
# 调用js单击元素
self.driver.execute_script("arguments[0].click();", element)
else:
print("该元素不可点击")
except Exception as e:
raise e
def scroll_to_bottom(self):
"""
滚动条滚动到页面底部
:return:
"""
self.driver.execute_script("document.documentElement.scrollTop=10000")
def scroll_to_top(self):
"""
滚动条滚动到页面顶部
:return:
"""
self.driver.execute_script("document.documentElement.scrollTop=0")
def scrolltobottom(self):
"""
滚动条滚动到页面底部
:return:
"""
self.driver.execute_script("window.scrollTo(0,100000)")
def scrolltotop(self):
"""
滚动条滚动到页面顶部
:return:
"""
self.driver.execute_script("window.scrollTo(0,1)")
def vertical_to_middle(self):
"""
纵向滚动条滚动到页面中部
:return:
"""
self.driver.execute_script("window.scrollBy(0, 0-document.body.scrollHeight *1/2)")
def horizontal_to_middle(self):
"""
滚动水平滚动条到页面中部
:return:
"""
self.driver.execute_script("window.scrollBy(0, 0-document.body.scrollWidht *1/2)")
def scroll_to_element(self, element):
"""
滚动到具体页面元素可见位置
:param element:
:return:
"""
self.driver.execute_script("arguments[0].scrollIntoView(true);", element)
def scroll_to_bottom_page(self):
"""
滚动条滚动到页面底部
:return:
"""
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
方法调用
def test_js(self): # 定义测试方法
chrome_driver = webdriver.Chrome()
chrome_driver.get("http://www.baidu.com")
chrome_driver.find_element_by_id("kw").send_keys("davieyang")
chrome_driver.find_element_by_id("su").click()
JS_Assistance(chrome_driver).scroll_to_bottom() #滚动页面到底部
time.sleep(3)
JS_Assistance(chrome_driver).scroll_to_top() #滚动页面到顶部
time.sleep(3)
JS_Assistance(chrome_driver).scroll_to_bottom_page() #滚动页面到底部
time.sleep(3)
JS_Assistance(chrome_driver).scrolltotop() #滚动页面到顶部
time.sleep(3)
JS_Assistance(chrome_driver).scrolltobottom() #滚动页面到底部
time.sleep(3)
element = chrome_driver.find_element_by_xpath("//*[@id='help']/a[3]")
JS_Assistance(chrome_driver).single_click(element) # 单击该页面元素
time.sleep(3)
[句柄]
在实际的自动化测试过程中往往会遇到我们的产品点击页面中的元素后,会启动浏览器新的页签,注意此处说的浏览器页签并不是我们系统内的标签,而启动了浏览器第二个页签后,就意味着我们的自动化程序要在两个页签内切换完成一些交互,因此切换页签便成了一个课题。
def test_switch_window_handle(self): # 定义测试方法
chrome_driver = webdriver.Chrome() #启动浏览器
chrome_driver.get("http://www.baidu.com") #打开百度首页
baidu_main_handle = chrome_driver.current_window_handle # 获取当前浏览器句柄
print(baidu_main_handle) # 为方便调试,将句柄打印到控制台
time.sleep(5) # 等待5秒
chrome_driver.find_element_by_link_text("登录").click() # 点击登录按钮
time.sleep(5) # 等待5秒
chrome_driver.find_element_by_link_text("立即注册").click() # 在弹出窗口中点击立即注册
all_handles = chrome_driver.window_handles # 获取所有句柄
print(all_handles) # 打印所有句柄到控制台
for handle in all_handles: # 在所有句柄中进行循环
try:
if handle != baidu_main_handle: # 判断是否句柄不等于百度首页的句柄,如不等于
chrome_driver.switch_to.window(handle) # 则切换句柄
print("进入新窗口....")
chrome_driver.switch_to.window(baidu_main_handle) #再切换回百度首页句柄
chrome_driver.refresh() # 刷新页面
# 输入检索内容到输入框
chrome_driver.find_element_by_id("kw").send_keys("__davieyang__")
time.sleep(5)
# 点击百度一下按钮
chrome_driver.find_element_by_id("su").click()
time.sleep(5)
except Exception as e:
raise e
chrome_driver.quit() # 关闭浏览器
[日志]
在实际的自动化测试代码调试过程中往往我们需要记录一些日志,一方面是打印到控制台便于我们调试代码,如果是持续集成的环境无人值守的话也是对测试执行过程的一个记录过程。
方法封装
新建一个Python文件, 并命名为ConstantConfig,然后在该文件中写入如下代码。
# 用于定义整个框架中所需要的全局常量值
# encoding = utf-8
import os
# 获取当前文件所在目录的父目录的绝对路径
parent_directory_path = os.path.abspath('..')
print(parent_directory_path)
# encoding = utf-8
import time
import logging
from Configuration.ConstantConfig import parent_directory_path
class Logger(object):
def __init__(self, logger):
"""
指定保存日志的文件路径,日志级别,以及调用文件
将日志存入到指定的文件中
:param logger:
"""
# 创建一个logger
self.logger = logging.getLogger(logger)
self.logger.setLevel(logging.DEBUG)
# 创建一个handler,用于写入日志文件
rq = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))
log_path = parent_directory_path + '/TestResult/TestLog/'
log_name = log_path + rq + '.log'
filehandler = logging.FileHandler(log_name)
filehandler.setLevel(logging.INFO)
# 再创建一个handler,用于输出到控制台
consolehandler = logging.StreamHandler()
consolehandler.setLevel(logging.INFO)
# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')
filehandler.setFormatter(formatter)
consolehandler.setFormatter(formatter)
# 给logger添加handler
self.logger.addHandler(filehandler)
self.logger.addHandler(consolehandler)
def getlog(self):
return self.logger
新建一个名为TestLog的文件夹,用于存储生成的log文件
方法调用
testlogger = GetLog.Logger('Test_Advanced_Application').getlog()
class Test_Advanced_Application(unittest.TestCase):
def test_get_log(self):
testlogger.info("打开浏览器")
driver = webdriver.Chrome()
driver.maximize_window()
testlogger.info("最大化浏览器窗口。")
driver.implicitly_wait(10)
testlogger.info("打开百度首页。")
driver.get("https://www.baidu.com")
testlogger.info("暂停3秒。")
time.sleep(3)
testlogger.info("关闭并退出浏览器")
driver.quit()
with self.assertLogs(testlogger, level=20) as log:
testlogger.error("打开浏览器")
testlogger.info('关闭并退出浏览器')
self.assertEqual(log.output,
['ERROR:Test_Advanced_Application:打开浏览器',
'INFO:Test_Advanced_Application:关闭并退出浏览器']
)
执行结果
能看到执行过程中将我们的日志逐一打印到控制台的过程
然后再到我们创建的TestLog文件夹下查看日志文件,如果遇到乱码,如图11.13所示,不必慌张,是因为编码不匹配导致的,点击Reload in ‘GBK’,将编码格式转换成GBK即可正常显示
产生乱码的原因是因为项目设置中的File Encodings里,Global Encoding是UTF-8