学习笔记——测试进阶之路 工作笔记:基于微信 minium 框架的小程序自动化初步实践(6)

大海 · 2022年07月03日 · 5000 次阅读

学习过程中的经验总结

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)

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册