python3+uiautomator2实现POM(微信小程序自动化测试)QQ群交流 822659419

在网上看的各种SeleniumPOM-框架,找来找去没有uiautomator2的POM,所以决定写一个uiautomator2的POM。

先介绍下什么是POM摘抄

Python中的单元测试框架unittest,以后我们所有的测试类文件,都采用unittest来辅助我们进行debug和脚本开发。搞定了debug机制和确定了unittest来进行创建和管理我们的自动化测试脚本,接下来我们来考虑下,框架设计中一种很普遍的设计思想-POM(Page Object Model)

POM,中文字母意思是,页面对象模型,POM是一种最近几年非常流行的自动化测试模型,或者思想,POM不是一个框架,就是一个解决问题的思想。采用POM的目的,是为了解决前端中UI变化频繁,从而造成测试自动化脚本维护的成本越来越大。

采取了POM设计思路和不采取的区别,左侧把测试代码和页面元素都写在一个类文件,如果需要更改页面,那么就要修改页面元素定位,从而要修改这个类中测试代码,这个看起来和混乱。右侧,采取POM后,主要的区别就是,把页面元素和业务逻辑和测试脚本分离出来到两个不同类文件。ClassA只写页面元素定位,和业务逻辑代码操作的封装,ClassB只写测试脚本,不关心如何元素定位,只写调用ClassA的代码去覆盖不同的测试场景。如果前端页面发生变化,只需要修改ClassA的元素定位,而不需要去修改ClassB中的测试脚本代码。

POM主要有以下优点:

1.把web ui对象仓库从测试脚本分离,业务代码和测试脚本分离。

2.每一个页面对应一个页面类,页面的元素写到这个页面类中。

3.页面类主要包括该页面的元素定位,和和这些元素相关的业务操作代码封装的方法。

4.代码复用,从而减少测试脚本代码量。

5.层次清晰,同时支持多个编写自动化脚本开发,例如每个人写哪几个页面,不影响他人。

6.建议页面类和业务逻辑方法都给一个有意义的名称,方便他人快速编写脚本和维护脚本。

关于基类,是这样定义的:把一些常见的页面操作的selenium封装到base_page.py这个类文件,以后每个POM中的页面类,都继承这个基类,这样每个页面类都有基类的方法

然后介绍下uiautomator2下POM的实现

  1. 创建文件夹搞清楚每个文件夹存放的内容
framework:代码复用,从而减少测试脚本代码量
logs:运行日志
modules:业务代码
screenshots:测试失败截图
testsuites:测试脚本
test_report:测试报告
tools:其他第三方库

在这里插入图片描述
2. 业务代码(modules)
entrance.py

# coding=utf-8
import time
import unittest
import uiautomator2  as u2
from framework.failed import Failed
from framework.logger import Logger

logger = Logger(logger="BrowserEngine").getlog()


d = u2.connect()
#d = u2.connect('192.168.1.173')

now=time.strftime("%Y%m%d%H%M%S",time.localtime())

class Entrance(unittest.TestCase):

    def entrance_test(self):
        logger.info("执行进入小程序.")
        print(now + ":" + "进入小程序")
        d.app_start("com.tencent.mm")
        logger.info("打开微信.")
        time.sleep(1)
        while True:
            logger.info("判断是否到微信主界面.")
            if d(resourceId="com.tencent.mm:id/jb").exists:
                logger.info("当前在微信主界面,执行测试微信小程序.")
                d(resourceId="com.tencent.mm:id/jb").click()
                time.sleep(1)
                d(text=u'搜索').set_text("火眼看看")
                time.sleep(1)
                d(resourceId="com.tencent.mm:id/qm").click()
                time.sleep(2)
                '''
                while True:

                    if d.xpath("//android.widget.TextView[@text='拍照识别']").exists:
                        logger.info("已经到达识别界面")
                        break
                    else:
                        logger.info("进入小程序不是识别界面,寻找识别界面")

                        d.press("back")
                        time.sleep(2)

                    # 断言
                    if (d(text=u"点我了解").exists):
                        print(now + ":" + '进入小程序成功')
                    else:
                        print(now + ":" + '进入小程序失败')
                        Failed.failed_test(self)
                        '''

                break
            else:
                logger.info("在非微信主界面,执行返回.")
                d.press("back")
                time.sleep(1)

