先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新软件测试全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注软件测试)
正文
测试套件(测试框架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)测试报告模板:
(5)配置邮件什么情况下发送(成功发送/失败发送):
(6)构建完后jenkins页面显示:
(7)最终邮件收到的报告:
好了 学习也就到此结束了 想了解更多相关知识请关注我吧!下面是小编想对读者大大们写的一封信哦! 记住要认真读哦!
感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接免费拿走:
① 2000多本软件测试电子书(主流和经典的书籍应该都有了)
② 软件测试/自动化测试标准库资料(最全中文版)
③ 项目源码(四五十个有趣且经典的练手项目及源码)
④ Python编程语言、API接口自动化测试、web自动化测试、App自动化测试(适合小白
⑤ Python学习路线图(告别不入流的学习)
上图的资料 在我的QQ技术交流群里(技术交流和资源共享,广告进来腿给你打断)
可以自助拿走,群号768747503备注(csdn999)群里的免费资料都是笔者十多年测试生涯的精华。还有同行大神一起交流技术哦
————————————————
「学习资料 笔记 工具 文档领取」
扫描二维码,
备注“csdn999”
小姐姐邀你一起学习哦~~
和志同道合的测试小伙伴一起讨论测试技术吧!
一定一定一定 要备注暗号:CSDN999
———————————————
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注软件测试)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
7503备注(csdn999)](?_wv=1027&k=TaGcsO6N “768747503备注(csdn999)”)群里的免费资料都是笔者十多年测试生涯的精华。还有同行大神一起交流技术哦
————————————————
「学习资料 笔记 工具 文档领取」**
扫描二维码,
备注“csdn999”
小姐姐邀你一起学习哦~~
和志同道合的测试小伙伴一起讨论测试技术吧!
一定一定一定 要备注暗号:CSDN999
———————————————
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注软件测试)
[外链图片转存中…(img-veeaieae-1713541263265)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!