Pytest+Allure+Uiautomator2框架实现APP自动化测试

发布时间:2023年12月20日

一? 总体框架

代码框架如下:

? ??

? ? ------ run.py? 主函数,用于运行所有测试用例

? ? ------ Commom,公共函数

? ? ? ------ logger.py, 定义日志

? ? ? ------ logins.py, 提取登录功能

? ? ? ------ Operator.py, 封装操作,如点击、输入等

? ? ------ Page ,各业务流使用的元素

? ? ------ Report, 测试报告

? ? ------ TestCase, 测试用例

? ? ------ TestData, 测试数据

二? 详解

? ? 1? Operator? ? ? ??
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     Operator.py
   Author :        曾良均
   QQ:             277099728
   Date:          12/11/2023 2:32 PM   
   Description :
-------------------------------------------------
   Change Activity:
                   
-------------------------------------------------
"""
__author__ = 'ljzeng'

import re
import uiautomator2 as u2


def device():
    driver = u2.connect()
    driver.app_start("com.xxx.xxx")
    return driver


class Operator:
    driver = None

    def find_element(self, location, **kwargs):
        if str(location).startswith("com"):  # 若开头是com则使用ID定位
            return self.driver(resourceId=location)
        elif str(location).startswith("android"):
            if kwargs:
                return self.driver(className=location, index=kwargs.values())  # classname+index组合定位
            else:
                return self.driver(className=location)
        elif re.findall("//", str(location)):  # 若//开头则使用正则表达式匹配后用xpath定位
            return self.driver.xpath(location)
        else:  # 若以上两种情况都不是,则使用描述定位
            try:
                if kwargs:
                    # text+index组合定位,Fleet App上存在以图片或字体来表示功能按钮,如WO附件的图库、拍照、录像功能
                    return self.driver(text=location, index=kwargs.values())
                else:
                    return self.driver(text=location)
            except Exception:
                return self.driver(description=location)

    def click(self, location, **kwargs):
        if kwargs:
            self.find_element(location, index=kwargs.values()).click()
        else:
            self.find_element(location).click()

    def clear(self, location):
        self.find_element(location).clear_text()

    def input(self, location, txt):
        # self.clear(location)
        self.find_element(location).set_text(txt)
2? Page页面
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     assetPage.py
   Author :        曾良均
   QQ:             277099728
   Date:          12/11/2023 5:34 PM   
   Description :
-------------------------------------------------
   Change Activity:
                   
-------------------------------------------------
"""
__author__ = 'ljzeng'

from Common.Operator import *


class assetPage(Operator):
    asset_menu = "Manage Assets"

    add_btn = 'Add'  # Add 按钮
    search_inbox = '//android.widget.RelativeLayout/android.view.ViewGroup[1]/android.view.ViewGroup[2]/android.view.ViewGroup[1]/android.view.ViewGroup[2]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.widget.FrameLayout[1]/android.view.ViewGroup[1]/android.view.ViewGroup[1]'

    # Add Page
    save_btn = 'Save'

3 测试用例
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     TestAssets.py
   Author :        曾良均
   QQ:             277099728
   Date:          12/11/2023 5:30 PM   
   Description :
-------------------------------------------------
   Change Activity:
                   
-------------------------------------------------
"""
__author__ = 'ljzeng'

import time

import pytest
from Common.logger import Log
from Common.logins import logins
import uiautomator2 as u2
from Page.assetPage import assetPage

log = Log("TestAssets")


@pytest.fixture()
def begin():
    driver = u2.connect()
    driver.app_start("com.xxx.xxx")
    driver.implicitly_wait(10)
    lg = logins()
    lg.login(driver, username='admin@iicon004.com', pwd='Win.12345')
    lg.asset = assetPage()

    yield lg
    driver.app_stop("com.xxx.xxx")
    driver.app_clear("com.xxx.xxx")


@pytest.mark.usefixtures("begin")
class TestAssets:
    def add_asset(self, begin):
        time.sleep(3)
        begin.click(begin.asset.asset_menu)
        time.sleep(2)
        txt = begin.find_element(begin.asset.add_btn).get_text()
        return txt

    def test_asset(self, begin):
        res = self.add_asset(begin)
        assert res == 'Add'


if __name__ == '__main__':
    pytest.main(['-vs', 'TestAssets.py'])  # 主函数模式
4 日志封装
# -*- coding:utf8 -*-
import logging
import os
from datetime import datetime


class Log:
    def __init__(self, log_name=None):
        # 指定log保存位置
        base_path = os.path.dirname(os.path.abspath(__file__))
        fpath = os.path.abspath(os.path.join(base_path, '..\\'))
        self.log_path = os.path.join(fpath, "Logs")

        # 文件的命名
        if log_name is None:
            self.logname = os.path.join(self.log_path, '%s.log' % datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
        else:
            self.logname = os.path.join(self.log_path,
                                        '%s_%s.log' % (log_name, datetime.now().strftime('%Y_%m_%d_%H_%M_%S')))

        self.logger = logging.getLogger()
        self.logger.setLevel(logging.DEBUG)
        # 日志格式
        # self.formatter = logging.Formatter('[%(asctime)s] - %(filename)s[line:%(lineno)d] - '
        #                                    'fuc:%(funcName)s- %(levelname)s: %(message)s')
        self.formatter = logging.Formatter('[%(asctime)s] - %(levelname)s: %(message)s')

    def __console(self, level, message):
        # 创建一个FileHandler,用于写到本地
        fh = logging.FileHandler(self.logname, 'a',  encoding='utf-8')  # 追加模式
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(self.formatter)
        self.logger.addHandler(fh)

        # 创建一个StreamHandler,用于输出到控制台
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(self.formatter)
        self.logger.addHandler(ch)

        if level == 'info':
            self.logger.info(message)
        elif level == 'debug':
            self.logger.debug(message)
        elif level == 'warning':
            self.logger.warning(message)
        elif level == 'error':
            self.logger.error(message)
        # 这两行代码是为了避免输出重复问题
        self.logger.removeHandler(ch)
        self.logger.removeHandler(fh)
        # 关闭打开的文件
        fh.close()

    def debug(self, message):
        self.__console('debug', message)

    def info(self, message):
        self.__console('info', message)

    def warning(self, message):
        self.__console('warning', message)

    def error(self, message):
        self.__console('error', message)


if __name__ == "__main__":
    log = Log()
    log.info("---测试开始---")
    log.info("--------------")
    log.warning("---测试结束---")

5 主函数
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     run.py
   Author :        曾良均
   QQ:             277099728
   Date:          12/11/2023 2:30 PM   
   Description :
-------------------------------------------------
   Change Activity:
                   
-------------------------------------------------
"""
__author__ = 'ljzeng'
import pytest
import os


if __name__ == '__main__':
    pytest.main(['-s', '-q', '--clean-alluredir', '--alluredir=allure-results'])
    os.system(r"copy environment.properties allure-results\\")
    os.system(r"allure generate -c -o Report\allure-report")

三? ?运行及结果查看

? ? 运行主函数后,在对应目录运行命令:? ??

allure open .\allure-report\

? 即在浏览器打开测试报告:

四? ?说明

? ? 1 Pytest配置:pytest.ini????????
[pytest]
addopts=-vs                 
testpaths=./TestCase
python_files=Test*.py
python_classes=Test
python_functions=test
markers=
    smoke:
    uermanage:
? 2 报告的环境变量:?environment.properties
ProductName = 你的项目名称
ProductVersion = 被测对象版本号
TestFramework = Pytest+Allure

文章来源:https://blog.csdn.net/zljun8210/article/details/135112111
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。