应用场景:安卓版移动端的自动化测试;以网易音乐APP为列
说明:demo中有两个测试用例:一个是流程测试(成功),一个用例失败时保存截图,并以附件的形式保存在allure报告中
话不多多说,直接上代码
一、以PO的分层结构

二、分别介绍各个层
1.commons层:
deal_log.py:

import logging
from datetime import datetime
from configs.config import *class Logs:# logging模块默认设置的日志级别是warning,而debug和info的级别是低于warning的,所以不会打印这两种日志信息def __init__(self, logger=None):# 1.定义日志收集器# 创建日志器logger对象以及日志级别self.logger = logging.getLogger(logger)  # 初始化日志类,定义一个日志收集器self.logger.setLevel(logging.DEBUG)  # 设置收集器的级别,不设定的话,默认收集warning及以上级别的日志# self.logger.setLevel("DEBUG")  # 也可以设置日志级别# self.logTime = datetime.now().strftime("%Y_%m_%d_%H_%M")self.logTime = datetime.now().strftime("%Y_%m_%d")self.logPath = LogPathself.logName = self.logPath + self.logTime + '.log'# self.logName = r'../logs\{}.log'.format(time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime()))  # r是防止字符转转义,保留原有的样子(注意:返回上一次的路径是两个..)# 创建处理器handler以及日志级别# conlHandler = logging.StreamHandler()  # 此处理器是输出到控制台fileHandler = logging.FileHandler(self.logName, 'a', encoding='utf-8')  # 此处理器是输出到文件# conlHandler.setLevel('DEBUG')  # 设置控制台输出日志级别ERROR 或DEBUGfileHandler.setLevel('INFO')  # 设置文件输出日志级别  ERROR'# 设置日志输出格式# %(asctime)s:打印日志的时间, %(filename)s:打印当前执行程序名, %(levelname)s:打印日志级别名称,# %(lineno)s:打印日志的当前行号, %(message)s:打印日志信息, %(name)s:Logger的名字# conlFormatter = logging.Formatter(#     "%(asctime)s-%(lineno)d-[%(levelname)s]-[msg]: %(message)s")  # 输出到控制台的格式fileFormatter = logging.Formatter("%(asctime)s-%(name)s-%(lineno)d-[%(levelname)s]-[msg]: %(message)s")  # 输出到文件中的格式# 用setFormatter()将上面设置的formatter配置到handler中#  conlHandler.setFormatter(conlFormatter)  # 配置控制台日志输出格式fileHandler.setFormatter(fileFormatter)  # 配置文件日志输出格式# 用addHandler()将配置好格式的Haddler添加到logger中,进行过滤# self.logger.addHandler(conlHandler)self.logger.addHandler(fileHandler)#  添加下面一句,在记录日志之后移除句柄# self.logger.removeHandler(ch)# self.logger.removeHandler(fh)# 关闭处理器# conlHandler.close()fileHandler.close()def get_log(self):return self.logger

deal_operate.py

