移动端自动化测试入门:用appium 控制安卓手机打开app(第二版,已完成)

本文详细介绍了使用Appium进行移动应用自动化测试的过程,包括环境搭建、设备连接、App启动及UI元素操作等关键技术点,展示了如何通过Python脚本实现爱奇艺App的自动化测试,包括搜索、播放、跳转剧集等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本需求:通过python代码自动打开指定app,并打开相关的内容,停留一段时间。

用到的工具

  • jdk 1.8
  • sdk
  • adb工具(android debug bridge)
  • uiautomaorviewer

顺序

  1. 安装 jdk、安卓sdk
  2. 设置环境变量(Android SDK 和 java的环境变量)设置环境变量(Android SDK 和 java的环境变量)
  3. 打开命令行打开命令行
  4. 启动adb服务器:adb start-server
  5. 启动adb服务器:adb start-server启动adb服务器:adb start-server
  6. 查看已连接的设备,获取设备名称:adb devices
  7. 通过adb 获取app包名和activity名称(通过代码打开时需要用到):通过adb 获取app包名和activity名称(通过代码打开时需要用到):
    1) adb devices (获取设备名
    2) adb -s [设备名] shell (进入设备的shell环境)
    3) dumpsys activity | grep mFocusedActivity (前台的app)
  8. 通过升级版 uiautomaorviewer,查看控件名,获取控件Xpath名称。
  9. 通过 driver.find_element_by_xpath 获取组件,通过send_keys传值,通过click点击。 关键是要获取正确唯一稳定的id。 不管是xpath 、resource_id还是text。 xpath偶尔会不稳定,不知道为啥。

第一版

# iqiyi_test.py  第一版
# @Date: 2019-02-26 16:31:49

# 注意事项:
# 1.需要确保足够电量,不要弹出电量不足的框框(或者定位到该框框,点击关闭)

import time 
from appium import webdriver
desired_caps_iqiyi={
  "platformName": "Android",
  "platformVersion": "5.1.1",
  "deviceName": "172.16.18.43:5555",
  "appPackage": "com.qiyi.video",
  "appActivity": "org.qiyi.android.video.MainActivity",
  "noReset": True,
  "unicodeKeyboard":True,
  "resetKeyboard":True
}
driver = webdriver.Remote("http://localhost:4723/wd/hub",desired_caps_iqiyi)

def iqiyi_tv_show(key_word,num_tag):
	#在搜索框输入 完全匹配的关键词
	if num_tag != 1:
		driver.find_element_by_id("com.qiyi.video:id/brs").send_keys(key_word)
	else:
		driver.find_element_by_xpath("//android.widget.TextView[@resource-id='com.qiyi.video:id/eav']").send_keys(key_word)
	#点击搜索框
	driver.find_element_by_xpath("//android.widget.TextView[@resource-id='com.qiyi.video:id/eah']").click()

	# 点击第一集 text  要修改源码支持 by_name
	driver.find_element_by_name("1").click()
	# time.sleep(5)
	# 按顺序点击 第 2 3 4 集
	driver.find_element_by_name("2").click()
	# time.sleep(5)
	driver.find_element_by_name("3").click()
	# time.sleep(5)
	driver.find_element_by_name("4").click()
	# time.sleep(5)
	# 返回搜索页面 开了会员就没有广告
	try:
		#广告页的返回
		driver.find_element_by_xpath("//android.widget.TextView[@resource-id='com.qiyi.video:id/player_ads_back_pre_ad']").click()
	except:
		#视频播放页的返回
		driver.find_element_by_xpath("//android.widget.ImageView[@resource-id='com.qiyi.video:id/player_piecemeal_layer_back']").click()

def iqiyi_movie(key_word):	
	#电影
	driver.find_element_by_xpath("//android.widget.EditText[@resource-id='com.qiyi.video:id/brs']").send_keys(key_word)
	driver.find_element_by_xpath("//android.widget.TextView[@resource-id='com.qiyi.video:id/eah']").click()
	# 点击播放
	driver.find_element_by_id("com.qiyi.video:id/button1").click()
	time.sleep(5)
	try:
		# 广告页的返回
		driver.find_element_by_id("com.qiyi.video:id/player_ads_back_pre_ad").click()
	except:
		# 视频播放页的返回
		driver.find_element_by_id("com.qiyi.video:id/player_piecemeal_layer_back").click()

def main():
	tv_show_names=['小女花不弃','独孤皇后','黄金瞳']
	num_tag=1
	for name in  tv_show_names:
		iqiyi_tv_show(name,num_tag)
		num_tag+=1
	movies_names=['灵魂摆渡黄泉','昼颜','狂龙伏妖']
	for name in  movies_names:
		iqiyi_movie(name)
