table of Contents
Preparing the Environment (windows)
adb devices may be obtained by deviceName
appPackage and appActivity acquisition: Connect the phone dos input
Pycharm plug Appium-Python-Client associated Appium and introducing ---- Python
HTMLTestReportCN ---- generate test reports
Main test: using a plurality of test package unittest
Data Configuration data separation yaml ----
---- Code Package Design mode PageObject
App startup configuration package
Windows tool used in the batch file to run test cases Bat
Jenkins Continuous Integration
Overall knowledge framework
Preparing the Environment (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 install and use
Official website address: https://github.com/appium/appium-desktop/releases/tag/v1.15.1
You can download and install
adb devices may be obtained by deviceName
appPackage and appActivity acquisition: Connect the phone dos input
adb shell dumpsys window | findstr mCurrentFocus
Pycharm plug Appium-Python-Client associated Appium and introducing ---- Python
HTMLTestReportCN ---- generate test reports
Download python and placed in the directory lib folder
Official website: https://github.com/findyou/HTMLTestRunnerCN
HTMLTestReportCN is unittest expand plug-in, both with the use of
The revised test codes:
Test Case two: 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)
Test Case two: 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)
Main test: using a plurality of test package 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)
Data Configuration data separation yaml ----
Table 1. Parameter Configuration: 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 file read data
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)
Log Collection
level |
When to Use |
DEBUG |
Debugging information, it is the most detailed log information. |
INFO |
Prove things work as expected. |
WARNING |
Show that there have been some accidents or problems (such as 'disk full') will occur in the near future. Or in the software work properly. |
ERROR |
Due to more serious problems, the software can not perform some of the functions. |
CRITICAL |
A serious error, indicating that the software can not continue to run. |
Define the log output position and the output format
#导入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 * * * *