# @Fuction  :主要是页面的一些操作import allure
from datetime import datetime
from appium import webdriver
from commons.deal_log import Logs
from configs.config import ImagePathclass Operates():# 初始化页面的操作def __init__(self):"""构造函数,创建必要的实例变量"""self.log = Logs().get_log()  # 初始化一个log对象# def init_driver(self):#     """#     告诉appium自动化测试相关的配置项#     :return: 返回驱动#     """caps = {# 被测APP所处平台-操作系统'platformName': 'Android',# 操作系统版本'platformVersion': '10',# 设备明后才能——可以随表填写,但是必须要有'deviceName': 'wangyimusic',# 被测APP的信息————打开某个APP后输入命令:adb shell dumpsys activity recents | findstr intent# cmd上展示的第一行命令:com.android.mediacenter/.PageActivity# 包名——代表被测app在设备上的地址'appPackage': 'com.netease.cloudmusic',# 入口信息——被测app入口'appActivity': '.activity.LoadingActivity',# 禁止app在自动化后重置'noReset': True,# 设置命令超时时间,超过后driver会关闭'newCommandTimeout': 3600,# 指定驱动——UI2,安卓5以下用uiautomator1,以上用uiautomator2'automationName': 'UiAutomator2',# 支持中文'unicodeKeyboard': True,  # 使用 Unicode 输入法'resetKeyboard': True,  # 在设定了 `unicodeKeyboard` 关键字的 Unicode 测试结束后,重置输入法到原有状态}# 启动被测试app,启动之前打开appium serverself.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', caps)  # 如果访问的是本机就用localhost或127.0.0.1, wd/hub是固定的self.driver.implicitly_wait(10)  # 设置隐士等待10s# return driverdef click(self, *locator):"""找到元素,并点击:param locator: 定位器:return:"""try:self.driver.find_element(*locator).click()except Exception:self.log.error("定位点击元素失败!")def click_ele_exist(self, *locator):"""当定为元素出现时,定位元素,并点击:param locator: 定位器:return:"""try:self.driver.find_element(*locator)btns = self.driver.find_elements(*locator)# 如果出现用户协议弹出按钮if btns:btns[0].click()except Exception:self.log.error("定位有时出现的点击元素失败!")def input_text(self, value, *locator):"""定位元素,并完成输入:param text::param locator::return:"""try:self.driver.find_element(*locator).send_keys(str(value))except Exception:self.log.info("定位输入元素失败!")def get_text(self, *locator):"""获取文本元素:param locator::return:"""try:ele = self.driver.find_element(*locator)except Exception:self.log.error("获取元素文本失败!")else:return ele.textdef getImage(self, image_name):"""生成用例失败的截图,并将截图展示到allure报告中:param image_name: 截图的名称:return:"""try:nowTime = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")NewPicture = ImagePath + '\\' + nowTime + '_' + image_name + '.png'   # 保存图片为png格式self.driver.get_screenshot_as_file(NewPicture)allure.attach.file(NewPicture, attachment_type=allure.attachment_type.PNG)  # 将截图作为附件上传到allure测试报告中except Exception:self.log.error(u'截图失败!')def getElements(self, *locator):"""根据某种方式定位到多个元素:return:"""try:eles = self.driver.find_elements(*locator)except Exception:self.log.error("定位多个元素失败!")else:return elesdef new_swap(self, start_x, start_y, end_x, end_y):"""重新疯转swap()函数滑动页面:param start_x: 当前位置的横坐标:param start_y: 当前位置的竖坐标:param end_x: 滑动后位置的横坐标:param end_y: 滑动后位置的竖坐标:return:"""try:self.driver.swap(start_x, start_y, end_x, end_y)except Exception:self.log.error("滑动页面失败!")def new_keyEvent(self, key):"""重新封装keyEvent()函数根据不同的键值,来确定系统的操作:param key::return:"""try:self.driver.keyevent(key)except Exception:self.log.error("系统键操作失败!")def quit(self):"""退出浏览器:return:"""try:self.driver.quit()except Exception:self.log.error("退出浏览器失败!")

deal_yaml.py

# 获取项目路径、项目的各环境的url,处理数据文件的方法,框架执行相关日志功能的实现方法
import os, configparser
# os: 获取操作系统级别的目录/文件夹的操作和文件的操作(读取,写入)
# configparser: 在python中的主要功能是读取配置文件config.ini# 获取config.ini路径
import yaml
from configs.config import DataPath#def congfig_path():# return os.path.split(os.path.realpath(__file__))[0].split('C')[0]# 获取当前文件的路径#curPath = os.path.dirname(os.path.realpath(__file__))# 返回config.ini路径#return os.path.join(curPath, "config.ini")# 返回config.ini中的testUrl
#def config_url(key, value):# 创建配置文件管理对象#config = configparser.ConfigParser()# 读取config.ini文件#config.read(congfig_path(), encoding="utf-8")#return config.get(key, value)def read_yaml(key):"""读取yaml文件:return:"""try:# with open(path, encoding='utf-8') as f:#     eles = yaml.safe_load(f)path = DataPath  # 配置在congfig中的相对路径# path = '../datas/test02.yaml'openYaml = open(path, 'r', encoding='UTF-8')data = yaml.load(openYaml, Loader=yaml.FullLoader)return data[key]except Exception:print(u"未找到yaml文件")

2.configs层
config.py

DataPath = "./datas/test01.yaml"  # yaml个数的测试数据的存放路径
LogPath = "./logs/"     # 日志文件存放路径
ImagePath = "./pictures/"   # 截图存放路径

3.datas层
test01.yaml