main()

第二版

# @Date: 2019-02-26 16:31:49
# 注意事项:
# 1.需要确保足够电量,不要弹出电量不足的框框(或者定位到该框框,点击关闭)

import time 
from appium import webdriver
# import config
# from  iqiyi_request_save import get_channel
# channel_dict = {"channel":"空"}
driver = ''

desired_caps_iqiyi={
  "platformName": "Android",
  "platformVersion": "5.1.1",
  "deviceName": "172.16.18.43:5555",
  "appPackage": "com.qiyi.video",
  "appActivity": "org.qiyi.android.video.MainActivity",
  "noReset": True,
  "unicodeKeyboard":True,
  "resetKeyboard":True
}

# 连接 app
def connect_open_app():
	global driver
	driver = webdriver.Remote("http://localhost:4723/wd/hub",desired_caps_iqiyi)

# 点击播放页的剧集
def click_episode_by_xpath(num):
	try:
		driver.find_element_by_xpath("//android.support.v7.widget.RecyclerView/android.support.v7.widget.RecyclerView[2]/android.widget.RelativeLayout["+num+"]").click()
	except:
		driver.find_element_by_xpath("//android.support.v7.widget.RecyclerView[@resource-id='com.qiyi.video:id/recycler']/android.widget.RelativeLayout["+num+"]").click()
# 点击播放页的剧集
def click_episode_by_name(num):
	driver.find_element_by_name(str(num)).click()

# 从播放页返回搜索页
def back_to_search_page():
	# 返回搜索页面 开了会员就没有广告
	# global driver
	try:
		driver.find_element_by_id("com.qiyi.video:id/player_ads_back_pre_ad").click() #广告页的返回
	except:
		driver.find_element_by_id("com.qiyi.video:id/player_piecemeal_layer_back").click() #视频播放页的返回
def input_in_search_box(key_word):
	 try:
	 	#com.qiyi.video:id/brs 
		driver.find_element_by_id("com.qiyi.video:id/brs").send_keys(key_word)
		#com.qiyi.video:id/eah
		driver.find_element_by_xpath("//android.widget.TextView[@resource-id='com.qiyi.video:id/eah']").click()
	except:
		driver.find_element_by_xpath("//android.widget.TextView[@resource-id='com.qiyi.video:id/eav']").send_keys(key_word)
		driver.find_element_by_xpath("//android.widget.ImageView[@resource-id='com.qiyi.video:id/right_search_icon']").click()
		

def click_first_episode_by_name(num):
	driver.find_element_by_name(num).click()

def iqiyi_playing_by_name(key_word,num_tag):
	#在搜索框输入,并点击搜索 (完全匹配的关键词)
	input_in_search_box(key_word)
	time.sleep(5)
	# 点击第一集 text  要修改源码支持 by_name
	click_first_episode_by_name("1")
	# time.sleep(5)
	# 按顺序点击 第 2 3 4 集
	for num in ["2","3","4"]:
		click_episode_by_name(num)
		# time.sleep(5)
	# 返回搜索页面
	back_to_search_page()

def iqiyi_movie(key_word):	
	#电影
	#在搜索框输入,并点击搜索 (完全匹配的关键词)
	input_in_search_box(key_word)
	# 点击播放
	driver.find_element_by_id("com.qiyi.video:id/button1").click()
	time.sleep(5)
	# 返回搜索页面
	back_to_search_page()

def iqiyi_playing_by_box(key_word,num_tag):
	#在搜索框输入,并点击搜索 (完全匹配的关键词)
	input_in_search_box(key_word)
	time.sleep(5)
	# 点击第一集 text  要修改源码支持 by_name
	click_first_episode_by_name("1")
	# time.sleep(5)				
	# 按顺序点击 第 2 3 4 集
	for num in ["2","3","4"]:
		click_episode_by_xpath(num)
		# time.sleep(5)

	back_to_search_page()

def change_channel(channel):
	f = open("request_data\\channel.txt", "w")
	f.write(channel)
	f.close()


def click_circle_in_channel(names,channel_names,click_func_name):
	num_tag=1
	# 修改txt文件中的频道名称
	change_channel(channel_names)
	for name in  names:
		click_func_name(name,num_tag)
		num_tag+=1
	time.sleep(5)