3.可复用性代码(framework)

执行结果失败时截图和标记用例失败(failed.py

# coding=utf-8
import time
import unittest
import os
import uiautomator2  as u2
from framework.logger import Logger


logger = Logger(logger="BasePage").getlog()
d = u2.connect()
#d = u2.connect('192.168.1.173')

now=time.strftime("%Y%m%d%H%M%S",time.localtime())

class Failed(unittest.TestCase):
    def failed_test(self):
        logger.error("出现错误!" )
        now = time.strftime("%Y%m%d%H%M%S", time.localtime())
        report_path = os.path.dirname(os.path.abspath('.')) + '/screenshots/'
        picture = report_path + now + "error.jpg"
        d.screenshot(picture)
        a = "P"
        b = "F"
        self.assertEqual(a, b)


判断手机是否需要解锁(lock.py

# coding=utf-8
import time
import unittest
import uiautomator2  as u2
from framework.logger import Logger

logger = Logger(logger="BrowserEngine").getlog()



d = u2.connect()
#d = u2.connect('192.168.1.173')

now=time.strftime("%Y%m%d%H%M%S",time.localtime())

class Lock(unittest.TestCase):
    def  lock_test(self):  #手机屏幕解锁
        logger.info("检查是否需要解锁.")
        if d(resourceId="com.smartisanos.keyguard:id/desk_kg").exists:  #检查是否存在灭屏状态的元素属性
            logger.info("需要解锁.")
            d.screen_on()
            time.sleep(3)
            d.swipe_points([(0.485, 0.708), (0.481, 0.286)], 0.05)  # 滑动解锁界面
            time.sleep(1)
            d.swipe_points([(0.762, 0.394), (0.489, 0.525), (0.777, 0.529), (0.503, 0.651), ], 0.05)  # 解九宫锁
            time.sleep(1)
            logger.info("解锁成功.")
        else:
            logger.info("当前未锁屏,无需解锁.")



运行日志(logger.py

# _*_ coding: utf-8 _*_
import logging
import os.path
import time


class Logger(object):
    def __init__(self, logger):
        '''''
            指定保存日志的文件路径,日志级别,以及调用文件
            将日志存入到指定的文件中
        '''

        # 创建一个logger
        self.logger = logging.getLogger(logger)
        self.logger.setLevel(logging.DEBUG)

        # 创建一个handler,用于写入日志文件
        rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
        # log_path = os.path.dirname(os.getcwd()) + '/Logs/'  # 项目根目录下/Logs 保存日志
        log_path = os.path.dirname(os.path.abspath('.')) + '/logs/'
        # 如果case组织结构式 /testsuit/featuremodel/xxx.py , 那么得到的相对路径的父路径就是项目根目录
        log_name = log_path + rq + '.log'
        fh = logging.FileHandler(log_name)
        fh.setLevel(logging.INFO)

        # 再创建一个handler,用于输出到控制台
        ch = logging.StreamHandler()
        ch.setLevel(logging.INFO)

        # 定义handler的输出格式
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        fh.setFormatter(formatter)
        ch.setFormatter(formatter)

        # 给logger添加handler
        self.logger.addHandler(fh)
        self.logger.addHandler(ch)

    def getlog(self):
        return self.logger

滑动屏幕,寻找目标(sliding.py

# coding=utf-8
import time
import unittest
import uiautomator2  as u2
from framework.failed import Failed

from framework.logger import Logger

logger = Logger(logger="BrowserEngine").getlog()


d = u2.connect()
#d = u2.connect('192.168.1.173')

now=time.strftime("%Y%m%d%H%M%S",time.localtime())

class Sliding(unittest.TestCase):



    def sliding_test(self):

        logger.info("滑动查找目标.")
        print(now + ":" + "滑动开始")
        count=1

        while  count<=3:
            print('第', count, "次滑动.")
            count=count+1
            logger.info("滑动查找目标")
            if d(className="android.view.View123", instance=450).exists:
                logger.info("找到目标")
                time.sleep(1)
                logger.info('查看目标属性.')
                d(className="android.view.View", instance=450).click()
                time.sleep(3)

                if d(text=u"最大宽度:").exists:
                    print(now + ":" + '查看病害属性成功')
                    time.sleep(3)

                else:
                    print(now + ":" + '查看属性成失败')
                    time.sleep(3)
                    Failed.failed_test(self)
                    time.sleep(3)
                break
            else:
                logger.info('未找到目标,继续滑动查找.')
                d.swipe_points([(0.457, 0.674), (0.465, 0.336) ], 0.08)  # 滑动界面
                time.sleep(3)


  1. 测试脚本(testsuites)
    用例脚本(test_relicl.py)
# coding=utf-8
import time
import unittest
import uiautomator2  as u2
from modules.entrance import Entrance
from framework.lock import Lock
from modules.location import Location
from modules.identification import Identification
from framework.logger import Logger

logger = Logger(logger="BrowserEngine").getlog()







d = u2.connect()
#d = u2.connect('192.168.1.173')


class Relicl(unittest.TestCase):

    #@classmethod
    def setUp(self):
        self.watcherson()
        #Lock.test_lock(self)
        print  ("----------SetUp -----\n")

    #@classmethod
    def tearDown(self):
        d.watchers.remove()
        print  ("-----------TearDown----\n")
    def watcherson(self):
        d.watcher(u'注意').when(text=u'你在此次选择后可以到“手机管理 - 权限管理”中修改此项设置').click(text=u'允许', className='android.widget.Button',resourceId='android:id/button1')
        d.watcher(u'无响应').when(text=u'关闭应用').click(text=u'关闭应用', className='android.widget.Button')
        d.watcher(u'删除').when(description=u'关闭').click(description=u'关闭', className='android.widget.ImageView',resourceId='com.android.mms:id/quick_message_close_sign')
        d.watcher(u'关闭').when(description=u'关闭').click(description=u'关闭', className='android.widget.ImageView',resourceId='com.android.systemui:id/pop_up_dialog_quick_message_close_sign')

        d.watchers.run()
        pass
    def test_entrance(self):
        Lock.lock_test(self)
        Entrance.entrance_test(self)
        while True:
            logger.info("尝试执行选择位置")
            try:
                Location.location_test(self)
                logger.info("可以执行")
                break
            except:
                logger.info("不能执行选择位置,寻找选择位置界面继续执行")
                d.press("back")

    def test_identification(self):
        logger.info("开始执行识别")
        for count in list(range(1,6)):
            Identification.identification_test(self)


启动测试代码(TestRunner.py

# coding=utf-8
import HTMLTestRunner
import os
import unittest
import time



if __name__ =='__main__':
    suite = unittest.TestLoader().discover("testsuites")
    # 初始化一个HTMLTestRunner实例对象,用来生成报告

    # 设置报告文件保存路径
    report_path = os.path.dirname(os.path.abspath('.')) + '/test_report/'
    # 获取系统当前时间
    now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
    # 设置报告名称格式
    HtmlFile = report_path + now + "HTMLtemplate.html"
    fp = open(HtmlFile, "wb")
    # 构建suite

    runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u"自动化测试报告", description=u"用例测试情况")
    # 开始执行测试套件
    runner.run(suite)

    fp.close()


运行后结果
在这里插入图片描述
测试log测试报告

发布了58 篇原创文章 · 获赞 18 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42846555/article/details/89378190
今日推荐