musicProcedure:  # 测试App流程
-title: APP流程成功description: 流程成功的用例,用来测试添加歌单并添加歌曲,查看添加的歌曲,最后删除歌单的流程caseDatas:stepName1: 点击“我的”按钮,进入该页面   # 也可也直接写在代码中myBtn: ['xpath','//*[@resource-id="com.netease.cloudmusic:id/bottomNav"]/android.view.ViewGroup[3]']  # 函数中定位的数据和输入的值stepName2: 创建歌单("测试歌单001")songListBtn: ['id','com.netease.cloudmusic:id/create']inputlistName: ['id','com.netease.cloudmusic:id/etPlaylistName']finishlistBtn: ['id','com.netease.cloudmusic:id/tvCreatePlayListComplete']stepName3: 进入每日推荐列表,选取两首歌曲添加到新建的歌单中discoveryBtn: ['xpath','//*[@resource-id="com.netease.cloudmusic:id/bottomNav"]/android.view.ViewGroup[1]']dailyRecommend: ['xpath','//*[@text="每日推荐"]']songNames: ['xpath','//*[@resource-id="com.netease.cloudmusic:id/daily_rv"]/android.widget.LinearLayout/android.widget.ImageView[3]']collectSong: ['xpath','//*[@text="收藏到歌单"]']listBtn: ['xpath','//*[@text="测试歌单001"]']stepName4: 查看添加的歌曲myPage: ['xpath','//*[@resource-id="com.netease.cloudmusic:id/bottomNav"]/android.view.ViewGroup[3]']checkList: ['xpath','//*[@text="测试歌单001"]']checkSong: ['id','com.netease.cloudmusic:id/songName']stepName5: 删除添加的歌单findList: ['xpath','//*[@resource-id="com.netease.cloudmusic:id/my_recycler_view"]/android.widget.FrameLayout[1]/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.ImageView']deleteBtn: ['xpath','//*[@text="删除"]']confirmBtn: ['id','com.netease.cloudmusic:id/buttonDefaultPositive']checkList:
-title: 测试歌单名不在歌单列表时失败截图处理description: 主要目的是将失败截图保存在picture中,且以附件的形式上传在allure测试报告中caseDatas:stepName1: 点击“我的”按钮,进入该页面   # 也可也直接写在代码中myBtn: ['xpath','//*[@resource-id="com.netease.cloudmusic:id/bottomNav"]/android.view.ViewGroup[3]']  # 函数中定位的数据和输入的值stepName2: 获取歌单列表歌单名listNames: ['xpath','//*[@resource-id="com.netease.cloudmusic:id/my_recycler_view"]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.TextView']

4.test_cases层
conftest.py

import pytest@pytest.fixture(scope='session')
def setListName():Name = '测试歌单001'print('获取歌单的名字---')yield Name

test_app.py