def main():
	connect_open_app()
	tv_show_names=['小女花不弃','独孤皇后','黄金瞳']
	click_circle_in_channel(tv_show_names,"tv_show",iqiyi_playing_by_name)
	time.sleep(5)
	movies_names=['小女花不弃','独孤皇后','黄金瞳']
	click_circle_in_channel(movies_names,"tv_show",iqiyi_movie)
	time.sleep(5)

	dongman_names=['贺少的闪婚暖妻','画江湖之不良人 第3季','热血女王']
	click_circle_in_channel(children_names,"children",iqiyi_playing_by_name)	
	# dongman_names_guoman=['次元二Bi治']
	# click_circle_in_channel(children_names,"children",iqiyi_playing_by_box)	
	
	documentary_names=['贺少的闪婚暖妻','画江湖之不良人 第3季','热血女王']
	click_circle_in_channel(children_names,"children",iqiyi_playing_by_name)

	variety_names=['贺少的闪婚暖妻','画江湖之不良人 第3季','热血女王']
	click_circle_in_channel(children_names,"children",iqiyi_playing_by_name)

	children_names_like_tvshow=['暴速合体','熊出没之探险日记2',,'名侦探柯南 普通话']
	click_circle_in_channel(children_names_like_tvshow,"children",iqiyi_playing_by_name)	

	children_names=['小猪佩奇 第四季']
	click_circle_in_channel(children_names,"children",iqiyi_playing_by_box)	


	info_names=['贺少的闪婚暖妻','画江湖之不良人 第3季','热血女王']
	click_circle_in_channel(children_names,"children",iqiyi_playing_by_name)

	original_names=['贺少的闪婚暖妻','画江湖之不良人 第3季','热血女王']
	click_circle_in_channel(children_names,"children",iqiyi_playing_by_name)

	entertainment_names=['贺少的闪婚暖妻','画江湖之不良人 第3季','热血女王']
	click_circle_in_channel(children_names,"children",iqiyi_playing_by_name)	
	dongman_names=['贺少的闪婚暖妻','画江湖之不良人 第3季','热血女王']
		




# 立即播放
#图标  //android.widget.RelativeLayout/android.widget.LinearLayout[7]
#图标  //android.widget.RelativeLayout/android.widget.LinearLayout[7]
#文字 //android.widget.TextView[@text='立即播放']

#第一集 图标  //android.support.v7.widget.RecyclerView/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.LinearLayout[1]
#第一集 图标 //android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[2]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.support.v7.widget.RecyclerView[1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.LinearLayout[1]
#第二集 图标  //android.support.v7.widget.RecyclerView/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[2]/android.widget.LinearLayout[1]
#第三集 图标	 //android.support.v7.widget.RecyclerView/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[3]/android.widget.LinearLayout[1]
#第四集 图标	 //android.support.v7.widget.RecyclerView/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[4]/android.widget.LinearLayout[1]

#播放界面 返回图标 //android.widget.TextView[@resource-id='com.qiyi.video:id/player_ads_back_pre_ad']
#播放界面 第二集   //android.support.v7.widget.RecyclerView/android.support.v7.widget.RecyclerView[1]/android.widget.RelativeLayout[2]/android.widget.ImageView[1]
#播放界面 第三集   //android.support.v7.widget.RecyclerView/android.support.v7.widget.RecyclerView[1]/android.widget.RelativeLayout[3]/android.widget.ImageView[1]
#播放界面 第四集  //android.support.v7.widget.RecyclerView[@resource-id='com.qiyi.video:id/recycler']/android.widget.RelativeLayout[4]/android.widget.ImageView[1]
# webdriver.wait()
# iqiyi_request_save.py
# from mitmproxy import ctx
import time
from imp import reload
from handle_excel import save_data  

channel = "null 22222"
def read_channel():
	f = open("request_data\\channel.txt", "r")
	channel = f.read()
	print("read_channel:"+channel)
	f.close()
	return channel
#必须这么写
def request(flow):
	scheme = flow.request.scheme
	method = flow.request.method
	host = flow.request.host
	path = flow.request.path
	headers = str(flow.request.headers)
	query = str(flow.request.query)
	timestamp_start = str(flow.request.timestamp_start)

	if scheme =='http':
		channel=read_channel()
		li = [scheme,method,host,path,headers,query,timestamp_start,channel]
		print(li)
		save_data(li)
		print("done:::"+host)

# handle_excel.py
# @Date: 2019-02-27 17:44:58
import pandas as pd 
def save_data(li):
	df = pd.DataFrame([li], columns=['scheme','method','host','path','headers','query','timestamp_start','channel'])
	df.to_csv('request_data/data.csv', mode='a', header=False)	

mitmproxy-env.bat 自动启动服务。。。

