当前位置: 代码网 > it编程>前端脚本>Python > 学会这篇至少涨薪10K:appium+python+jenkins自动化测试框架持续集成_python+appium+jenkins

学会这篇至少涨薪10K:appium+python+jenkins自动化测试框架持续集成_python+appium+jenkins

2024年08月02日 Python 我要评论
(2) Nose提供了递归查找测试套件的功能,而unitest是代码中通过调用unitest库的testrun的方法去执行当前模块下的测试用例(代码层面控制不方便),nose中只需要通过nose命令就可以递归的寻找python文件,通过正则匹配的方式发现test开头或者包含test的文件、方法,执行测试。(2)对于appium来说,可存放端口号(比如appium启动端口4723,如果并发测试,启动多个server需要管理多个端口号)和一些全局控制参数(封装好的点击的方法,错误时截图)。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事it行业的老鸟或是对it行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

(3)text

find_element_by_accessibility_id(u’美食’)

优先匹配content-desc,然后匹配resourceid,最后匹配text

图片


(四)appium自动化测试框架介绍与搭建

4.1 框架的几大要素

自动化通用要素(web/接口/app):

  • 数据/环境管理 —数据如何维护(测试环境/线上环境账号、数据的不同、维护),环境切换(测试环境/线上环境/预发布环境)
  • log日志 —appium server的日志,脚本代码print的日志、框架运行的日志、手机运行时的日志
  • 通用方法(util—在数据、环境的处理上,对数据库的操作、文件的读写等较常用的方法
  • 框架运行机制—框架是按照什么样的顺序运行,appium server何时启动、何时得到设备、用例testcase套件怎么跑等
  • result报表 —测试用例运行完毕后,查看运行的信息、错误的失败的情况进行报告解析

android****自动化特有的驱动要素:

  • appium server****管理 —如果有很多的设备、很多的用例,通过管理不同的appium server端口、appium server对应的设备号、appium server不同的启动状态
  • driver****管理 —即session,每一个session可能对应不同的desired capibilities,对应不同的场景,是否每个用例脚本中要见session都需要考虑,对测试效率有影响
  • 元素对象管理 —比如pageobject设计模式
  • android****设备管理 —结合appiumserver管理使用,关注设备是否空闲,比如把100个用例分发到不同设备跑,提高效率,对android兼容性测试也有帮助

4.2 框架的分层思想

开发者角度:

测试套件(测试框架junit/testng/unitest)—本次使用nose的测试框架(基于unitest),解决数据运行的管理、解决框架的运行机制、result报表(java推荐testng,python推荐用nose)

server —包括设备和appium管理,才能对并发测试进行很好分配(需要了解多线程和线程锁等)

脚本编写角度:

脚本(testcase)—只写testcase,不要写数据

数据(data)

元素(element)

三个层面不能掺杂,脚本调用数据,使用时,只需要把数据填充到一个地方,脚本调用就ok,数据变动不会影响到脚本

4.3 如何持续集成

什么是持续集成

ci平台(continuous integration)

包含要素:

统一的代码平台

自动触发构建、完成测试得出报告

提交代码会触发构建

持续集成应该是一个完整的方案

图片

实战内容:

  • python版本:

基于python+nose+nose-testconfig(管理数据配置)+logging(日志系统)+appium +jenkins+svn(git)

  • java版本:

java+testng+reportng+maven+appium+svn(git)

4.4 环境搭建

  • java环境:jdk1.8.0_151
  • android开发环境:adt-bundle-windows-x86_64-20140702
  • python环境:python2.7.9
  • appium环境:nodejs v5.6.0,appium1.3.4
  • 脚本开发ide:pycharm
  • 其他组件:nose&&nose-testconfig,selenium,appium-python-client,nosehtmloutput-2,nose-html-reporting插件等

环境搭建网上都有很多资料,此处不再赘述。

4.5 nose框架介绍

(1)   继承自unitest,比unitest更加简单,功能更强大

(2)   nose提供了递归查找测试套件的功能,而unitest是代码中通过调用unitest库的testrun的方法去执行当前模块下的测试用例(代码层面控制不方便),nose中只需要通过nose命令就可以递归的寻找python文件,通过正则匹配的方式发现test开头或者包含test的文件、方法,执行测试。

nosetest支持较多方便实用的插件,比如html报告的生成等集成插件

(3)   支持setup、teardown函数

有四种作用域:

  • package层
  • module层
  • class层
  • function层

(4)可生成xml报告或html报告

例子如下:

图片

_init_.py文件

def setup():
    print 'package setup'
def teardown():
    print 'package teardown'

testmycase.py文件


4.6 框架数据的管理:

全局数据(影响到框架运行流程的数据)-----通过testconfig文件来实现支持

(1)存放通用性的全局数据,常见的如域名地址,接口测试时的测试/线上环境等域名是不同的;

(2)对于appium来说,可存放端口号(比如appium启动端口4723,如果并发测试,启动多个server需要管理多个端口号)和一些全局控制参数(封装好的点击的方法,错误时截图)。

局部数据(不会影响到框架运行的全局数据) —通过html或excel表格来获取

比如发文章、评论的时用的数据

私有数据 —写到代码模块特定用例会用到的数据

nose-testconfig的使用(全局数据的一个插件)

①在.cfg文件中声明testconfig文件目录;

[nosetests]
;--with-unit output xml report
with-html-output=true
html-out-file=result1.html
tests=testcase/testlogin/testlogin.py
nocapture=true
verbose=true

tc-format=python
tc-file=conf/env/testconfig.py

;exclude=testcase/testmycase.py

②定义config字典,字典中可以填入全局变量

global config
config = {}

config['packagename'] = 'com.hwd.test'
config['all']  = 'acj'

config['appiumport'] = 4723
config['selendroidport'] = 8120
config['bootstrapport'] = 4750
config['chromiumport'] = 9553

config['app'] = r'd:\pycharmworkspace\demoproject\src\testdata\xxxx-release-v4.2.0-2018-03-07.apk'
config['apppackage'] = 'com.cashregisters.cn'
config['appactivity'] ='com.hkrt.qpos.presentation.screen.welcome.welcomeactivity'

config['appwaitactivity'] = ''

③在自动化脚本中导入模块,并使用变量

#coding:utf-8
from appium import webdriver
from testconfig import config
import logging,time

class testlogin:
    def __init__(self):
        self.logger =logging.getlogger(__name__)

    def setup(self):
        desired_caps = {}
        desired_caps['platformname'] = 'android'
        desired_caps['platformversion'] = '6.0.0'
        desired_caps['devicename'] = 'twgdu1700002279'
        desired_caps['app'] = config['app']
        desired_caps['apppackage'] = config['apppackage']
        desired_caps['appactivity'] = config['appactivity']
        desired_caps['noreset'] = 'true'
        desired_caps['unicodekeyboard'] = 'true'
        desired_caps['resetkeyboard'] = 'true'
        if config['appwaitactivity'] != none:
            desired_caps['appwaitactivity'] = config['appwaitactivity']

        self.logger.info('session starting...')

        self.driver =webdriver.remote('http://localhost:4723/wd/hub',desired_caps)
    def teardown(self):
        self.logger.info('quit!')
        self.driver.quit()


    def testlogin(self):
        self.logger.info('test logining!!!')
        self.driver.implicitly_wait(10)
        userfiled =self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id')
        pwdfiled =self.driver.find_element_by_id('com.cashregisters.cn:id/password_id')
        userfiled.send_keys('18610000920')
        pwdfiled.send_keys('1234qwer')
       self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id').click()
       self.driver.find_element_by_id('com.cashregisters.cn:id/loginbutton').click()
        time.sleep(2)

图片

图片


(五)框架中的appiumserver模块设计与编写

appium server的启动方式

(1)使用appium.exe(windows)/appium.app(mac)

(2)通过代码控制命令行启动,通过下面例子可以看明白

例子:

①appium.py文件编写appium的启动模块

import logging,os,time
import subprocess
from testconfig import config

class appium(object):

    def __init__(self):
        self.logger = logging.getlogger(__name__)

    def start(self):
        self.logger.info('starting appium server ...')
        currenttime = time.strftime('%y%m%d%h%m%s',time.localtime())

        udid = self.getudid()
        appiumport = config['appiumport']
        bootstrapport = config['bootstrapport']
        selendroidport = config['selendroidport']
        chromiumport = config['chromeport']
        logpath = os.path.abspath(os.path.join(os.getcwd(),'log','as'+currenttime+'.log'))
        try:
            self.process = subprocess.popen('appium --port={} --bootstrap-port={} --selendroid-port={} --chromedriver-port={}'
                                            '--log={} -u{}'.format(appiumport,bootstrapport,selendroidport,chromiumport,logpath,udid),
                                            stdout=subprocess.pipe,
                                            shell=true)
        except exception,e:
            self.logger.error('start appium server failed!')
            self.logger.error('errormsg:{}'.format(e))
        time.sleep(5)

    def stop(self):
        self.logger.info('stop appium server.')
        self.process.kill()
        os.system('taskkill /im node.exe /f')

    def getudid(self):
        'adb devices'
        cmd = 'adb devices'
        output = subprocess.popen(cmd,stdout=subprocess.pipe,shell=true)
        infolist = output.stdout.read().strip('list of devices attached').split()
        deviceslist = []
        if infolist != 0:
            for deviceinfo in infolist:
                if deviceinfo != 'devices':
                    deviceslist.append(deviceinfo)
        return deviceslist[0]

②testconfig.py文件设置启动端口

global config
config = {}

config['packagename'] = 'com.hwd.test'
config['all']  = 'acj'

config['port'] = 4723
config['selendroidport'] = 8120
config['bootstrapport'] = 4750
config['chromeport'] = 9553

③用例中初始化启动文件__init__.py

#coding:utf-8
import logging
from src.appiumserver  import appiumserver

logger = logging.getlogger(__name__)

def setup(self):
    logger.info(u'启动appium server')
    appiumserver().start()

def teardown(self):
    logger.info(u'关闭appium server')
    appiumserver().stop()

④编写测试用例

#coding:utf-8
from appium import webdriver
from testconfig import config
import logging,time

class testlogin:
    def __init__(self):
        self.logger =logging.getlogger(__name__)

    def setup(self):
        desired_caps = {}
        desired_caps['platformname'] = 'android'
        desired_caps['platformversion'] = '6.0.0'
        desired_caps['devicename'] = 'twgdu17000002279'
        desired_caps['app'] = config['app']
        desired_caps['apppackage'] = config['apppackage']
        desired_caps['appactivity'] = config['appactivity']
        desired_caps['noreset'] = 'true'
        desired_caps['unicodekeyboard'] = 'true'
        desired_caps['resetkeyboard'] = 'true'
        if config['appwaitactivity'] != none:
            desired_caps['appwaitactivity'] = config['appwaitactivity']

        self.logger.info('session starting...')

        self.driver =webdriver.remote('http://localhost:4723/wd/hub',desired_caps)
    def teardown(self):
        self.logger.info('quit!')
        self.driver.quit()


    def testlogin(self):
        self.logger.info('test logining!!!')
        self.driver.implicitly_wait(10)
        userfiled =self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id')
        pwdfiled =self.driver.find_element_by_id('com.cashregisters.cn:id/password_id')
        userfiled.send_keys('18610000920')
        pwdfiled.send_keys('1234qwer')
       self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id').click()
       self.driver.find_element_by_id('com.cashregisters.cn:id/loginbutton').click()
        time.sleep(2)

执行顺序:

框架的入口src/__init__.py (初始化日志相关的东西)—>testcase/__init__.py文件(package层的init)—>testlogin/__init__.py启动appiumserver—>testlogin方法


(六)框架内自动化测试用例设计和方法封装

(1)创建appiumserver,上面已介绍

(2)在run_test.cfg中指定只运行自动化测试脚本testloginsuccess.py文件

[nosetests]
;--with-unit output xml report
with-xunit=true
tests=testcase/testlogin/testloginsuccess.py
nocapture=true
verbose=true

tc-format=python
tc-file=conf/env/testconfig.py

;exclude=testcase/testmycase.py

(3)新建basetestcase基类(测试用例中继承basetestcase即可使用)

#coding:utf-8
from appium import webdriver
from testconfig import config
import logging

class basetestcase:
    def __init__(self):
        self.logger = logging.getlogger(__name__)

    def setup(self):
        desired_caps = {}
        desired_caps['platformname'] = 'android'
        desired_caps['platformversion'] = '6.0.0'
        desired_caps['devicename'] = 'google nexus 5'
        desired_caps['app'] = config['app']
        desired_caps['apppackage'] = config['apppackage']
        desired_caps['appactivity'] = config['appactivity']
        desired_caps['noreset'] = 'true'
        desired_caps['unicodekeyboard'] = 'true'
        desired_caps['resetkeyboard'] = 'true'
        if config['appwaitactivity'] != none:
            desired_caps['appwaitactivity'] = config['appwaitactivity']
        self.logger.info('session starting...')

        self.driver = webdriver.remote('http://localhost:4723/wd/hub', desired_caps)
    def teardown(self):
        self.logger.info('quit!')
        self.driver.quit()

(4)新建不同otherconfig文件,指定不同的端口、实现不同apk应用运行不同的用例,上面已介绍

global config
config = {}

config['packagename'] = 'com.hwd.test'
config['all']  = 'acj'

config['appiumport'] = 4724
config['selendroidport'] = 8124
config['bootstrapport'] = 4754
config['chromiumport'] = 9554

config['app'] = r'd:\pycharmworkspace\demoproject\src\testdata\xxx-release-v4.2.0-2018-03-07.apk'
config['apppackage'] = 'com.cashregisters.cn'
config['appactivity'] = 'com.hkrt.qpos.presentation.screen.welcome.welcomeactivity'

config['appwaitactivity'] = none

(5)测试脚本中导入使用

import logging,time
from src.util.commonbase import  *
from src.testcase.basetestcase import basetestcase

class testloginsucess(basetestcase):

    def __init__(self):
        basetestcase.__init__(self)
        self.logger = logging.getlogger(__name__)

    def setup(self):
        basetestcase.setup(self)

    def teardown(self):
        basetestcase.teardown(self)

    def testloginfailed(self):
        self.logger.info('test begaining!')
        self.driver.implicitly_wait(10)
        inputbyid(self.driver,'com.cashregisters.cn:id/phone_id','111111')
        inputbyid(self.driver, 'com.cashregisters.cn:id/password_id', 'aaaaaa')
        clickelementbyid(self.driver,'com.cashregisters.cn:id/loginbutton')
        time.sleep(2)

    def testloginsuccess(self):
        self.logger.info('test case 2.')
        self.driver.implicitly_wait(10)
        userfiled = self.driver.find_element_by_id('com.cashregisters.cn:id/phone_id')
        pwdfiled = self.driver.find_element_by_id('com.cashregisters.cn:id/password_id')
        userfiled.send_keys(18610000920)
        pwdfiled.send_keys(1234qwer)
        self.driver.find_element_by_id('com.cashregisters.cn:id/loginbutton').click()
        time.sleep(2)
self.driver.find_element_by_id('android:id/button2').click()

(6)封装方法 —比如封装登录按钮、截图等等,使用例看起来更简洁

import logging
logger = logging.getlogger(__name__)
def clickelementbyid(driver,elementid):
    '''
    :click element by id
    '''
    logging.info('click element by id:{}'.format(elementid))
    try:
        driver.find_element_by_id(elementid).click()
    except exception,e:
        logger.error('click id:{} fail,error msg:{}'.format(elementid,e))

def inputbyid(driver,elementid,text):
    '''
    :input sth into element by id
    '''
    logger.info('input {} into element by id:{}'.format(text,elementid))
    try:
        driver.find_element_by_id(elementid).send_keys(text)
    except exception,e:
        logger.error('input fail,error msg:{}'.format(e))

(7)测试用例中可以这样使用:

inputbyid(self.driver,'com.cashregisters.cn:id/phone_id','111111')
inputbyid(self.driver, 'com.cashregisters.cn:id/password_id', 'aaaaaa')
clickelementbyid(self.driver,'com.cashregisters.cn:id/loginbutton')

(8)测试报告

图片


(七)jenkins持续集成

jenkins配置:网上教程很多,就不赘述,挑一些项目相关的实例

7.1 需要安装的插件

此处用到2个插件:

(1)邮件插件email extension

图片

(2)报告插件html-reporting和groovy

图片

7.2 smtp邮件服务配置

(1)管理员邮件配置:

图片

(2)smtp服务器配置:

图片

(3)邮件通知配置:

图片

7.3 app自动化测试工程

(1)配置执行自动化测试的脚本:

图片

(2)测试报告设置:

图片

(3)收件人配置:

图片

(4)测试报告模板:

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事it行业的老鸟或是对it行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

图片

7.3 app自动化测试工程

(1)配置执行自动化测试的脚本:

图片

(2)测试报告设置:

图片

(3)收件人配置:

图片

(4)测试报告模板:

[外链图片转存中…(img-cdw7v5qe-1715316350009)]
[外链图片转存中…(img-b2r4cwp5-1715316350010)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事it行业的老鸟或是对it行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com