学习笔记——测试进阶之路 工作笔记:基于微信 minium 框架的小程序自动化初步实践(6)
大海
·
2022年07月03日
·
4933 次阅读
「原创声明:保留所有权利,禁止转载」
学习过程中的经验总结
1、如何元素定位
首先找到该元素,(微信开发工具 > 调试器 > Wxml)
根据元素不同位置、属性、标签等,自行选择元素定位方式,支持
- 选择器 selector (id/class/标签/属性等)
- 选择器 selector + inner_text/text_contains/value
- XPath 定位
2、自定义组件有三种定位方式,通过 xpath 也可定位
- get_element()参数可选不同选择器,包括 xpath 定位元素
- get_element_by_xpath()通过 xpath 定位元素
3、如何选择定位元素方式
有自定义组件元素
- 使用 [>>>] 连接自定义组件及其后代元素
- custom-element1>>>.custom-element2>>>.the-descendant
- custom-element1 和 .custom-element2 必须是自定义组件标签或者能获取到自定义组件的选择器
- 先 get 自定义组件, 再使用 Element.get_element 获取其子节点
- XPath 定位
普通元素
- XPath 定位
- 选择器 selector(ID 选择器/class 选择器/标签选择器/子元素选择器/后代选择器/多选择器的并集)
4、minitest 生成的测试报告为空
解决方法:
执行命令
minitest -m test.first_test -c config.json -g
测试用例的执行可以用执行 unittest 的方式,也可以用官方提供的脚本 minitest 来加载用例,相关的参数说明如下:
minitest 命令
-h, --help: 使用帮助。
-v, --version: 查看 minium 的版本。
-p PATH/--path PATH: 用例所在的文件夹,默认当前路径。
-m MODULE_PATH, --module MODULE_PATH: 用例的包名或者文件名
--case CASE_NAME: test_开头的用例名
-s SUITE, --suite SUITE:测试计划文件,文件的格式如下:
{
"pkg_list": [
{
"case_list": [
"test_*"
],
"pkg": "test.*_test"
}
]
}
suite.json的pkg_list字段说明要执行用例的内容和顺序,pkg_list是一个数组,每个数组元素是一个匹配规则,会根据pkg去匹配包名,找到测试类,然后再根据case_list里面的规则去查找测试类的测试用例。可以根据需要编写匹配的粒度。注意匹配规则不是正则表达式,而是通配符。
测试文件可以指定特定的用例,规定执行顺序
-c CONFIG, --config CONFIG:配置文件名,配置项目参考配置文件
-g, --generate: 生成网页测试报告
--module_search_path [SYS_PATH_LIST [SYS_PATH_LIST ...]]: 添加 module 的搜索路径
-a, --accounts: 查看开发者工具当前登录的多账号, 需要通过 9420 端口,以自动化模式打开开发者工具
--mode RUN_MODE: 选择以parallel(并行, 每个账号从队列中取一个pkg运行, 完成后取下一个)或者fork(复刻, 每个帐号都跑全部的pkg)的方式运行用例
--task-limit-time: 任务超时时间,如果到期还没跑完测试,直接终止测试进程. 单位: s
5、为什么小程序底部菜单栏页面跳转失败
例如以下报错信息
minium.framework.exception.MiniAppError: can not redirectTo a tabbar page
该页面为 tabbar 页面,需调用 switch_tab() 跳转 注:页面 url 前面需加上 “/”
6、自定义日志信息
Minium 框架提供 Logger 类,自定义各级别日志信息
self.logger.info("")
self.logger.debug("")
self.logger.warn("")
7、用例执行耗时优化
缩短元素查找时间
例如该用例中获取元素方式,查找慢,会导致用例执行时长长
self.page.get_element("view", inner_text="xxx", max_timeout=5)
元素定位用基础标签 view、text 方式,时间相对会长,建议用特别的 class 等 css 选择器定位,或者尝试用 xpath(//view)
8、连接超时问题
本地调试,控制台出现报错时
Exception: receive from remote timeout, id: f933c71e-bc2b-41b7-b286-29c65dd0d4d8
一般是因为 ide 的通道被远程调试占用了,请检查 ide 是否有打开。建议关闭工具,重新执行,防止端口被占用,或者网络环境等原因引起的。
9、minium 自动化怎么授权位置信息弹窗
https://minitest.weixin.qq.com/#/minium/Python/api/Native?id=allow_authorize
测试用例 DEMO
# !/usr/bin/python
# -*- coding: utf-8 -*-
"""
@Author : Charles
@Contact : MyProject@MyProject.com
@File : MyProject_test.py
@Create Time: 2022-06-30 09:26
@Description: XXXXXXXXXXXX
"""
import unittest
import time
import minium
from common import db_func
from common.db_func import get_current_time
from common.message import send_failmessage
execid = int(time.time())
print("execid: ", execid)
@minium.ddt_class
class MyProject(minium.MiniTest):
"""
MyProjectMyProjectMyProjectMyProjectMyProjectMyProject
"""
case_name = "MyProject"
case_field = "MyProject"
project_name = "MyProject"
show_livedata_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0))
show_offline_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0))
@minium.ddt_case("43338957","43338966", "43338976", "43338978", "44280929", "43338962", "43338960", "43338961",
"43338954", "43338956", "43338958", "43338955", "43338959", "43338950", "43338952", "43338951",
"43338953", "43338963", "43338965", "43338967", "43338964", "43338968", "43338969", "43338970",
"43338971", "43338972", "43338973", "43338974", "43338975", "43338977", "43338979", "44280927",
"44280930", "44280921", "44280922", "44280917", "44280918", "44280920", "44280923", "44280924",
"44280925", "44280926")
def test_device_livedata(self, args):
"""
MyProjectMyProjectMyProjectMyProjectMyProject
"""
self.page.wait_for(2)
# 用例开始时间
case_starttime = get_current_time()
try:
if self.page.get_element("/page/view/equip//view/view[3]/view[1]/view[2]/view[2]",
text_contains="MyProject",
max_timeout=5).inner_text == "MyProject":
self.page.wait_for(2)
except:
if self.page.get_element("/page/view/equip//view/view[3]/view[2]",
text_contains="MyProject",
max_timeout=5).inner_text == "MyProject":
self.page.wait_for(2)
# 点击设备搜索框
try:
elem = self.page.get_element("input[name='input'][placeholder='MyProject']",
inner_text="MyProject",
text_contains="MyProject",
value="MyProject",
max_timeout=5)
except:
elem = self.page.get_element("/page/view/equip//view/view[1]/view/view[2]/input", max_timeout=5)
elem.click()
self.page.wait_for(2)
# 设备搜索框中输入设备号
device_number = args
try:
elem = self.page.get_element("input[name='input'][placeholder='MyProject']", max_timeout=5)
except:
elem = self.page.get_element("/page/view/view/view[1]/view/view[2]/input", max_timeout=5)
elem.trigger("input", {"value": device_number})
self.page.wait_for(2)
# 点击搜索图标进行搜索
elem = self.page.get_element("/page/view/view/view[1]/view/view[1]/image", max_timeout=5)
elem.click()
self.page.wait_for(2)
# 点击设备,跳转到设备详情页
try:
elem = self.page.get_element("text.text-black", max_timeout=5)
except:
elem = self.page.get_element("/page/view/view/view[2]/view/scroll-view/view",
inner_text="MyProject",
text_contains="MyProject",
value="MyProject",
max_timeout=5)
elem.click()
# 获取当前楼层数据
trigger_data_time = get_current_time()
print("获取当前楼层数据 trigger_data_time: ", trigger_data_time)
try:
# 判断状态是否为--
start_time = time.time()
floor_number = '--'
while self.page.element_is_exists("/page/view/view[2]/view[2]/view/view[1]/view/view[2]/text"):
print("While True: 继续执行下面的代码")
if floor_number == '--':
end_time = time.time()
duration = end_time - start_time
print("数据加载时间: ", duration)
print("-------------------判断加载时间是否大于60秒-----------------")
if duraion > 60:
# 超过60秒默认数据加载失败,获取当前设备掉线时间
MyProject.show_offline_time = get_current_time()
MyProject.show_livedata_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0))
print("超过60秒默认数据加载失败,数据加载失败的时间: ", MyProject.show_offline_time,
"实时数据设为默认时间", MyProject.show_livedata_time)
break
floor_marking = self.page.get_element(
"/page/view/view[2]/view[2]/view/view[1]/view/view[2]/text",
max_timeout=5).inner_text
floor_number = floor_marking.strip()
print("循环语句下,当前楼层数为:", floor_number)
if floor_number != "--" and floor_number != "----" and floor_number != "设备DTU掉线" and floor_number != "设备CPU掉线":
MyProject.show_livedata_time = get_current_time()
MyProject.show_offline_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0))
print("循环语句下,实时数据成功展示时间: ", MyProject.show_livedata_time,
"设备掉线时间设为默认值", MyProject.show_offline_time)
break
elif floor_number == "----":
floor_marking = self.page.get_element(
"/page/view/view[2]/view[2]/view/view[1]/view/view[2]/text",
max_timeout=5).inner_text
floor_number = floor_marking.strip()
print("发现xpath路径,再次进行取值,当前楼层数为:", floor_number)
MyProject.show_livedata_time = get_current_time()
MyProject.show_offline_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0))
print("循环语句下,实时数据成功展示时间: ", MyProject.show_livedata_time,
"设备掉线时间设为默认值", MyProject.show_offline_time)
break
else:
print("设备实时数据,存在异常。")
continue
except:
# 进入设备掉线的页面
print("等待状态已改变,进入设备掉线的页面。。。。。。。。。。")
floor = self.page.get_element("/page/view/view[2]/view[2]/view/view[1]/text",
text_contains="掉线",
max_timeout=5)
floor_marking = floor.inner_text
floor_number = floor_marking.strip()
print("当前楼层数为: ", floor_number)
MyProject.show_offline_time = get_current_time()
MyProject.show_livedata_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0))
print("进入设备掉线的页面,设备掉线的时间: ", MyProject.show_offline_time,
"设备掉线,实时数据设为默认时间", MyProject.show_livedata_time)
# 楼层数据校验
if floor_number != "" and floor_number != "--" and floor_number != "设备XXX掉线" and floor_number != "设备YYY掉线":
floor_int_value = ["-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
floor_str_value = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
if 1 < len(floor_number) < 7:
if floor_number[0] in floor_int_value and floor_number[1] in floor_int_value:
# 如果楼层数为正负数开头的,且第二位是数字,说明数据显示正常,进行类型转换
self.assertTrue(floor_number, "当前楼层显示正常,楼层为: {}".format(int(floor_number)))
assert_status = "pass"
elif floor_number[0] in floor_str_value and floor_number[1] in floor_int_value:
# 如果楼层数第一位是字母,第二位是数字,说明数据依然是显示正常,不需要进行类型转换
self.assertTrue(floor_number, "当前楼层显示正常,楼层为: {}".format(floor_number))
assert_status = "pass"
else:
# 电梯数据存在异常,直接推送报警
send_failmessage(self.case_name, device_number, self.project_name, self.case_field,
floor_number)
assert_status = "fail"
case_endtime = get_current_time()
# 写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number,
trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
self.assertFalse(floor_number, "楼层信息显示异常,楼层为:{}".format(floor_number))
elif len(floor_number) == 1:
if floor_number[0] in floor_int_value:
# 如果楼层数为单个数字,说明数据显示正常,进行类型转换
self.assertTrue(floor_number, "当前楼层显示正常,楼层为: {}".format(int(floor_number)))
assert_status = "pass"
elif floor_number[0] in floor_str_value:
# 如果楼层数为单个字母,说明数据显示正常,进行类型转换
self.assertTrue(floor_number, "当前楼层显示正常,楼层为: {}".format(floor_number))
assert_status = "pass"
else:
# 如果楼层数不是单个数字或者单个字母,说明数据显示异常,直接推送报警
send_failmessage(self.case_name, device_number, self.project_name, self.case_field,
floor_number)
assert_status = "fail"
case_endtime = get_current_time()
# 写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number,
trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
self.assertFalse(floor_number, "楼层信息显示异常,楼层为:{}".format(floor_number))
else:
# 如果获取到的数据长度不是1,2,7,说明是其他内容,直接推送报警
send_failmessage(self.case_name, device_number, self.project_name, self.case_field,
floor_number)
assert_status = "fail"
case_endtime = get_current_time()
# 写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number, trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
self.assertFalse(floor_number, "楼层信息显示异常,楼层为:{}".format(floor_number))
elif floor_number == "设备XXX掉线":
# 设备掉线,直接推送报警
send_failmessage(self.case_name, device_number, self.project_name, self.case_field, floor_number)
assert_status = "fail"
case_endtime = get_current_time()
# 写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number, trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
self.assertFalse(floor_number, "楼层信息显示异常,楼层为:{}".format(floor_number))
elif floor_number == "设备YYY掉线":
# 设备掉线,直接推送报警
send_failmessage(self.case_name, device_number, self.project_name, self.case_field, floor_number)
assert_status = "fail"
case_endtime = get_current_time()
# 写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number, trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
self.assertFalse(floor_number, "楼层信息显示异常,楼层为:{}".format(floor_number))
elif floor_number == "--" or floor_number == "----":
# 实时数据没有加载出来,推送报警
send_failmessage(self.case_name, device_number, self.project_name, self.case_field, floor_number)
assert_status = "fail"
case_endtime = get_current_time()
# 写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number, trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
self.assertFalse(floor_number, "楼层信息未成功显示,楼层为:{}".format(floor_number))
else:
# 楼层信息显示为其他字符,说明电梯存在异常,直接推送报警
send_failmessage(self.case_name, device_number, self.project_name, self.case_field, floor_number)
assert_status = "fail"
case_endtime = get_current_time()
# 写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number, trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
self.assertFalse(floor_number, "楼层信息显示异常,楼层为:{}".format(floor_number))
# 返回上一级:搜索结果页
self.page.wait_for(2)
self.app.navigate_back()
self.page.wait_for(2)
# 返回上一级:小程序首页
self.app.navigate_back()
self.page.wait_for(2)
# 测试用例结束时间
case_endtime = get_current_time()
self.page.wait_for(2)
# 数据写入数据库
db = db_func.connect_db()
sql = "insert INTO device (execid, device_number, project_name, case_field, case_content, triggertime, " \
"showtime, offlinetime, starttime, endtime, status) " \
"VALUES ('{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}');".format \
(execid, device_number, self.project_name, self.case_field, floor_number, trigger_data_time,
MyProject.show_livedata_time, MyProject.show_offline_time, case_starttime, case_endtime,
assert_status)
db_func.db_insert(db, sql)
if __name__ == "__main__":
loaded_suite = unittest.TestLoader().loadTestsFromTestCase(KoneView)
result = unittest.TextTestRunner().run(loaded_suite)
TesterHome 为用户提供「保留所有权利,禁止转载」的选项。
除非获得原作者的单独授权,任何第三方不得转载标注了「原创声明:保留所有权利,禁止转载」的内容,否则均视为侵权。
具体请参见TesterHome 知识产权保护协议。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。