import allure
import pytest
import time
from commons.deal_log import Logs
from commons.deal_operate import Operates
from commons.deal_yaml import read_yamlclass Test_WangYiMusic:def setup_class(self):"""构造函数,创建类象时会执行:return:"""print("执行setup_class-------")self.baseDriver = Operates()  # 创建一个对象self.log = Logs().get_log()  # 获取一个对象的get_log()函数def teardown_class(self):"""执行完类中的测试用例后进行的清理工作:param self::return:"""print("结束teardown_class—————————")self.baseDriver.quit()# 测试网易音乐APP某个流程: 建立一个新的歌单(测试歌单)- 进入“每日精选”-选取其中的两首歌曲,添加到新建的歌单中-进入歌单查看歌曲是否成功-最善删除新建的歌单@allure.story('网易音乐APP的成功流程')  # 同一个分组,比如成功和失败@pytest.mark.parametrize('data', read_yaml('musicProcedure'))def test_appPocedure(self, data, setListName):self.log.info("执行---test_appPocedure------")allure.dynamic.title(data['title'])  # allure:获取yaml文件中的titleallure.description(data['description'])   # allure: 获取yaml文件中的descriptionprint('获取data的title数据:------', data['title'])caseData = data['caseDatas']listName = setListName# 1.进入“我的”页面with allure.step(caseData['stepName1']):self.baseDriver.click(*caseData['myBtn'])# 2、创建新的歌单,并返回到 我的  页面with allure.step(caseData['stepName2']):# 2.1、点击 创建歌单self.baseDriver.click(*caseData['songListBtn'])# 2.2、输入歌单名称——测试歌单self.baseDriver.input_text(listName, *caseData['inputlistName'])time.sleep(2)# 2.3、点击 提交按钮self.baseDriver.click(*caseData['finishlistBtn'])# 2.4、回到我的,利用系统返回键time.sleep(1)self.baseDriver.new_keyEvent(4)# 3、进入每日推荐列表,选取两首歌曲添加到新建的歌单中with allure.step(caseData['stepName3']):# 3.1、点击发现按钮self.baseDriver.click(*caseData['discoveryBtn'])# 3.2、进入每日推荐self.baseDriver.click(*caseData['dailyRecommend'])# 3.3、前三首歌曲添加到测试歌单# 获取前三首歌曲的添加操作菜单按钮,然后重复添加歌曲过程time.sleep(2)print('开始选择两首歌曲')options = self.baseDriver.getElements(*caseData['songNames'])[3:5]  # 列表前两个元素,可以通过切片ele[:3] (切片选取)for option in options:# 点击菜单print("开始执行循环")time.sleep(2)option.click()time.sleep(2)self.baseDriver.click(*caseData['collectSong'])# print("点击测试歌单")time.sleep(1)self.baseDriver.click(*caseData['listBtn'])time.sleep(1)# 4 查看添加的歌曲with allure.step(caseData['stepName4']):# 4.1、返回到 发现 页面self.baseDriver.new_keyEvent(4)# 4.2、进入 我的 页面self.baseDriver.click(*caseData['myPage'])# 4.3、 点击 测试歌单001time.sleep(1)self.baseDriver.click(*caseData['checkList'])time.sleep(2)# 4.4、 查看歌曲,获取所有歌名songs = self.baseDriver.getElements(*caseData['checkSong'])time.sleep(5)print('歌单内的歌曲信息是:')for song in songs:print(song.text)# 5、删除创建的歌单——测试歌单002with allure.step(caseData['stepName5']):# 5.1 返回self.baseDriver.new_keyEvent(4)time.sleep(3)# 5.2 点击第一个新建的歌单的选线——测试歌单002self.baseDriver.click(*caseData['findList'])time.sleep(1)# 5.3 点击删除选项self.baseDriver.click(*caseData['deleteBtn'])# 5.4 点击删除确认弹框上的删除按钮time.sleep(2)self.baseDriver.click(*caseData['confirmBtn'])@allure.story('将失败用例进行截图:歌单列表中没有该歌单')  # 同一个分组,比如成功和失败@pytest.mark.parametrize('data', read_yaml('checkList'))def test_checkListName(self, data):self.log.info("执行---test_checkListName------")allure.dynamic.title(data['title'])  # allure:获取yaml文件中的titleallure.description(data['description'])   # allure: 获取yaml文件中的descriptionprint('获取data的title数据:------', data['title'])caseData = data['caseDatas']# 1.进入“我的”页面with allure.step(caseData['stepName1']):self.baseDriver.click(*caseData['myBtn'])# 2、检核个订单列表中是否有该歌单with allure.step(caseData['stepName2']):print('进入歌单列表页')try:listNames = self.baseDriver.getElements(*caseData['listNames'])tempList = []for listName in listNames:tempList.append(listName.text)print(tempList)assert 'music' in tempList  # 失败用例except AssertionError:time.sleep(1)print('执行失败:执行截图操作')self.baseDriver.getImage('失败用例的截图')  # 添加失败截图的功能assert Falseelse:print("没有捕捉到异常")assert True

5、run层

# !/usr/bin python3
# encoding  : utf-8 -*-
# @author   : Shan Shan
# @software : PyCharm
# @file     : run.py
# @Time     : 2021/6/8 16:21import os
import pytest# pytest.main(["test_cases/test_procedure002.py", '-s'])
pytest.main(['-s', 'test_cases/test_app.py', '--alluredir', './temp'])  # 生成allure报告,并放在./temp
os.system('allure generate ./temp -o ./reports --clean')

6、执行后,allure报告

