Appium 简介及工作原理
开源、跨平台、原生/混合app、 Ios/Android
工作原理讲解;
Appium 环境搭建:
脚本----Appium Server ----UiAutomator---手机执行;
脚本:Python
Appium Server: Appium ----Node.js
UiAutomator: AndroidSDK----- jdk
手机执行:手机/模拟器
Appium -----Appium-Python-Client---- Python
1、jdk ; 2、AndroidSDK; 3、Node.js; 4、Appium; 5、Python ; 6、Appium-Python-Client ;
第一步:1、安装jdk1.8.0_05,安装路径记住; 2、环境变量的配置; JAVA_HOME 、 Path 、 CLASSPATH ;3、环境检验: Java -version;
第二步:AndroidSDK (完整目录:add-ons、build-tools、extras、platforms、platform-tools、system-images、tools)
1、 环境变量配置:ANDROID_HOME : Path: %ANDROID_HOME%\tools;
%ANDROID_HOME%\platform-tools;
2、环境检验:输入adb aapt
第三步:安装Node.js; 一路傻瓜式安装,安装完成后,运行cmd,输入node –v查看版本号,然后输入npm
第四步:Appium
第五步:Python(2.7)
第六步:Appium-Python-Client
Appium 运行起来
保证Appium 出现方格,显示debug 模式;脚本才可以正常运行。
Android 工具使用介绍
adb connect 127.0.0.1:62001
adb devices
aapt dump badging xxx.apk
sdk/build-tools/android-4.3/aapt.exe
查看package 和 activity 的属性:aapt d bading 路径
Appium 启动代码配置讲解
#coding = utf-8
from appium import webdriver
capabilities = {
"platformName" : "Android",
"deviceName" : "127.0.0.1:4723",
"app": "E:\\PythonAppium\\AutoTestAppium\\apps\\xxx.apk"
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",capabilities)
启动日志分析:
页面滑动及初级使用
driver.swipe(x,y,x1,y1,time)
# 获取屏幕的size
size = driver.get_window_size()
print(size)
# 屏幕宽度width
print(size['width'])
# 屏幕高度width
print(size['height'])
页面简单滑动函数封装
#获取屏幕的宽高
def get_size():
size = driver.get_window_size()
width = size['width']
heigth = size['height']
return width,heigth
#向左边滑动
def swipe_left():
x1 = get_size()[0]/10*9
y1 = get_size()[1]/2
x = get_size()[0]/10
driver.swipe(x1,y1,x,y1)
swipe_left()
参考网址:https://www.cnblogs.com/yoyoketang/p/7766878.html
driver 和滑动函数封装结合
from appium import webdriver
def get_driver():
capabilities = {
"platformName" : "Android",
"deviceName" : "127.0.0.1:21503",
"app": "E:\\PythonAppium\\AutoTestAppium\\apps\\"
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",capabilities)
return driver
#获取屏幕的宽高
def get_size():
size = driver.get_window_size()
width = size['width']
heigth = size['height']
return width,heigth
#向左边滑动
def swipe_left():
x1 = get_size()[0]/10*9
y1 = get_size()[1]/2
x = get_size()[0]/10
driver.swipe(x1,y1,x,y1)
#向右边滑动
def swipe_right():
x1 = get_size()[0]/10
y1 = get_size()[1]/2
x = get_size()[0]/10*9
driver.swipe(x1,y1,x,y1)
def login():
driver.find_element_by_id('').
driver.find_element_by_id('').
driver.find_element_by_id('').click()
#向上滑动
def swipe_up():
x1 = get_size()[0]/2
y1 = get_size()[1]/10*9
y = get_size()[1]/10
driver.swipe(x1,y1,x1,y)
#向下滑动
def swipe_down():
x1 = get_size()[0]/2
y1 = get_size()[1]/10
y = get_size()[1]/10*9
driver.swipe(x1,y1,x1,y)
def swipe_on(direction):
if direction == 'up':
swipe_up()
if direction == 'down':
swipe_down()
if direction == 'left':
swipe_left()
else:
swipe_right()
driver = get_driver()
swipe_on('left')
login()
如何切换activity 解决真机无法启动问题
"appWaitActivity":"cn.com.open.mooc.index.spain"
id 定位进行登录操作
className 定位元素
重置应用: “noReset”:"true"
element = driver.find_element_by_class_name('')
print element
elements = driver.find_elements_by_class_name('')
print len(elements)
层级定位思想分析
element = driver.find_element_by_id("cn.com.open.mooc:id/rv_child")
elements = element.find_elements_by_class_name("android.widget.RelativeLayout")
elements[1].click()
for ele in elements:
ele.click()
层级定位和list定位结合实战
element = driver.find_element_by_id('')
elements = element.find_element_by_class_name('')
Element = driver.find_element_by_class_name('android.widget.EditText')
print(Element)
elements = driver.find_elements_by_class_name('android.widget.EditText')
elements[0].send_keys('jzp')
print(len(elements))
for i in elements:
i.send_keys('jzp')
time.sleep(2)
i.send_keys('123')
time.sleep(2)
driver.find_elements_by_class_name('android.widget.EditText')[0].send_keys('jzp')
driver.find_elements_by_class_name('android.widget.EditText')[1].send_keys('123')
driver.find_element_by_class_name('android.widget.Button').click()
可以先用find_elements定位一组对象,再通过下标索引[0]取出第一个就可以了
driver.find_elements_by_class_name("android.widget.Image")[0].click()
uiautomator定位元素
driver.find_element_by_android_uiautomator('new UiSelector().index("2")').send_keys('jzp')
driver.find_element_by_android_uiautomator('new UiSelector().index("4")').send_keys('123')
通过xpath进位
driver.find_element_by_xpath('//android.widget.TextView[@text="忘记密码"]').click()
driver.find_element_by_xpath('//*[contains(@text,"忘记密码")]').click()
driver.find_element_by_xpath('//android.widget.TextView[@resource-id="cn.com.open.mooc:id/login_lable"]/../') #返回父层节点
driver.find_element_by_xpath('//android.widget.TextView[@resource-id="cn.com.open.mooc:id/login_lable"]/../preceding-sibling::android.widget.RelativeLayout').send_keys('') #返回父层节点/兄弟节点(兄弟节点的属性)
原生APP 和H5 进行相互切换代码实战
见整个页面就是一个webview,
def get_web_view():
time.sleep(10)
webview = driver.contexts
print(webview)
如何获取tost元素
from appium import webdriver
def get_driver():
capabilities = {
"platformName" : "Android",
“automationName”:"UiAutomator2" #必须添加这个
"deviceName" : "127.0.0.1:21503",
"app": "E:\\PythonAppium\\AutoTestAppium\\apps\\xxx.apk"
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",capabilities)
return driver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def get_tost():
tost_element = ("xpath","//*[contain(@text,'请输入密码')]")
WebDriverWait(driver,10,0.1).until(EC.presence_of_element_located())
读取配置文件基础代码讲解
在Python2下,需要大写:import ConfigParser
在PYthon3下,需要小写:import configparser
在new ----Directory---
工程目录下创建一个Config
创建一个 localElement.ini
[login_element]
username=email
password=pass
login_button=loginbutton
工程目录下创建一个util
创建一个 read_init.py
#conding=utf-8
import ConfigParser #在命令窗口pip install ConfigParser
class ReadIni:
def _init_(self,file_path=None):
if file_path == None:
self.file_path = '路径 + /config/LocalElement.ini'
else:
self.file_path = file_path
self.data = self.read_ini()
def read_ini(self):
read_ini = ConfigParser.ConfigParser()
data = read_ini.read('路径 + /config/LocalElement.ini')
return read_ini
#通过KEY获取对应的value
def get_value(self,key,section=None):
if section == None:
section = 'login_element'
try:
value = self.data.get(section,key)
except:
value = None
return value
if __name__ == '__main__':
read_ini = ReadIni()
print read_ini.get_value("password")
python3.6 使用查询的一些方法:
http://www.pythondoc.com/pythontutorial3/modules.html#tut-packages
import sound.effects.echo
from sound.effects import echo
from sound.effects.echo import echofilter
#coding=utf-8
import configparser
print read_ini.geet('login_element','useername')
class ReadIni:
def __init__(self,file_path=None):
if file_path == None:
self.file_path = '路径 + /config/LocalElement.ini'
else:
self.file_path = file_path
self.data = self.read_ini()
def read_ini(self):
read_ini = configparser.ConfigParser()
read_ini.read(self.file_path)
return read_ini
def get_value(self,section=None,key):
return self.data.get('login_element',key)
if __name__ == '__main__':
read_ini = ReadIni()
print read_ini.get_value("password")
封装定位信息:
在util 下面创建一个get_by_local.py
#coding=utf-8
from read_init import ReadIni
class GetByLocal:
def __init__(self,driver):
self.driver = driver
def get_element(self,key):
read_ini = ReadIni()
local = read_ini.get_value(key)
by = local.split('>')[0]
local_by = local.split('>')[1]
if by == 'id':
return self.driver.find_element_by_id(local_by)
elif by == 'className'
return self.driver.find_element_by_class_name(local_by)
else:
return self.driver.find_element_by_xpath(local_by)
分层思想
创建page 层
在page层中创建一个login_page.py
#coding=utf-8
from util.get_by_local import GetByLocal
from util.read_init import ReadIni
class loginPage:
#获取登录页面所有的页面元素信息
def __init__(self,driver):
self.driver = driver
self.get_by_local = GetByLocal(driver)
def get_username_element(self):
...
获取用户名元素信息
...
return self.get_by_local.get_element('username')
def get_password_element(self)
...
获取密码元素信息
...
return self.get_by_local.get_element('password')
def get_login_button_element(self)
...
获取登录按钮元素信息
...
return self.get_by_local.get_element('login_button')
创建handle 层
在page层中创建一个login_handle.py
#coding=utf-8
from page.login_page import loginPage
class ClassName:
def __init__(self,driver):
self.login_page = LoginPage(driver) #这里书写不一样
def send_username(self,user):
self.login_page.get_username_element().send_keys(user)
def send_password(self,password):
self.login_page.get_password_element().send_keys(password)
def click_login(self):
self.login_page.get_login_button_element().click
创建business 层
在business 层中创建一个login_business.py
#coding=utf-8
from handle.login_handle import ClassName(LoginHandle) #这里书写不一样
class LoginBusiness:
def __init__(self):
self.login_handle = LoginHandle(driver)
def login_pass(self):
self.login_handle.send_username('xxxx')
self.login_handle.send_password('111111')
self.login_handle.click_login
def login_user_error(self):
self.login_handle.send_username('xxxx')
self.login_handle.send_password('111111')
self.login_handle.click_login
unittest 的简单使用
在case 层中创建一个test_case.py
#coding=utf-8
import unittest
class CaseTest(unittest.TestCase) :
@classmethod
def setUpClass(cls):
print("this is class")
def setUp(self):
print("this is setup")
def test_01(self):
print("this is case")
def test_02(self):
print("this is case02")
def tearDown(self):
print("this is teardown")
@classmethod
def tearDownClass(cls):
print("this is class")
if __name__ == '__main__':
unittest.main()
unittest中断言的使用
unittest 的api
flag = True
self.assertEqual(1,2,'数据错误') #前2个值对比判断,第3个值输出;
self.assertNotEqual(1,2)
self.assertTrue(flag)
self.assertFalse(flag)
unittest 中case 的管理
#coding=utf-8
import unittest
class CaseTest(unittest.TestCase) :
@classmethod
def setUpClass(cls):
print("this is class")
def setUp(self):
print("this is setup")
def test_01(self):
print("this is case")
def test_02(self):
print("this is case02")
def tearDown(self):
print("this is teardown")
@classmethod
def tearDownClass(cls):
print("this is class")
if __name__ == '__main__':
#unittest.main()
suite = unittest.TestSuite()
suite.addTest(CaseTest("test_02"))
suite.addTest(CaseTest("test_01"))
unittest.TextTestRunner().run(suite)
unittest 中 HTMLTestRunner 的使用
在报告中看结果:
1)把HTMLTestRunner.py 文件下载下来,放到Python 的Lib 文件中;
2)在cmd 中运行 python ,在输入import HTMLTestRunner 检查是否报错。
在代码中如何使用:
#coding=utf-8
import unittest
import HTMLTestRunner
class CaseTest(unittest.TestCase) :
@classmethod
def setUpClass(cls):
print("this is class")
def setUp(self):
print("this is setup")
def test_01(self):
print("this is case")
def test_02(self):
print("this is case02")
def tearDown(self):
print("this is teardown")
@classmethod
def tearDownClass(cls):
print("this is class")
if __name__ == '__main__':
#unittest.main()
suite = unittest.TestSuite()
suite.addTest(CaseTest("test_02"))
suite.addTest(CaseTest("test_01"))
#unittest.TextTestRunner().run(suite)
html_file = "" #路径需要添加\\ 斜杠
fp = file(html_file,"wb") #file 有时候显示不出来需要用open
HTMLTestRunner.HTMLTestRunner(fp).run(suite)
创建report 层
在report层中创建一个report.html 文件
在html_file = "" 添加 路径+report.html
多线程的初级使用
#coding=utf-8
import threading
def sum(a)
print(a+1)
threads = []
for i in rang(3):
print i
t = threading.Thread(target=sum,args=(i,))
t.start()
for j in threads:
j.start()
多线程和unittest、HTMLTestRunner 结合使用
#coding=utf-8
import unittest
import HTMLTestRunner
class CaseTest(unittest.TestCase) :
@classmethod
def setUpClass(cls):
print("this is class")
def setUp(self):
print("this is setup")
def test_01(self):
print("this is case")
def test_02(self):
print("this is case02")
def tearDown(self):
print("this is teardown")
@classmethod
def tearDownClass(cls):
print("this is class")
def get_suite(i):
suite = unittest.TestSuite()
suite.addTest(CaseTest("test_02"))
suite.addTest(CaseTest("test_01"))
#unittest.TextTestRunner().run(suite)
html_file = "D:\\py_testt\\report\\report"+str(i)+".html" #路径需要添加\\ 斜杠
fp = file(html_file,"wb") #file 有时候显示不出来需要用open
HTMLTestRunner.HTMLTestRunner(fp).run(suite)
if __name__ == '__main__':
threads = []
for i in range(3):
print i
t = threading.Thread(target=get_suite,args=(i,))
t.start()
for j in threads:
j.start()
appium 服务介绍
卸载Appium的客户端,使用命令行操作;
appium 翻墙软件
https://blog.csdn.net/Candy_mi/article/details/80764319
如果使用输入cnpm 显示不是内部命令时,需要进入对应的文件目录运行。
npm install -g cnpm --registry=http://registry.taobao.org
cnpm
cnpm install -g appium
在命令行输入 appium 自动启动 按终止程序Ctrl c shutting down
启动成功会出现版本号:
指定端口
appium -p 4725 启动后出现端口为4725
多个设备
appium -p 4700 -bp 4701 -U 127.0.0.1:21503
命令行启动和脚本结合
检查通讯是否成功;
把配置连接放到 setUpClass(cls) 中;
保证环境可以正常运行起来;
Page 层封装driver
1、需要把login_business 引入到 test_case 中
from business.login_business import LoginBusiness
cls.login_business = LoginBusiness(driver)
self.login_business.login_pass
2、在base 层中 新建一个 base_driver.py
#coding=utf-8
import time
from appium import Webdriver
class BaseDriver:
def android_driver(self):
capabilities = {
"platformName" : "Android",
"automationName":"UiAutomator2" #必须添加这个
"deviceName" : "127.0.0.1:21503",
"app": "E:\\PythonAppium\\AutoTestAppium\\apps\\mukewang.apk"
"appWaitActivity":"cn.com.open.mooc.index.splash.MCSplashActivity"
"noReset":"true"
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",capabilities)
time.sleep(10)
return driver
3、修改login_page 中的driver
from base.base_driver import BaseDriver
def __init__(self,driver):
base_driver = BaseDriver()
self.driver = base_driver.android_driver()
business 和 handle 、login_page 去掉driver;
服务端设计思路:
封装执行命令方法:
在util 目录下创建一个dos_cmd.py 文件
#coding=utf-8
import os
class DosCmd:
def excute_cmd_result(self,command):
result_list = []
result = os.popen(command).readlines()
for i in result:
if i=='\n':
continue
result_list.append(i.strip('\n'))
return result_list
def excute_cmd(self,command):
os.system(command)
if __name__ == '__main__':
dos = DosCmd()
print dos.excute_cmd_result('adb devices')
重构封装获取设备信息类
在util 目录下创建一个server.py 文件
#coding=utf-8
from util.dos_cmd import DosCmd
class Server:
def get_devices(self):
self.dos = DosCmd()
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list)>=2:
for i in result_list:
if 'List' in i:
continue
devices_info = i.split('\t')
if devices_info[1] == 'device':
devices_list.append(devices_info[0])
return devices_list
else:
return None
if __name__ == '__main__':
server = Server()
print server.get_devices()
检查端口是否被占用
在util 目录下创建一个port.py 文件
#coding=utf-8
from util.dos_cmd import DosCmd
class Port:
def port_is_used(self,port_num):
flag = None
self.dos = DosCmd()
command = 'netstat -ano | findstr ' + port_num
result = self.dos.excute_cmd_result(command)
if len(result) > 0:
flag = True
else:
flag = False
return flag
if __name__ == '__main__':
port = Port()
print (port.port_is_used('1234'))
封装生成可用端口方法:
#coding=utf-8
from util.dos_cmd import DosCmd
class Port:
def port_is_used(self,port_num):
flag = None
self.dos = DosCmd()
command = 'netstat -ano | findstr ' + str(port_num)
result = self.dos.excute_cmd_result(command)
if len(result) > 0:
flag = True
else:
flag = False
return flag
def create_port_list(self,start_port,device_list):
'''start_port
生成可用端口
@parameter start_port
@parameter device_list
'''
port_list = []
if device_list != None:
while len(port_list) != len(device_list) :
if self.port_is_user(start_port) != True:
port_list.append(start_port)
start_port = start_port +1
return port_list
else:
print ("生成可用端口失败")
return None
if __name__ == '__main__':
port = Port()
# print (port.port_is_used('1234'))
li = [1,2,3]
print (port.create_port_list(4725,li))
封装生成启动命令行函数
使用命令生成2个端口
appium -p 4723 -bp 4724 -U 127.0.0.1:62001
在server.py 文件中设置:
#coding=utf-8
from util.dos_cmd import DosCmd
from util.port import Port
class Server:
def get_devices(self):
self.dos = DosCmd()
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list)>=2:
for i in result_list:
if 'List' in i:
continue
devices_info = i.split('\t')
if devices_info[1] == 'device':
devices_list.append(devices_info[0])
return devices_list
else:
return None
def create_port_list(self,start_port):
'''
创建可用端口
'''
port = Port()
port_list = []
port_list = port.create_port_list(start_port,self.get_devices())
return port_list
def create_command_list(self):
#appium -p 4700 -bp 4701 -U 127.0.0.1:21503
command_list = []
appium_port_list = self.creare_port_list(4700)
bootstrap_port_list = self.create_port_list(4900)
device_list = self.get_devices()
for i in range(len(device_list)):
command = "appium -p " + str(appium_port_list[i]) + " -bp " + str( bootstrap_port_list[i])+ " -U " +device_list[i]+ " --no-reset --session-override"
command_list.append(command)
return command_list
if __name__ == '__main__':
server = Server()
print server.create_command_list()
封装多线程启动appium 服务
#coding=utf-8
from util.dos_cmd import DosCmd
from util.port import Port
import threading
class Server:
def get_devices(self):
self.dos = DosCmd()
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list)>=2:
for i in result_list:
if 'List' in i:
continue
devices_info = i.split('\t')
if devices_info[1] == 'device':
devices_list.append(devices_info[0])
return devices_list
else:
return None
def create_port_list(self,start_port):
'''
创建可用端口
'''
port = Port()
port_list = []
port_list = port.create_port_list(start_port,self.get_devices())
return port_list
def create_command_list(self):
#appium -p 4700 -bp 4701 -U 127.0.0.1:21503
command_list = []
appium_port_list = self.creare_port_list(4700)
bootstrap_port_list = self.create_port_list(4900)
device_list = self.get_devices()
for i in range(len(device_list)):
command = "appium -p " + str(appium_port_list[i]) + " -bp " + str( bootstrap_port_list[i])+ " -U " +device_list[i]+ " --no-reset --session-override"
command_list.append(command)
return command_list
def start_server(self,i):
self.start_list = self.create_command_list()
self.dos.excute_cmd(self.start_list[i])
def main(self):
for i in range(len(self.create_command_list())):
appium_start = threading.Thread(target=self.start_server,args=(i,))
appium_start.start()
if __name__ == '__main__':
server = Server()
print server.main()
在任务管理器中查看Node.exe 是否启动多个(遇实际一致不)
清理 appium 环境
#coding=utf-8
from util.dos_cmd import DosCmd
from util.port import Port
import threading
class Server:
def __init__(self):
self.dos = DosCmd()
def get_devices(self):
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list)>=2:
for i in result_list:
if 'List' in i:
continue
devices_info = i.split('\t')
if devices_info[1] == 'device':
devices_list.append(devices_info[0])
return devices_list
else:
return None
def create_port_list(self,start_port):
'''
创建可用端口
'''
port = Port()
port_list = []
port_list = port.create_port_list(start_port,self.get_devices())
return port_list
def create_command_list(self):
#appium -p 4700 -bp 4701 -U 127.0.0.1:21503
command_list = []
appium_port_list = self.creare_port_list(4700)
bootstrap_port_list = self.create_port_list(4900)
device_list = self.get_devices()
for i in range(len(device_list)):
command = "appium -p " + str(appium_port_list[i]) + " -bp " + str( bootstrap_port_list[i])+ " -U " +device_list[i]+ " --no-reset --session-override"
command_list.append(command)
return command_list
def start_server(self,i):
self.start_list = self.create_command_list()
self.dos.excute_cmd(self.start_list[i])
def kill_server(self):
server_list = self.dos.excute_cmd_result('tasklist | find "node.exe"')
if len(server_list)>0:
self.dos.excute_cmd('taskkill -F -PID node.exe')
def main(self):
self.kill_server()
for i in range(len(self.create_command_list())):
appium_start = threading.Thread(target=self.start_server,args=(i,))
appium_start.start()
if __name__ == '__main__':
server = Server()
print server.main()
通过yaml文件获取命令行数据
config 层中创建一个userconfig.yaml 文件;
user_info_0: {bp: '4900',deviceName: '127.0.0.1:62001',port: '4723'}
user_info_1: {bp: '4901',deviceName: '127.0.0.1:62002',port: '4724'}
user_info_2: {bp: '4902',deviceName: '127.0.0.1:62003',port: '4725'}
util 层中创建一个write_user_command.py 文件
#coding=utf-8
import yaml #如果报错,需要下载安装pip install pyyaml
class WriteUserCommand:
def read_data(self):
with open("../config/userconfig.yaml") as fr: #路径要加双斜杠python3.6中
data = yaml.load(fr)
return data
def get_value(self,key,port):
data = self.read_data()
value = data[key][port]
return value
if __name__ == '__main__':
write_file = WriteUserCommand()
print(write_file.get_value('user_info_2','bp'))
多线程启动appium 和写入命令到文件
在write_user_command.py
#coding=utf-8
import yaml #如果报错,需要下载安装pip install pyyaml
class WriteUserCommand:
def read_data(self):
with open("../config/userconfig.yaml") as fr: #路径要加双斜杠python3.6中
data = yaml.load(fr)
return data
def get_value(self,key,port):
data = self.read_data()
value = data[key][port]
return value
def write_data(self,i,device,bp,port):
data = self.join_data(i,device,bp,port)
with open("../config/userconfig.yaml","a") as fr:
yaml.dump(data,fr)
def join_data(self,i,device,bp,port):
data = {
"user_info_"+str(i):{
"deviceName":device,
"bp":bp,
"port":port
}
}
return data
def clear_data(self):
with open("../config/userconfig.yaml","w") as fr:
fr.truncate()
fr.close()
if __name__ == '__main__':
write_file = WriteUserCommand()
print(write_file.get_value('user_info_2','bp'))
在server.py
#coding=utf-8
from util.dos_cmd import DosCmd
from util.port import Port
import threading
from util.write_user_command import WriteUserCommand 。。。。。。。。。。。。
class Server:
def __init__(self):
self.dos = DosCmd()
def get_devices(self):
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list)>=2:
for i in result_list:
if 'List' in i:
continue
devices_info = i.split('\t')
if devices_info[1] == 'device':
devices_list.append(devices_info[0])
return devices_list
else:
return None
def create_port_list(self,start_port):
'''
创建可用端口
'''
port = Port()
port_list = []
port_list = port.create_port_list(start_port,self.get_devices())
return port_list
def create_command_list(self):
#appium -p 4700 -bp 4701 -U 127.0.0.1:21503
write_file = WriteUserCommand() 。。。。。。。。。。。。。。。。。。
command_list = []
appium_port_list = self.creare_port_list(4700)
bootstrap_port_list = self.create_port_list(4900)
device_list = self.get_devices()
for i in range(len(device_list)):
command = "appium -p " + str(appium_port_list[i]) + " -bp " + str( bootstrap_port_list[i])+ " -U " +device_list[i]+ " --no-reset --session-override"
command_list.append(command)
write_file.write_data(i,device_list[i],str( bootstrap_port_list[i]),str(appium_port_list[i]),) 。。。。。。。。。。。。。。。
return command_list
def start_server(self,i):
self.start_list = self.create_command_list()
self.dos.excute_cmd(self.start_list[i])
def kill_server(self):
server_list = self.dos.excute_cmd_result('tasklist | find "node.exe"')
if len(server_list)>0:
self.dos.excute_cmd('taskkill -F -PID node.exe')
def main(self):
self.kill_server()
self.write_file.clear_data()
for i in range(len(self.create_command_list())):
appium_start = threading.Thread(target=self.start_server,args=(i,))
appium_start.start()
if __name__ == '__main__':
server = Server()
print server.main()
在server.py
#coding=utf-8
from util.dos_cmd import DosCmd
from util.port import Port
import threading
from util.write_user_command import WriteUserCommand 。。。。。。。。。。。。
class Server:
def __init__(self):
self.dos = DosCmd()
self.deivce_list = self.geet_devices()
self.write_file = WriteUserCommand() 。。。。。。。。。。。。。。。。。。
def get_devices(self):
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list)>=2:
for i in result_list:
if 'List' in i:
continue
devices_info = i.split('\t')
if devices_info[1] == 'device':
devices_list.append(devices_info[0])
return devices_list
else:
return None
def create_port_list(self,start_port):
'''
创建可用端口
'''
port = Port()
port_list = []
port_list = port.create_port_list(start_port,self.deivce_list)
return port_list
def create_command_list(self,i):
#appium -p 4700 -bp 4701 -U 127.0.0.1:21503
command_list = []
appium_port_list = self.creare_port_list(4700)
bootstrap_port_list = self.create_port_list(4900)
device_list = self.deivce_list
command = "appium -p " + str(appium_port_list[i]) + " -bp " + str( bootstrap_port_list[i])+ " -U " +device_list[i]+ " --no-reset --session-override"
command_list.append(command)
write_file.write_data(i,device_list[i],str( bootstrap_port_list[i]),str(appium_port_list[i]),) 。。。。。。。。。。。。。。。
return command_list
def start_server(self,i):
self.start_list = self.create_command_list(i)
self.dos.excute_cmd(self.start_list[0])
def kill_server(self):
server_list = self.dos.excute_cmd_result('tasklist | find "node.exe"')
if len(server_list)>0:
self.dos.excute_cmd('taskkill -F -PID node.exe')
def main(self):
self.kill_server()
self.write_file.clear_data()
for i in range(len(self.deivce_list):
appium_start = threading.Thread(target=self.start_server,args=(i,))
appium_start.start()
if __name__ == '__main__':
server = Server()
print server.main()
多线程和Unittes 和po 结合问题分析
在base_driver 中导入util.write_user_command import WriteUserCommand
#coding=utf-8
import time
from appium import Webdriver
from util.write_user_command import WriteUserCommand
class BaseDriver:
def android_driver(self,i):
write_file = WriteUserCommand()
devices = write_file.get_value('user_info_'+str(i),'deviceName')
port = write_file.get_value('user_info_'+str(i),'port')
capabilities = {
"platformName" : "Android",
"automationName":"UiAutomator2" #必须添加这个
"deviceName" : devices,
"app": "E:\\PythonAppium\\AutoTestAppium\\apps\\mukewang.apk"
"appWaitActivity":"cn.com.open.mooc.index.splash.MCSplashActivity"
"noReset":"true"
}
driver = webdriver.Remote("http://127.0.0.1:"+ port +"/wd/hub",capabilities)
time.sleep(10)
return driver
在test_case 中如何使用
在login_page 、login_handle 、login_business中需要传入i ;
多线程、unittest、启动服务逻辑串联:
#coding=utf-8
import unittest
import HTMLTestRunner
import threading
from util.server import Server
import time
from appium import webdriver
from business.login_business import loginBusiness
class ParameTestCase(unittest.TestCase)
def __init__(self,methodName='runTest',parame=None):
super(ParameTestCase,self).__init__(methodName)
global parames
parames = parame
class CaseTest(ParameTestCase) :
@classmethod
def setUpClass(cls):
print ("setUpclass------->",parames)
#cls.login_business = LoginBusiness(i)
def setUp(self):
print("this is setup")
def test_01(self):
print("test case 里面的参数", parames)
#print("this is case")
def test_02(self):
print("this is case02")
def tearDown(self):
print("this is teardown")
@classmethod
def tearDownClass(cls):
time.sleep(1)
print("this is class teardown\n")
def appium_init():
server = Server()
server.main()
def get_suite(i):
print ("get_suite里面的",i)
suite = unittest.TestSuite()
suite.addTest(CaseTest("test_02",parame=i))
#suite.addTest(CaseTest("test_01"))
unittest.TextTestRunner().run(suite)
#html_file = "D:\\py_testt\\report\\report"+str(i)+".html" #路径需要添加\\ 斜杠
#fp = file(html_file,"wb") #file 有时候显示不出来需要用open
#HTMLTestRunner.HTMLTestRunner(fp).run(suite)
if __name__ == '__main__':
appium_init()
threads = []
for i in range(3):
print i
t = threading.Thread(target=get_suite,args=(i,))
t.start()
for j in threads:
j.start()
多线程、Unittest、启动服务、流程梳理代码重构
#coding=utf-8
import unittest
import HTMLTestRunner
import threading
from util.server import Server
import time
from appium import webdriver
from business.login_business import loginBusiness
class ParameTestCase(unittest.TestCase)
def __init__(self,methodName='runTest',parame=None):
super(ParameTestCase,self).__init__(methodName)
global parames
parames = parame
class CaseTest(ParameTestCase) :
@classmethod
def setUpClass(cls):
print ("setUpclass------->",parames)
#cls.login_business = LoginBusiness(parames)
def setUp(self):
print("this is setup")
def test_01(self):
print("test case 里面的参数", parames)
#print("this is case")
def test_02(self):
print("this is case02")
def tearDown(self):
print("this is teardown")
@classmethod
def tearDownClass(cls):
time.sleep(1)
print("this is class teardown\n")
def appium_init():
server = Server()
server.main()
def get_suite(i):
print ("get_suite里面的",i)
suite = unittest.TestSuite()
suite.addTest(CaseTest("test_02",parame=i))
#suite.addTest(CaseTest("test_01",parame=i))
unittest.TextTestRunner().run(suite)
#html_file = "D:\\py_testt\\report\\report"+str(i)+".html" #路径需要添加\\ 斜杠
#fp = file(html_file,"wb") #file 有时候显示不出来需要用open
#HTMLTestRunner.HTMLTestRunner(fp).run(suite)
if __name__ == '__main__':
appium_init()
threads = []
for i in range(3):
print i
t = threading.Thread(target=get_suite,args=(i,))
t.start()
for j in threads:
j.start()
第五章 复习
appium 命令行启动命令: appium -p 4723 -bp 4724 -u 127.0.0.1:21503
获取设备信息、端口号;
1、设备信息获取方法,完成后最终结果:['127.0.0.1:62001','127.0.0.1:62025']
在util 下面创建一个dos_cmd.py 文件(如何封装去获取设备信息)
#coding=utf-8
import os
class DosCmd:
def excute_cmd_result(self,command): #第一种收集命令显示设备信息excute_cmd_result;
result_list = []
result = os.popen(command).readlines()
for i in in result:
if i == '\n':
continue
result_list.append(i.strip('\n'))
return result_list
def excute_cmd(self,command): #第二种不收集命令显示设备信息excute_cmd;
os.system(command) 使用command 原因是执行命令可能是其他命令(command 代替adb devices)
if __name__ == '__main__':
dos = DosCmd()
print(dos.excute_cmd_result('adb devices'))
#目前拿到的设备信息如下:
['List of devices attached', '127.0.0.1:62001\tdevice', '127.0.0.1:62025\tdevice']
在util 下面创建一个sever.py 文件 (设备信息进行处理需要拿到127.0.0.1:62001)
#coding=utf-8
form dos_cmd import DosCmd
class Server:
def get_devices(self):
'''
获取设备信息
'''
self.dos = DosCmd()
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list) >= 2 : #如果设备大于 等于2进行下面判断
for i in result_list:
if 'List' in i: #如果是List跳出下一个循环
continue
devices_info = i.split('\t') #下一个循环用'\t'拆分,如果等于device有效,如果等于offile无效;这里拆分完之后又是一个list[0]list[1]
if devices_info[1] == 'device' #如果list[1]==device,就把list[0]放到devices_list = []中
devices_list.append(devices_info[0])
return devices_list
else:
return None
if __name__ == '__main__':
server = Server()
print(server.get_devices())
#目前拿到的设备信息如下: ['127.0.0.1:62001','127.0.0.1:62025']
拿到设备信息还缺少端口-p -bp
2、根据设备信息获取端口方法,完成后最终结果:[4725, 4726, 4727]
cmd命令窗口检查端口是否被占用的命令 netstat -ano | findstr 4723 如果无内容出现说明端口没有被占用;
#coding=utf-8
form dos_cmd import DosCmd
class Port:
def port_is_used(self,port_num):
'''
判断端口是否被占用
'''
flag = None
self.dos = DosCmd()
command = 'netstat -ano | findstr ' + str(port_num)
result = self.dos.excute_cmd_result(command)
if len(result)>0:
flag = True #如果字符大于0,返回为True,端口占用;
else:
flag = Fasle
return flag
def create_port_list(self,start_port,device_list):
'''
生成可用端口
@parameter start_port
@parameter device_list
'''
port_list = [] #存放生成完可用端口
if devices_list != None: #判断如果设备信息不等于None,执行下列方法;如果设备信息等于None,输出“生成可用端口失败”
while len(port_list) != len(device_list): #如果端口长度不等于设备信息长度,执行下列方法;
if self.port_is_used(start_port) != True: #如果你这个端口占用的时候,增加一个端口
port_list.append(start_port)
start_port = start_port + 1
return port_list
else:
print ("生成可用端口失败")
return None
if __name__ == '__main__':
port = Port()
li = [1,2,3]
print(port.create_port_list(4723,li))
#目前拿到的端口信息如下: [4725, 4726, 4727]
3、使用多线程启动appium命令,启动之前清理环境:
appium 命令行启动命令: appium -p 4723 -bp 4724 -u 127.0.0.1:21503
分析:目前已生成端口和设备信息,然后需要生成启动命令。
在server文件中获取设备信息、创建可用端口、拼接appium命令、多线程启动appium服务、
#coding=utf-8
form dos_cmd import DosCmd
from port import Port
import threading
class Server:
def __init__(self):
self.dos = DosCmd()
def get_devices(self):
'''
获取设备信息
'''
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list) >= 2 : #如果设备大于 等于2进行下面判断
for i in result_list:
if 'List' in i: #如果是List跳出下一个循环
continue
devices_info = i.split('\t') #下一个循环用'\t'拆分,如果等于device有效,如果等于offile无效;这里拆分完之后又是一个list[0]list[1]
if devices_info[1] == 'device' #如果list[1]==device,就把list[0]放到devices_list = []中
devices_list.append(devices_info[0])
return devices_list
else:
return None
def create_port_list(self,start_port):
'''
创建可用端口
'''
port = Port()
port_list = []
port_list = port.create_port_list(start_port,self.get_devices())
return port_list
def create_command_list(self):
'''
拼接命令
'''
command_list = []
appium_port_list = self.create_port_list(4700)
bootstrap_port_list = self.create_port_list(4900) appium -p 4723 -bp 4724 -u 127.0.0.1:21503
device_list = self.get_devices()
for i in range(len(device_list)):
command = "appium -p " + str(appium_port_list[i]) + " -bp " + str(bootstrap_port_list[i]) + " -U " + device_list[i] + " --no-reset --session-override"
command_list.append(command)
return command_list
def start_server(self,i):
self.start_list = self.create_command_list()
self.dos.excute_cmd(self.start_list[i])
def kill_server(self):
server_list = self.dos.excute_cmd_result('tasklist | find "node.exe"') #查找是否有进程,如果有进程,长度大于0;
if len(server_list)>0: #如果长度
self.dos.excute_cmd('taskkill -F -PID node.exe')
def main(self):
self.kill_server()
for i in range(len(self.create_command_list())):
appium_start = threading.Thread(target= self.start_server,args=(i,))
appium_start.start()
if __name__ == '__main__':
server = Server()
print(server.main())
在config文件夹下创建一个userconfig.yaml存放数据的文件
er_info_0: {bp: '4902',deviceName: '127.0.0.1:21503',port: '4723'}
在util中创建一个write_user_command.py文件(需要从yaml 文件中拿去数据)
#coding=utf-8
import yaml
class WriteUserCommand:
def read_data(self):
'''
加载yaml数据
'''
with open("../config/userconfig.yaml") as fr:
data = yaml.load(fr)
return data
def get_value(self):
'''
获取value
'''
data = self.read_data()
value = data[key][port]
return value
def write_data(self,i,device,bp,port):
'''
写入数据
'''
data = self.join_data(i,device,bp,port)
with open("../config/userconfig.yaml","a") as fr:
yaml.dump(data,fr)
def join_data(self,i,device,bp,port):
data ={
"user_info_"+str(i):{
"deviceName":device,
"bp":bp,
"port":port
}
}
return data
def clear_data(self):
with open("../config/userconfig.yaml","w") as fr:
fr.truncate()
fr.close()
def get_file_lines(self):
data = self.read_data()
return len(data)
if __name__ == '__main__':
write_file = WriteUserCommand()
print(write_file.get_value('user_info_2','bp'))
#coding=utf-8
form dos_cmd import DosCmd
from port import Port
from write_user_command import WriteUserCommand
import threading
class Server:
def __init__(self):
self.dos = DosCmd()
self.device_list = self.get_devices()
self.write_file = WriteUserCommand()
def get_devices(self):
'''
获取设备信息
'''
devices_list = []
result_list = self.dos.excute_cmd_result('adb devices')
if len(result_list) >= 2 : #如果设备大于 等于2进行下面判断
for i in result_list:
if 'List' in i: #如果是List跳出下一个循环
continue
devices_info = i.split('\t') #下一个循环用'\t'拆分,如果等于device有效,如果等于offile无效;这里拆分完之后又是一个list[0]list[1]
if devices_info[1] == 'device' #如果list[1]==device,就把list[0]放到devices_list = []中
devices_list.append(devices_info[0])
return devices_list
else:
return None
def create_port_list(self,start_port):
'''
创建可用端口
'''
port = Port()
port_list = []
port_list = port.create_port_list(start_port,self.device_list())
return port_list
def create_command_list(self):
'''
拼接命令
'''
command_list = []
appium_port_list = self.create_port_list(4700)
bootstrap_port_list = self.create_port_list(4900) appium -p 4723 -bp 4724 -u 127.0.0.1:21503
device_list = self.device_list()
command = "appium -p " + str(appium_port_list[i]) + " -bp " + str(bootstrap_port_list[i]) + " -U " + device_list[i] + " --no-reset --session-override"
command_list.append(command)
self.write_file.write_data(i,device_list[i],str(bootstrap_port_list[i]),str(appium_port_list[i]))
return command_list
def start_server(self,i):
self.start_list = self.create_command_list(i)
self.dos.excute_cmd(self.start_list[0])
def kill_server(self):
server_list = self.dos.excute_cmd_result('tasklist | find "node.exe"') #查找是否有进程,如果有进程,长度大于0;
if len(server_list)>0: #如果长度
self.dos.excute_cmd('taskkill -F -PID node.exe')
def main(self):
self.kill_server() #杀掉进程;
self.write_file.clear_data() #清理yaml文件中的内容;
for i in range(len(self.device_list)):
appium_start = threading.Thread(target= self.start_server,args=(i,))
appium_start.start()
if __name__ == '__main__':
server = Server()
print(server.main())
多线程和Unittes 和po 结合问题分析
在base_driver 中导入util.write_user_command import WriteUserCommand
#coding=utf-8
import time
from appium import Webdriver
from util.write_user_command import WriteUserCommand
class BaseDriver:
def android_driver(self,i):
write_file = WriteUserCommand()
devices = write_file.get_value('user_info_'+str(i),'deviceName')
port = write_file.get_value('user_info_'+str(i),'port')
capabilities = {
"platformName" : "Android",
"automationName":"UiAutomator2" #必须添加这个
"deviceName" : devices,
"app": "E:\\PythonAppium\\AutoTestAppium\\apps\\mukewang.apk"
"appWaitActivity":"cn.com.open.mooc.index.splash.MCSplashActivity"
"noReset":"true"
}
driver = webdriver.Remote("http://127.0.0.1:"+ port +"/wd/hub",capabilities)
time.sleep(10)
return driver
在test_case 中如何使用
在login_page 、login_handle 、login_business中需要传入i ;
多线程、unittest、启动服务逻辑串联:
#coding=utf-8
import unittest
import HTMLTestRunner
import threading
from util.server import Server
import time
from appium import webdriver
from business.login_business import loginBusiness
from util.write_user_command import WriteUserCommand
class ParameTestCase(unittest.TestCase)
def __init__(self,methodName='runTest',parame=None):
super(ParameTestCase,self).__init__(methodName)
global parames
parames = parame
class CaseTest(ParameTestCase) :
@classmethod
def setUpClass(cls):
print ("setUpclass------->",parames)
cls.login_business = LoginBusiness(parames)
def setUp(self):
print("this is setup")
def test_01(self):
print("test case 里面的参数", parames)
#print("this is case")
def test_02(self):
print("this is case02")
def tearDown(self):
print("this is teardown")
@classmethod
def tearDownClass(cls):
time.sleep(1)
print("this is class teardown\n")
def appium_init():
server = Server()
server.main()
def get_suite(i):
print ("get_suite里面的",i)
suite = unittest.TestSuite()
suite.addTest(CaseTest("test_02",parame=i))
#suite.addTest(CaseTest("test_01"))
unittest.TextTestRunner().run(suite)
#html_file = "D:\\py_testt\\report\\report"+str(i)+".html" #路径需要添加\\ 斜杠
#fp = file(html_file,"wb") #file 有时候显示不出来需要用open
#HTMLTestRunner.HTMLTestRunner(fp).run(suite)
def get_count():
write_user_file = WriteUserCommand()
count = write_user_file.get_file_lines()
return count
if __name__ == '__main__':
appium_init()
threads = []
for i in range(get_count()):
print i
t = threading.Thread(target=get_suite,args=(i,))
t.start()
for j in threads:
j.start()