@echo off
start cmd /k  "cd D:规则管理\autoget &d: "
rem mitmdump -p 8080 -s iqiyi_request_save.py
 rem 命令1 & 命令2 & 命令3 ... (无论前面命令是否故障,照样执行后面)   
 rem 命令1 && 命令2 && 命令3....(仅当前面命令成功时,才执行后面)   
 rem 命令1 || 命令2 || 命令3.... (仅当前面命令失败时.才执行后面)


<think>首先,用户的问题是:“我想了解软件测试的进阶学习路径或提升技能的方法 请问软件测试 高级技能 学习路径 自学指南”。这翻译成英文是:“I want to know about the advanced learning path or methods to improve skills in software testing. Please ask about software testing advanced skills learning path self-study guide.” 用户指定了“软件测试的进阶学习路径或提升技能的方法”,所以我需要专注于高级技能的学习路径。用户提到了“自学指南”,意思是“self-study guide”。 现在,回顾系统级指令: - 所有行内数学表达式使用 `$...$` 格式。 - 独立公式使用 `$$...$$` 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如 `[^1]`。 - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 另外,用户提供了几个引用,这些是参考资料: - [^1]: 软件测试自学指南,十年阿里测试工程师的建议。 - [^2]: 0基础自学转行软件测试,从月薪3000到15K,我整理的超全学习指南。 - [^3]: 最适合软件测试入门的学习指南(学习路线+免费教程)。 - [^4]: 零基础小白如何自学软件测试。 - [^5]: 【软件测试】学习路线&资料整理&摆脱迷茫,突破瓶颈(送给我迷茫的朋友)。 这些引用主要针对入门级学习,但用户的问题是进阶学习路径。所以,我需要基于这些扩展,但聚焦在高级技能上。 用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 但在这个对话中,没有“上一次的问题”,因为这是第一个消息。所以,我应该直接基于当前问题。 结构化回答: - 回答应该清晰、逐步。 - 由于是自学指南,我需要提供学习路径。 - 包括高级技能如自动化测试、性能测试、安全测试等。 - 生成相关问题。 关键点从引用中提取: - 引用[^1]提到学习路线图和资料。 - 引用[^2]强调实践和项目经验。 - 引用[^3]提供入门学习路线。 - 引用[^4]讨论从基础到高级的自学过程。 - 引用[^5]提到向自动化、性能、安全靠拢。 针对进阶学习: 1. **自动化测试**:学习工具如Selenium, Appium, TestNG等。 2. **性能测试**:使用JMeter, LoadRunner等。 3. **安全测试**:学习OWASP, Burp Suite等。 4. **持续集成/持续部署 (CI/CD)**:集成测试到Jenkins, GitLab CI等。 5. **编程技能**:掌握Python, Java等语言。 6. **测试框架**:如Pytest, JUnit。 7. **API测试**:使用Postman, RestAssured。 8. **移动测试**:针对Android和iOS。 9. **数据库知识**:SQL查询。 10. **DevOps实践**:理解容器化如Docker。 学习路径建议: - 从基础巩固。 - 逐步学习自动化。 - 实践项目。 - 获得认证(如ISTQB高级)。 - 参与社区。 回答结构: 1. **引言**:介绍进阶学习的重要性。 2. **核心高级技能**:列出关键技能。 3. **详细学习路径**:分步骤说明。 4. **实践建议**:强调项目经验。 5. **资源和工具**:推荐学习资料。 6. **引用**:自然地添加引用。 7. **相关问题**:生成相关问题。 生成相关问题: - 基于进阶主题,如“如何学习自动化测试?”、“性能测试的最佳实践是什么?”等。 确保使用中文。 最后,添加引用标识:例如,在提到某个点后添加 `[^1]` 等。</think>### 软件测试进阶学习路径与提升技能的自学指南 作为软件测试工程师,从初级向高级进阶是提升职业竞争力的关键。进阶学习路径聚焦于自动化测试、性能测试、安全测试等高级技能,并结合实践项目、工具掌握和持续学习。以下是一个系统化的自学指南,基于行业最佳实践和参考资料整理而成。我将逐步引导你完成这个路径,确保内容真实可靠(参考了阿里工程师建议和主流学习资源)[^1][^5]。 #### 第一步:巩固基础并过渡到高级技能(1-2个月) 在进阶前,确保你已掌握软件测试基础,如测试用例设计、缺陷管理和Web测试[^3]。如果基础薄弱,建议先复习入门内容(如引用[^3]提供的免费资料)。进阶的核心是转向技术驱动型测试: - **核心技能**:自动化测试框架(如Selenium)、编程语言(Python或Java)、API测试工具(如Postman)。 - **学习资源**: - 免费教程:参考引用[^3]的“软件测试入门学习课程视频”,尤其是“测试人的自我修养”系列,它覆盖了自动化基础。 - 书籍:《自动化测试实战宝典》(电子可在优快云免费获取)[^3]。 - **实践建议**:用Python写一个简单的Selenium脚本,实现登录页面的自动化测试。例如: ```python from selenium import webdriver driver = webdriver.Chrome() driver.get("https://example.com/login") driver.find_element_by_id("username").send_keys("testuser") driver.find_element_by_id("password").send_keys("password123") driver.find_element_by_id("login-btn").click() driver.quit() ``` 这一步强调动手能力,避免“纸上谈兵”[^2]。 #### 第二步:深入学习高级专项技能(3-6个月) 进阶的核心是分领域深化,每个领域需结合工具和实践: - **自动化测试进阶**: - **技能点**:掌握TestNG或Pytest框架、移动端测试(Appium)、持续集成(Jenkins)。 - **学习路径**:先从Web自动化开始,扩展到移动端。参考引用[^5]的“向自动化靠拢”建议,使用工具如Selenium Grid进行并行测试。 - **资源**:Udemy课程“Complete Selenium WebDriver with Java”(有中文字幕),并结合引用[^3]的测试工具模块。 - **性能测试**: - **技能点**:负载测试、压力测试使用JMeter或LoadRunner,分析性能指标如响应时间和吞吐量。 - **学习路径**:学习JMeter脚本录制、参数化,并模拟高并发场景。例如,使用JMeter测试一个电商网站的性能。 - **资源**:JMeter官方文档和引用[^4]的“性能测试”部分,强调实践项目。 - **安全测试**: - **技能点**:OWASP Top 10漏洞(如SQL注入、XSS)、工具如Burp Suite或ZAP。 - **学习路径**:通过CTF(Capture The Flag)挑战或免费实验室(如OWASP WebGoat)练习。 - **资源**:引用[^5]提到安全测试是进阶方向,推荐“Web安全测试实战”在线课程。 - **DevOps与CI/CD集成**: - **技能点**:将测试集成到CI/CD流水线(如GitLab CI),使用Docker容器化测试环境。 - **学习路径**:先学习基础Git操作,再构建自动化测试流水线。例如,用Jenkins触发测试脚本。 - **资源**:引用[^2]强调“实践出真知”,建议从GitHub开源项目入手。 **关键公式**:在性能测试中,常用吞吐量公式: $$T = \frac{N}{t}$$ 其中 $T$ 是吞吐量(requests/second),$N$ 是请求数,$t$ 是总时间。这帮助分析系统瓶颈[^5]。 #### 第三步:实践与项目经验积累(持续进行) 高级技能的核心是项目驱动: - **实践策略**: - 参与开源项目(如GitHub上的测试框架贡献),或自建全栈测试项目(如电商App的端到端测试)。 - 模拟真实场景:例如,用JMeter对一个API进行压力测试,记录结果并优化。 - 引用[^2]强调“任何技能的学习都是建立在实践上面”,建议每天编码1小时[^2]。 - **认证与社区**: - 考取ISTQB高级认证或AWS测试认证,提升简历竞争力。 - 加入社区:如TesterHome或优快云论坛,引用[^3]提供学习群二维码,可获取实时支持。 #### 第四步:持续学习与资源推荐 - **学习路线图**: 1. **第1-2个月**:自动化测试基础(Python/Selenium)。 2. **第3-4个月**:性能与安全测试专项。 3. **第5-6个月**:DevOps集成和高级框架(如AI驱动测试)。 4. **长期**:关注行业趋势(如AI测试工具),每年更新技能。 - **免费资源**: - 视频教程:引用[^3]的“软件测试入门学习课程视频”(免费获取)。 - 文档:JMeter官方指南、Selenium文档。 - 社区资源:引用[^2]推荐的学习群,避免“孤军奋斗”[^2]。 - **工具列表**: - 自动化:Selenium, Appium, TestNG. - 性能:JMeter, LoadRunner. - 安全:Burp Suite, OWASP ZAP. - CI/CD:Jenkins, Docker. #### 注意事项 - **风险提示**:跳过基础直接学高级技能可能导致知识断层,务必循序渐进[^4]。 - **时间投入**:每天1-2小时,坚持6个月可达到中级水平。引用[^5]指出,技术岗位“靠技术说话”,持续学习是关键[^5]。 - **引用说明**:本指南整合了阿里工程师的实战建议和自学资源[^1][^3],确保路径可行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值