移动端自动化测试:python+appium+pytest+allure+yaml相关推荐

  1. python+requests+pytest+allure+yaml+DDT+logs 接口自动化框架使用手册

    一.单条测试用例 无ddt数据驱动的场景 1 config.yaml 中书写基础路径 2 在redloads模块中,新建一个demo_fun.py文件, 其中demo要用的方法写在其中 如:读取con ...

  2. Appium+Pytest+Allure集成PO项目管理模式实现自动化测试

    Appium+Pytest+Allure集成PO项目管理模式实现自动化测试 环境配置 Appium环境配置 Pytest环境配置 Allure环境配置 使用与集成 Appium使用 Pytest使用 ...

  3. 2022超级好用的接口自动化测试框架:基于python+requests+pytest+allure实现

    众所周知,目前市面上大部分的企业实施接口自动化最常用的有两种方式: 1.基于工具类的接口自动化,如: Postman+Newman+Jenkins+Git/svn Jmeter+Ant+Jenkins ...

  4. 接口自动化测试框架:python+requests+pytest+allure实现

    接口自动化测试框架 一.接口自动化测试框架需要解决的问题 二.接口自动化测试框架目录结构 三.日志监控文件的信息 四.搭建具有企业Logo的定制化报告.    今年是以往10年中最坏的一年,是未来10 ...

  5. 接口自动化测试框架搭建:基于python+requests+pytest+allure实现

    目录 一.接口自动化测试框架需要具备什么功能? 二.接口自动化测试框架目录结构 三.日志监控文件的信息 四.搭建具有企业Logo的定制化报告. 众所周知,目前市面上大部分的企业实施接口自动化最常用的有 ...

  6. CSDN绝无仅有只此一篇:Appium+pytest+allure+jenkins如何实现多台手机连接详细教程教学

    使用appium可以实现app自动化测试,我们之前是连接一台手机去运行, 如何同时连接多台手机呢? 很多人可能想到的是多线程(threading). 今天分享一种比多线程更简单的方法,虽然不是多台手机 ...

  7. 2022软件测试技能 APP自动化测试 Python+Appium+Uiautomator2 实战教程

    系列文章目录 提示:阅读本章之前,请先阅读目录 文章目录 系列文章目录 前言 一.Appium 原理 二.环境搭建,一键搞定 1. 安装Java JDK 2. Android SDK 安装与配置 3. ...

  8. 移动端自动化测试工具 Appium 快速入门

    文章目录 一.前言 二.Appium 概述 1.架构图 2.UI 自动化收益 三.环境安装 1.桌面版本安装 2.DOS命令安装 3.安装SDK 四.常用操作方法 五.常见定位方式 1.ID 定位 2 ...

  9. 【python数据驱动+接口自动化测试】pytest+allure+yaml+jenkins+git(gitlab/gitee)下的接口自动化测试实战

    大家好,我是好学的小师弟.今天和大家分享下我前段时间的工作学习心得-接口自动化测试及其全套工作流程. 注:本文的侧重点在于工作流程,代码讲解.工具安装步骤方面可能就浅尝辄止了. 目录 前言: 工作流程 ...

  10. 接口自动化测试框架开发 | Pytest+Allure+AIOHTTP+用例自动生成

    测试开发实战技能进阶学习,文末加群! 近期准备优先做接口测试的覆盖,为此需要开发一个测试框架,经过思考,这次依然想做点儿不一样的东西. 接口测试是比较讲究效率的,测试人员会希望很快能得到结果反馈,然而 ...

最新文章

  1. mysql与ofbiz,ofbiz+mysql安装求教
  2. python函数后面有多个括号怎么理解?
  3. android 运行在ui县城,Android基础:在UI线程中运行代码
  4. 如何搭建一套完整的深度学习系统?
  5. 3-2 :eq(index)过滤选择器
  6. 如何选择合适的BI工具
  7. hibernate映射(一对一、一对多、多对一、多对多)配置 【转】
  8. iOS NSURLSession
  9. jsp+servlet+mysql的简单使用
  10. 计算机专业助我成长作文600,我在成长作文
  11. 《机器视觉算法与应用》第3章 机器视觉算法之光学字符识别(OCR)——学习笔记
  12. Android 四大组件
  13. python 批量打开网页并截图_如何实现批量截取整个网页完整长截图,批量将网页保存成图片web2pic/webshot/screencapture/html2picture...
  14. logistic人口模型python代码_用Matlab程序对logistic人口模型进行拟合?
  15. 使用JOPENS-MSDP系统进行简单的地震定位
  16. 设计师的“通天塔”—浅谈设计沟通
  17. solr和elasticsearch
  18. 网页中无法复制的文字如何复制
  19. html在字体两边加直线,css怎么在文字两边加上横线
  20. HTML语言中代表网页标题的标签是,html标题标记 在html中,标题标签一共有几级?

热门文章

  1. 基于扩频信号的水声信道数据传输系统仿真,研究满足了WSSUS假设的瑞利信道模型,采用相干BPSK调制,联合多普勒Rake接收机
  2. lcd1602c语言程序分析,LCD1602 C程序
  3. Trained Tesseract on 瘦金体 successfully!!
  4. xlsx表格怎么做汇总统计_用excel表格统计数据-如何将多个EXCEL表格的数据进行汇总?...
  5. 网络掩码 网关 接口 活跃点数介绍
  6. linux小米随身wifi驱动下载,小米随身wifi驱动
  7. AOP与AspectJ的关系
  8. 成吉思汗1怀旧版 一键端 win服务端
  9. DevExpress TreeList GridView 样式设置
  10. xml文件导入wps_Office12使用XML格式存储文件回击WPS