前面文章中针对云真机平台稳定性设计了【云真机平台】平台稳定性测试方案,接下来实现具体的脚本进行稳定性测试
一、selenium IDE录制
许久没有写UI自动化,倍感陌生哈哈,那么使用selenium IDE进行录制看看是否能一把搞定
1.1 录制步骤
- 打开页面
- 点击立即使用
- 点击断开连接
1.2 录制效果
1.3 脚本导出
从selenium IDE中导入录制的脚本(此处导出语言选择为Python pytest)
# Generated by Selenium IDE
import pytest
import time
import json
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
class Testlluozh():
def setup_method(self, method):
self.driver = webdriver.Chrome()
self.vars = {}
def teardown_method(self, method):
self.driver.quit()
def test_untitled(self):
# Test name: Untitled
# Step # | name | target | value
# 1 | open | / |
self.driver.get("http://uiat.gz.lluozh.cn/")
# 2 | setWindowSize | 1545x708 |
self.driver.set_window_size(1545, 708)
# 3 | click | xpath=(//button[@type='button'])[8] |
self.driver.find_element(By.XPATH, "(//button[@type=\'button\'])[8]").click()
# 4 | click | xpath=//div[@id='mobile-container']/div[2]/div[6] |
self.driver.find_element(By.XPATH, "//div[@id=\'mobile-container\']/div[2]/div[6]").click()
二、多进程执行
前面测试方案中提及,需要同时执行1、2、5、10台设备,故需要多进程执行脚本文件
def device_connect(device_id):
# TODO
if __name__ == '__main__':
device_list = ['lluozh001','lluozh002','lluozh003','lluozh004']
for device_id in device_list:
p = Process(target=device_connect, args=(device_id,))
p.start()
p_list.append(p)
for p in p_list:
p.join()
for p in p_list:
p.join()
三、连接特定设备
3.1 设备列表展示逻辑
录制脚本可执行成功,但多个进程执行时设备列表实时刷新连接状态,且将正在使用的设备排序在未使用设备后面,未使用设备根据借用时间排序
3.2 出现问题
出现两个进程同时点击连接某个设备,这样导致其中一个进程出现非预期的失败
3.3 解决方案
- 不同进程操作各自的设备,相互之间互不影响
- 根据设备名称查找对应的设备
- 定位到设备名称后通过子节点查找对应的父节并找到对应的btn
3.4 实现细节
- 设备选择框的树状层级
使用子节点查找对应的父节并找到对应的btn,对应的脚本
# 点击立即使用按钮
driver.find_element_by_xpath("//input[@value='{}']/../../../../../../../../../ul/li/span/button".format(device_id)).click()
四、连接状态
点击立即使用连接设备后需要判断连接是否成功
4.1 问题现象
虽然点击连接均会打开设备连接窗口,但连接异常时会有对应的提示,比如
- 远程连接已断开
- 设备操作异常
故仅通过窗口是否存在无法判断连接状态是否成功
4.2 判断提示字样是否存在
- 提示字样的树状图
- 通过判断该字段是否存在
try:
driver.find_element_by_class_name("connect-close")
expect:
pass
发现不管是否连接成功该字段都存在
4.2 判断提示字样style属性
既然该字段均存在,即异常时该信息才显示,需要通过另外的属性判断
通过树状图可以看到style字段信息,存在时显示为display: block;
那不存在异常提示的时候呢?
从上图可以看出,显示的是display: none;
故判断连接是否成功可以通过字样的style属性
style = driver.find_element_by_class_name("connect-close").get_attribute("style")
if style == 'display: block;':
return False
else:
return True
五、页面刷新
5.1 设备断开时实现逻辑
-
前端操作断开设备连接
-
后台异步返回结果后前端立即页面状态刷新
此时设备状态时占用中 -
后台处理断开连接逻辑
此时设备实际状态为未使用,但前端页面若没刷新还是显示该设备在使用中
5.2 解决方案:
针对该问题在此次稳定性测试中的解决方案:
- 需要将逻辑改成同步,后台处理断开连接逻辑后再返回处理结果
- 每次连接状态前先刷新页面
接下来暂时使用每次连接前先大刷新页面的方式
# 页面大刷新(不进行大刷新出现页面中该设备使用状态未更新)
driver.refresh()
六、滑动翻页
除了需要不断的进行连接和断开操作外,还需要通过web端滑动页面操作测试其稳定性
当前使用的方案是selenium+python,如何实现在web页面对远程的手机页面进行滑动操作呢?
6.1 ActionChains
接下来主要是使用ActionChains尝试针对页面进行操作
ActionChains的方法列表:
- click(on_element=None)
单击鼠标左键 - click_and_hold(on_element=None)
点击鼠标左键,不松开 - context_click(on_element=None)
点击鼠标右键 - double_click(on_element=None)
双击鼠标左键 - drag_and_drop(source, target)
拖拽到某个元素然后松开 - drag_and_drop_by_offset(source, xoffset, yoffset)
拖拽到某个坐标然后松开 - key_down(value, element=None)
按下某个键盘上的键 - key_up(value, element=None)
松开某个键 - move_by_offset(xoffset, yoffset)
鼠标从当前位置移动到某个坐标 - move_to_element(to_element)
鼠标移动到某个元素 - move_to_element_with_offset(to_element, xoffset, yoffset)
移动到距某个元素(左上角坐标)多少距离的位置 - perform()
执行链中的所有动作 - release(on_element=None)
在某个元素位置松开鼠标左键 - send_keys(*keys_to_send)
发送某个键到当前焦点的元素 - send_keys_to_element(element, *keys_to_send)
发送某个键到指定元素
6.2 move_to_element
使用ActionChains中move_to_element的方式滑动操作
actions = ActionChains(driver)
element = driver.find_element_by_id("transparentCanvas")
actions.move_to_element(element).click_and_hold().perform()
time.sleep(1)
element = driver.find_element_by_id("transparentCanvas")
actions = ActionChains(driver)
ActionChains(driver).move_to_element(element).perform()
time.sleep(1)
element = driver.find_element_by_id("transparentCanvas")
actions = ActionChains(driver)
actions.move_to_element(element).release().perform()
ActionChains(driver).click(element).perform()
通过验证发现,以上脚本使用move_to_element的方式无法成功对手机屏幕进行翻页
6.3 move_by_offset
使用ActionChains中move_by_offset的方式滑动操作
# 选择拖动滑块的节点
element = driver.find_element_by_id("transparentCanvas")
# ------------鼠标滑动操作------------
action = ActionChains(driver)
# 第一步:在滑块处按住鼠标左键
action.click_and_hold(element)
# 第二步:相对鼠标当前位置进行移动
action.move_by_offset(element.size.get('width'), 0)
# 第三步:释放鼠标
action.release()
# 执行动作
action.perform()
验证发现执行后依旧没有任何反应
6.3 drag_and_drop
使用ActionChains中之前使用过的drag_and_drop方式试试是否可以
element = driver.find_element_by_id("transparentCanvas")
ActionChains(driver).drag_and_drop_by_offset(element,element.size.get('width'), 0).perform()
执行后依旧没有反应,那究竟是哪里出现了问题呢?
6.4 offset数值
无法滑动成功,是否因为offset移动超过了范围导致未能成功
- width-10
通过移动width-10的宽度
element = driver.find_element_by_id("transparentCanvas")
ActionChains(driver).drag_and_drop_by_offset(element,element.size.get('width')-10, 0).perform()
验证发现同样没有反应
- width/2
通过移动width/2的宽度
element = driver.find_element_by_id("transparentCanvas")
ActionChains(driver).drag_and_drop_by_offset(element,element.size.get('width')/2, 0).perform()
验证发现同样没有反应
- width/4
通过移动width/4的宽度
element = driver.find_element_by_id("transparentCanvas")
ActionChains(driver).drag_and_drop_by_offset(element,element.size.get('width')/4, 0).perform()
验证发现可成功滑动
在offset=width/4时,验证发现使用move_by_offset和drag_and_drop均可正常滑动
6.5 实现脚本
实现不断左右滑动的脚本
element = driver.find_element_by_id("transparentCanvas")
offset_x = element.size.get('width')/4
for j in range(0,100):
# 左滑动
ActionChains(driver).drag_and_drop_by_offset(element,offset_x, 0).perform()
time.sleep(1)
# 右滑动
ActionChains(driver).drag_and_drop_by_offset(element, -offset_x, 0).perform()
七、截图
由于需要长时间自动化执行验证稳定性,那就不可能实时关注页面信息,故需要失败、异常或其他有需要时截取当时页面的图片,方便接下来定位问题
其实selenium封装了比较好的方式进行页面截图保存
# 文件目录
dir_name = '/Users/lluozh/docs/'
if not os.path.isdir(dir_name):
os.makedirs(dir_name)
# 设备id
device_id = 'lluozh001'
# 执行次数
times = 1
# 执行时间
run_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
# 截图操作(截图目录中保存这些信息主要是方便出现问题时更好定位问题)
driver.get_screenshot_as_file(dir_name + "/{}_{}_{}_{}.png".format(device_id, times, run_time, connect[1]))
稳定性验证的自动化脚本完成