移动端自动化测试:python+appium+pytest+allure+yaml
应用场景:安卓版移动端的自动化测试;以网易音乐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相关推荐
- python+requests+pytest+allure+yaml+DDT+logs 接口自动化框架使用手册
一.单条测试用例 无ddt数据驱动的场景 1 config.yaml 中书写基础路径 2 在redloads模块中,新建一个demo_fun.py文件, 其中demo要用的方法写在其中 如:读取con ...
- Appium+Pytest+Allure集成PO项目管理模式实现自动化测试
Appium+Pytest+Allure集成PO项目管理模式实现自动化测试 环境配置 Appium环境配置 Pytest环境配置 Allure环境配置 使用与集成 Appium使用 Pytest使用 ...
- 2022超级好用的接口自动化测试框架:基于python+requests+pytest+allure实现
众所周知,目前市面上大部分的企业实施接口自动化最常用的有两种方式: 1.基于工具类的接口自动化,如: Postman+Newman+Jenkins+Git/svn Jmeter+Ant+Jenkins ...
- 接口自动化测试框架:python+requests+pytest+allure实现
接口自动化测试框架 一.接口自动化测试框架需要解决的问题 二.接口自动化测试框架目录结构 三.日志监控文件的信息 四.搭建具有企业Logo的定制化报告. 今年是以往10年中最坏的一年,是未来10 ...
- 接口自动化测试框架搭建:基于python+requests+pytest+allure实现
目录 一.接口自动化测试框架需要具备什么功能? 二.接口自动化测试框架目录结构 三.日志监控文件的信息 四.搭建具有企业Logo的定制化报告. 众所周知,目前市面上大部分的企业实施接口自动化最常用的有 ...
- CSDN绝无仅有只此一篇:Appium+pytest+allure+jenkins如何实现多台手机连接详细教程教学
使用appium可以实现app自动化测试,我们之前是连接一台手机去运行, 如何同时连接多台手机呢? 很多人可能想到的是多线程(threading). 今天分享一种比多线程更简单的方法,虽然不是多台手机 ...
- 2022软件测试技能 APP自动化测试 Python+Appium+Uiautomator2 实战教程
系列文章目录 提示:阅读本章之前,请先阅读目录 文章目录 系列文章目录 前言 一.Appium 原理 二.环境搭建,一键搞定 1. 安装Java JDK 2. Android SDK 安装与配置 3. ...
- 移动端自动化测试工具 Appium 快速入门
文章目录 一.前言 二.Appium 概述 1.架构图 2.UI 自动化收益 三.环境安装 1.桌面版本安装 2.DOS命令安装 3.安装SDK 四.常用操作方法 五.常见定位方式 1.ID 定位 2 ...
- 【python数据驱动+接口自动化测试】pytest+allure+yaml+jenkins+git(gitlab/gitee)下的接口自动化测试实战
大家好,我是好学的小师弟.今天和大家分享下我前段时间的工作学习心得-接口自动化测试及其全套工作流程. 注:本文的侧重点在于工作流程,代码讲解.工具安装步骤方面可能就浅尝辄止了. 目录 前言: 工作流程 ...
- 接口自动化测试框架开发 | Pytest+Allure+AIOHTTP+用例自动生成
测试开发实战技能进阶学习,文末加群! 近期准备优先做接口测试的覆盖,为此需要开发一个测试框架,经过思考,这次依然想做点儿不一样的东西. 接口测试是比较讲究效率的,测试人员会希望很快能得到结果反馈,然而 ...
最新文章
- mysql与ofbiz,ofbiz+mysql安装求教
- python函数后面有多个括号怎么理解?
- android 运行在ui县城,Android基础:在UI线程中运行代码
- 如何搭建一套完整的深度学习系统?
- 3-2 :eq(index)过滤选择器
- 如何选择合适的BI工具
- hibernate映射(一对一、一对多、多对一、多对多)配置 【转】
- iOS NSURLSession
- jsp+servlet+mysql的简单使用
- 计算机专业助我成长作文600,我在成长作文
- 《机器视觉算法与应用》第3章 机器视觉算法之光学字符识别(OCR)——学习笔记
- Android 四大组件
- python 批量打开网页并截图_如何实现批量截取整个网页完整长截图,批量将网页保存成图片web2pic/webshot/screencapture/html2picture...
- logistic人口模型python代码_用Matlab程序对logistic人口模型进行拟合?
- 使用JOPENS-MSDP系统进行简单的地震定位
- 设计师的“通天塔”—浅谈设计沟通
- solr和elasticsearch
- 网页中无法复制的文字如何复制
- html在字体两边加直线,css怎么在文字两边加上横线
- HTML语言中代表网页标题的标签是,html标题标记 在html中,标题标签一共有几级?
热门文章
- 基于扩频信号的水声信道数据传输系统仿真,研究满足了WSSUS假设的瑞利信道模型,采用相干BPSK调制,联合多普勒Rake接收机
- lcd1602c语言程序分析,LCD1602 C程序
- Trained Tesseract on 瘦金体 successfully!!
- xlsx表格怎么做汇总统计_用excel表格统计数据-如何将多个EXCEL表格的数据进行汇总?...
- 网络掩码 网关 接口 活跃点数介绍
- linux小米随身wifi驱动下载,小米随身wifi驱动
- AOP与AspectJ的关系
- 成吉思汗1怀旧版 一键端 win服务端
- DevExpress TreeList GridView 样式设置
- xml文件导入wps_Office12使用XML格式存储文件回击WPS