目錄
appPackage 和appActivity 的獲取:連接手機 dos 輸入
Pycharm引入插件Appium-Python-Client----關聯Appium 和Python
整體知識框架
環境準備 (windows)
1.jdk1.8.0 (64位)
2.android-sdk(直接下載安卓studio就都有了)
3.python:3.7
4.Appium-windows-1.15.1
5.Node.js
//以上安裝並配置好環境變量
6.Appium-Python-Client
7.pycharm(用於編寫腳本)
8.HTMLTestReportCN(用於生成測試報告)
//以下可選
yaml
//以下兩個是爲了定時執行用例和發送測試報告——可不用安裝
9.Tomcat
10.Jenkins
appium安裝和使用
官網地址:https://github.com/appium/appium-desktop/releases/tag/v1.15.1
下載後安裝即可
deviceName 可通過adb devices 得到
appPackage 和appActivity 的獲取:連接手機 dos 輸入
adb shell dumpsys window | findstr mCurrentFocus
Pycharm引入插件Appium-Python-Client----關聯Appium 和Python
HTMLTestReportCN----生成測試報告
下載並放入python 目錄lib文件夾下
官網:https://github.com/findyou/HTMLTestRunnerCN
HTMLTestReportCN是unittest 拓展插件,二者配合使用
修改後的測試用例代碼 :
測試用例二:FirstTest
# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
from appium import webdriver
import time
import unittest
from HTMLTestRunnerCN import HTMLTestReportCN
caps = {}
caps["platformName"] = "Android"
caps["platformVersion"] = "10"
caps["deviceName"] = "R28M3126C2W"
caps["appPackage"] = "cn.cntv"
caps["appActivity"] = "cn.cntv.ui.activity.SplashActivity"
class FirstTest(unittest.TestCase):
def setUp(self) -> None:
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
def tearDown(self) -> None:
self.driver.quit()
def test_start(self):
el1 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
el1.click()
time.sleep(10)
el2 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_always_button")
el2.click()
time.sleep(10)
el3 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
el3.click()
time.sleep(10)
el4 = self.driver.find_element_by_id("cn.cntv:id/agree")
el4.click()
time.sleep(10)
el5 = self.driver.find_element_by_xpath(
"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.HorizontalScrollView/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.TextView")
el5.click()
el6 = self.driver.find_element_by_xpath(
"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.HorizontalScrollView/android.widget.LinearLayout/android.widget.LinearLayout[3]/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.TextView")
el6.click()
el7 = self.driver.find_element_by_xpath(
"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.HorizontalScrollView/android.widget.LinearLayout/android.widget.LinearLayout[4]/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.TextView")
el7.click()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(FirstTest)
unittest.TextTestRunner().run(suite)
測試用例二:SecondTest
# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
from appium import webdriver
import time
import unittest
caps = {}
caps["platformName"] = "Android"
caps["platformVersion"] = "10"
caps["deviceName"] = "R28M3126C2W"
caps["appPackage"] = "cn.cntv.zongyichunwan"
caps["appActivity"] = "cn.cntv.ui.activity.SplashActivity"
#TestCase類,所有測試用例類繼承的基本類
class SecondTest(unittest.TestCase):
def setUp(self) -> None:
self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)
def tearDown(self) -> None:
self.driver.quit()
def test_start(self):
el1 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
el1.click()
time.sleep(10)
el2 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
el2.click()
time.sleep(10)
el3 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
el3.click()
time.sleep(10)
el4 = self.driver.find_element_by_id("cn.cntv.zongyichunwan:id/dialog_like_ios_certain")
el4.click()
time.sleep(10)
el5 = self.driver.find_element_by_id("cn.cntv.zongyichunwan:id/btnJump")
el5.click()
time.sleep(10)
el6 = self.driver.find_element_by_xpath(
"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.LinearLayout[1]/android.widget.HorizontalScrollView/android.widget.LinearLayout/android.support.v7.app.ActionBar.Tab[2]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.TextView")
el6.click()
time.sleep(10)
el7 = self.driver.find_element_by_xpath(
"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.support.v4.view.ViewPager/android.widget.RelativeLayout/android.widget.ListView/android.widget.LinearLayout[1]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.ImageView[2]")
el7.click()
time.sleep(10)
self.driver.back()
el8 = self.driver.find_element_by_xpath(
"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.support.v4.view.ViewPager/android.widget.RelativeLayout/android.widget.ListView/android.widget.LinearLayout[1]/android.widget.LinearLayout/android.widget.TextView")
el8.click()
time.sleep(10)
self.driver.back()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(SecondTest)
unittest.TextTestRunner().run(suite)
主測試用例:使用unittest封裝多個測試用例
# import os
# os.system("python ./FirstTest.py")
# os.system("python ./SecondTest.py")
import unittest
from FirstTest import FirstTest
from SecondTest import SecondTest
import HTMLTestRunnerCN
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(FirstTest))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SecondTest))
# 確定生成報告的路徑
filePath = 'D:\ReportCN.html'
fp = open(filePath, 'wb')
# 生成報告的Title,描述
runner = HTMLTestRunnerCN.HTMLTestReportCN(stream=fp, title='自動化測試報告', description='詳細測試用例結果', tester='jackron')
runner.run(suite)
yaml數據配置----數據分離
1.參數配置表:desired_caps.yaml
platformName: Android
platformVersion: 5.1.1
deviceName: 127.0.0.1:62025
app: C:\Users\Shuqing\Desktop\Appium software\chapter4\App\kaoyan3.1.0.apk
noReset: False
appPackage: com.tal.kaoyan
appActivity: com.tal.kaoyan.ui.activity.SplashActivity
python文件進行數據讀取
rom appium import webdriver
import yaml
file=open('desired_caps.yaml','r')
data=yaml.load(file)
desired_caps={}
desired_caps['platformName']=data['platformName']
desired_caps['platformVersion']=data['platformVersion']
desired_caps['deviceName']=data['deviceName']
desired_caps['app']=data['app']
desired_caps['noReset']=data['noReset']
desired_caps['appPackage']=data['appPackage']
desired_caps['appActivity']=data['appActivity']
driver = webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub', desired_caps)
日誌收集
級別 |
何時使用 |
DEBUG |
調試信息,也是最詳細的日誌信息。 |
INFO |
證明事情按預期工作。 |
WARNING |
表明發生了一些意外,或者不久的將來會發生問題(如‘磁盤滿了’)。軟件還是在正常工作。 |
ERROR |
由於更嚴重的問題,軟件已不能執行一些功能了。 |
CRITICAL |
嚴重錯誤,表明軟件已不能繼續運行了。 |
定義日誌輸出位置和輸出格式
#導入logging模塊
import logging
logging構成
logging模塊包括logger,Handler,Filter,Formatter四個部分。
- Logger 記錄器,用於設置日誌採集。
- Handler 處理器,將日誌記錄發送至合適的路徑。
- Filter 過濾器,提供了更好的粒度控制,它可以決定輸出哪些日誌記錄。
- Formatter 格式化器,指明瞭最終輸出中日誌的格式。
Formatter
使用Formatter對象設置日誌信息最後的規則、結構和內容,默認的時間格式爲%Y-%m-%d %H:%M:%S。
格式 |
描述 |
%(levelno)s |
打印日誌級別的數值 |
%(levelname)s |
打印日誌級別名稱 |
%(pathname)s |
打印當前執行程序的路徑 |
%(filename)s |
打印當前執行程序名稱 |
%(funcName)s |
打印日誌的當前函數 |
%(lineno)d |
打印日誌的當前行號 |
%(asctime)s |
打印日誌的時間 |
%(thread)d |
打印線程id |
%(threadName)s |
打印線程名稱 |
%(process)d |
打印進程ID |
%(message)s |
打印日誌信息
|
logging.basicConfig(filename='runlog.log',level=logging.DEBUG,
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
PageObject設計模式----代碼封裝
封裝App啓動配置信息
desired_caps.py
import yaml
import logging.config
from appium import webdriver
CON_LOG = '../log/log.conf'
logging.config.fileConfig(CON_LOG)
logging = logging.getLogger()
def appium_desired():
stream = open('../yaml/desired_caps.yaml', 'r')
data = yaml.load(stream)
desired_caps={}
desired_caps['platformName']=data['platformName']
desired_caps['platformVersion']=data['platformVersion']
desired_caps['deviceName']=data['deviceName']
desired_caps['app']=data['app']
desired_caps['noReset']=data['noReset']
desired_caps['unicodeKeyboard']=data['unicodeKeyboard']
desired_caps['resetKeyboard']=data['resetKeyboard']
desired_caps['appPackage']=data['appPackage']
desired_caps['appActivity']=data['appActivity']
logging.info('start run app...')
driver = webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub', desired_caps)
driver.implicitly_wait(8)
return driver
if __name__ == '__main__':
appium_desired()
封裝基類:baseview
class BaseView(object):
def __init__(self,driver):
self.driver=driver
def find_element(self,*loc):
return self.driver.find_element(*loc)
封裝通用公共類
common_fun.py
from appium_advance.page_object.baseView import BaseView
from selenium.common.exceptions import NoSuchElementException
import logging
from selenium.webdriver.common.by import By
from appium_advance.page_object.desired_caps import appium_desired
class Common(BaseView):
cancelBtn=(By.ID,'android:id/button2')
skipBtn=(By.ID,'com.tal.kaoyan:id/tv_skip')
def check_cancelBtn(self):
logging.info("============check_cancelBtn===============")
try:
element = self.driver.find_element(*self.cancelBtn)
except NoSuchElementException:
logging.info('update element is not found!')
else:
logging.info('click cancelBtn')
element.click()
def check_skipBtn(self):
logging.info("==========check_skipBtn===========")
try:
element = self.driver.find_element(*self.skipBtn)
except NoSuchElementException:
logging.info('skipBtn element is not found!')
else:
logging.info('click skipBtn')
element.click()
if __name__ == '__main__':
driver=appium_desired()
com=Common(driver)
com.check_updateBtn()
com.check_skipBtn()
Windows 中使用批量工具Bat文件運行測試用例
@echo off
appium
pause
@echo off
D:
cd D:\study\PycharmProjects\HelloTest
start python MainTest.py
Python 啓動Appium
import subprocess
from time import ctime
def appium_start(host,port):
'''啓動appium server'''
bootstrap_port = str(port + 1)
cmd = 'start /b appium -a ' + host + ' -p ' + str(port) + ' -bp ' + str(bootstrap_port)
print('%s at %s' %(cmd,ctime()))
subprocess.Popen(cmd, shell=True,stdout=open('./appium_log/'+str(port)+'.log','a'),stderr=subprocess.STDOUT)
if __name__ == '__main__':
host = '127.0.0.1'
for i in range(2):
port=4723+2*i
appium_start(host,port)
Jenkins 持續集成
jenkins定時構建語法
* * * * *
(五顆星,中間用空格隔開)
第一個*表示分鐘,取值0~59
第二個*表示小時,取值0~23
第三個*表示一個月的第幾天,取值1~31
第四個*表示第幾月,取值1~12
第五個*表示一週中的第幾天,取值0~7,其中0和7代表的都是週日
每天下午下班前18點定時構建一次
0 18 * * *
每天早上8點構建一次
0 8 * * *
每30分鐘構建一次:
H/30 * * * *