前言
之前的文章说过, 要写一篇自动化实战的文章, 这段时间比较忙再加回家过11一直没有更新博客,今天整理一下实战项目的代码共大家学习。(注:项目是针对我们公司内部系统的测试,只能内部网络访问,外部网络无法访问)
问:
1.外部网络无法访问,代码也无法运行,那还看这个项目有啥用
2.如何学习本项目
3.如何学习自动化测试(python+selenium)
答:
1.其实代码并不重要,希望大家完完整整的看完这个项目后,自己会有思路有想法,学会这个项目的框架结构和设计思想,把这些能应用到自己的项目中,那么目的就达到了(项目中涉及到的一些公共方法是可以单独运行的,大家可以拿来执行用到自己的项目中)
2.首先希望大家带着目标来学习这个项目1. 项目的目录结构(每个目录中存放什么东西)2.项目如何使用框架(本项目使用的是unittest框架)3.设计模式是如何应用在本项目中的(本项目应用page object设计模式)
3.个人而言
1)如果你没有任何的编程基础,建议先学习一门编程语言,包括环境的搭建,自己动手写代码,遇到问题多想多琢磨,这样一定会加深自己的印象。如果你有一定的编程基础那么直接看看python的基础语法和selenium就ok(我的自动化测试经验也有限,可能给不了大家太多的建议 ,当然会的越多越好 呵!)
2)自己动手搭个框架,手写一个实战的项目,这时候你会发现你还有好多东西不会,那么线路就来了,哪里不会就去学哪里,边学边写,直到你的项目完成,再次回味就会发现你会了好多,当然不会的东西更多了因为你的思路慢慢的扩宽了,你会想到无人值守,集成等等的想法
3)可以参加培训机构的培训,说实话现在的培训机构越来越多,个人认为有些机构的老师确实是没什么水准的,因为他们教的是基础没有太多的拔高内容,但是有一点是好了,你可以很系统的学习一系列的自动化知识
ok 说了很多废话,大家不要介意!直接上项目
项目简介
项目名称:**公司电子零售会员系统
项目目的:实现电子零售会员系统项目自动化测试执行
项目版本:v1.0
项目目录
Retail_TestPro Docs# 存放项目的相关文档 01测试计划 02测试大纲 03测试用例 04测试报告 05测试进度 06技术文档 07测试申请 Package# 存放第三方插件 HTMLTestRunner.py Retail Config __init__.py Conf.py# 读配置文件获取项目跟目录路径 并获取所有欲使用的目录文件的路径 Config.ini# 存放项目跟目录的路径 Data TestData __init__.py elementDate.xlsx# 存放项目中所有的元素信息及测试数据 Email_receiver.txt# 存放邮件的接受者信息 Report# 测试报告 Image Fail# 存放用例执行失败时的截图 Pass# 存放用例执行成功时的截图 Log# 存放用例执行过程中的log信息 TestReport# 存放测试用例执行完成后生成的测试报告 Test_case# 测试用例信息 Models # 存放一些公共方法 Doconfini.py# 读配置文件 Doexcel.py# 读excel文件 Driver.py# 存放driver Log.py# 生成log Myunit.py# 继承unittest.Testcase Sendmail.py# 发送邮件 Strhandle.py# 字符串处理 Tcinfo.py# 测试用例基本信息 Testreport.py# 测试报告 Page_obj# 测试模块 Activerule_page.py Base_page.py Company_page.py Createrule_page.py Memberquery_page.py Modifypw_page.py Pointquery_page.py ActiveRuleTc.py CompanyQueryTc.py CreateRuleTc.py LoginTc.py MemberQueryTc.py ModifyPwTc.py PointQueryTc.py runTc.py# 执行测试用例
项目环境
本版
python 36
pip insatll selenium
PyCharm 2017.2.4
Windows 10 10.0
HTMLTestRunner.py
项目框架
unittest单元测试框架
pageobject 设计模式
UI对象库思想
项目设计
1.一个模块(被测项目的页面)对应一个py文件及一个测试类(测试文件)
2.每一个测试页面(系统的页面)中存储页面元素及此页面中涉及到的功能
3.每一个用例组合在一个测试类里面生成一个py文件
项目目标
我们在写自动化测试项目的时候一定要想好你的脚本都要哪些功能,页面元素平凡改动的时候是否需要大批量的修改脚本,及测试不同数据时是否也要修改脚本,那么能想到这些我们的初始目标差不多就有了
1. 生成测试用例执行结果报告
2.生成测试用例执行日志
3.用例执行失败或者执行完成后自动发送邮件报告
4. 用例执行失败或者成功时截取图片
5.数据驱动(读取测试数据,减少脚本维护成本)
项目代码
config.ini # 存放项目跟路径
1 2 |
|
conf.py
elementData.xlsx # 存放所有的测试数据及元素
一个excel文件,不方便贴里面内容(先过,别管里面是啥了 哈哈 后面再找吧)
mail_receiver.txt# 存放邮件接收者的账号 , 可以添加多个账号以‘,’号分割
**@qq.com
公共方法models下面的文件:
doconfini.py
1 ''' 2 Code description:read conf file 3 Create time: 4 Developer: 5 ''' 6 7 import logging 8 import configparser 9 from retail.config.conf import * 10 from retail.test_case.models.log import Logger 11 12 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO) 13 class DoConfIni(object): 14 15 def __init__(self): 16 """ 17 18 :param filename: 19 """ 20 self.cf = configparser.ConfigParser() 21 22 # 从ini文件中读数据 23 def getConfValue(self,filename,section,name): 24 """ 25 26 :param config: 27 :param name: 28 :return: 29 """ 30 try: 31 self.cf.read(filename) 32 value = self.cf.get(section,name) 33 except Exception as e: 34 log.logger.exception('read file [%s] for [%s] failed , did not get the value' %(filename,section)) 35 raise e 36 else: 37 log.logger.info('read excel value [%s] successed! ' %value) 38 return value 39 # 向ini文件中写数据 40 def writeConfValue(self,filename, section, name, value): 41 """ 42 43 :param section: section 44 :param name: value name 45 :param value: value 46 :return: none 47 """ 48 try: 49 self.cf.add_section(section) 50 self.cf.set(section, name, value) 51 self.cf.write(open(filename, 'w')) 52 except Exception : 53 log.logger.exception('section %s has been exist!' %section) 54 raise configparser.DuplicateSectionError(section) 55 else: 56 log.logger.info('write section'+section+'with value '+value+' successed!') 57 58 if __name__ == '__main__': 59 file_path = currPath 60 print(file_path) 61 read_config = DoConfIni() 62 63 value = read_config.getConfValue(os.path.join(currPath,'config.ini'),'project','project_path') 64 print(value) 65 66 read_config.writeConfValue(os.path.join(currPath,'config.ini'),'tesesection', 'name', 'hello word')
doexcel.py
1 ''' 2 Code description:read excel.xlsx, get values 3 Create time: 4 Developer: 5 ''' 6 7 import xlrd 8 import os 9 import logging 10 from retail.config import conf 11 from retail.test_case.models.log import Logger 12 13 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO) 14 15 class ReadExcel(object): 16 17 def __init__(self,fileName='elementDate.xlsx',sheetName='elementsInfo'): 18 """ 19 20 :param fileName: 21 :param sheetName: 22 """ 23 try: 24 self.dataFile = os.path.join(conf.dataPath, fileName) 25 self.workBook = xlrd.open_workbook(self.dataFile) 26 self.sheetName = self.workBook.sheet_by_name(sheetName) 27 except Exception: 28 log.logger.exception('init class ReadExcel fail', exc_info=True) 29 raise 30 else: 31 log.logger.info('initing class ReadExcel') 32 # 读excel中的数据 33 def readExcel(self,rownum,colnum): 34 """ 35 36 :param rownum: 37 :param colnum: 38 :return: 39 """ 40 try: 41 value = self.sheetName.cell(rownum,colnum).value 42 except Exception: 43 log.logger.exception('read value from excel file fail', exc_info=True) 44 raise 45 else: 46 log.logger.info('reading value [%s] from excel file [%s] completed' %(value, self.dataFile)) 47 return value 48 49 if __name__ == '__main__': 50 cellValue = ReadExcel().readExcel(1,3) 51 print((cellValue))
log.py
1 ''' 2 Code description:log info 3 Create time: 4 Developer: 5 ''' 6 7 import logging 8 import time 9 10 11 class Logger(object): 12 def __init__(self, logger, CmdLevel=logging.INFO, FileLevel=logging.INFO): 13 """ 14 15 :param logger: 16 :param CmdLevel: 17 :param FileLevel: 18 """ 19 self.logger = logging.getLogger(logger) 20 self.logger.setLevel(logging.DEBUG) # 设置日志输出的默认级别 21 # 日志输出格式 22 fmt = logging.Formatter('%(asctime)s - %(filename)s:[%(lineno)s] - [%(levelname)s] - %(message)s') 23 # 日志文件名称 24 # self.LogFileName = os.path.join(conf.log_path, "{0}.log".format(time.strftime("%Y-%m-%d")))# %H_%M_%S 25 currTime = time.strftime("%Y-%m-%d") 26 self.LogFileName = r'D:\Petrochina_Retail_Test_Project\retail\report\Log\log'+currTime+'.log' 27 # 设置控制台输出 28 # sh = logging.StreamHandler() 29 # sh.setFormatter(fmt) 30 # sh.setLevel(CmdLevel)# 日志级别 31 32 # 设置文件输出 33 fh = logging.FileHandler(self.LogFileName) 34 fh.setFormatter(fmt) 35 fh.setLevel(FileLevel)# 日志级别 36 37 # self.logger.addHandler(sh) 38 self.logger.addHandler(fh) 39 40 # def debug(self, message): 41 # """ 42 # 43 # :param message: 44 # :return: 45 # """ 46 # self.logger.debug(message) 47 # 48 # def info(self,message): 49 # """ 50 # 51 # :param message: 52 # :return: 53 # """ 54 # self.logger.info(message) 55 # 56 # def warn(self,message): 57 # """ 58 # 59 # :param message: 60 # :return: 61 # """ 62 # self.logger.warning(message) 63 # 64 # def error(self,message): 65 # """ 66 # 67 # :param message: 68 # :return: 69 # """ 70 # self.logger.error(message) 71 # 72 # def criti(self,message): 73 # """ 74 # 75 # :param message: 76 # :return: 77 # """ 78 # self.logger.critical(message) 79 80 if __name__ == '__main__': 81 logger = Logger("fox",CmdLevel=logging.DEBUG, FileLevel=logging.DEBUG) 82 logger.logger.debug("debug") 83 logger.logger.log(logging.ERROR,'%(module)s %(info)s',{'module':'log日志','info':'error'}) #ERROR,log日志 error
sendmail.py
1 ''' 2 Code description:send email 3 Create time: 4 Developer: 5 ''' 6 7 import smtplib 8 from email.mime.text import MIMEText 9 from email.header import Header 10 import os 11 from retail.config import conf 12 from retail.test_case.models.log import Logger 13 14 log = Logger(__name__) 15 # 邮件发送接口 16 class SendMail(object): 17 ''' 18 邮件配置信息 19 ''' 20 def __init__(self, 21 receiver, 22 subject='Retail 系统测试报告', 23 server='smtp.qq.com', 24 fromuser='281754043@qq.com', 25 frompassword='gifhhsbgqyovbhhc', 26 sender='281754043@qq.com'): 27 """ 28 29 :param receiver: 30 :param subject: 31 :param server: 32 :param fromuser: 33 :param frompassword: 34 :param sender: 35 """ 36 37 self._server = server 38 self._fromuser = fromuser 39 self._frompassword = frompassword 40 self._sender = sender 41 self._receiver = receiver 42 self._subject = subject 43 44 def sendEmail(self, fileName): 45 """ 46 47 :param filename: 48 :return: 49 """ 50 # 打开报告文件读取文件内容 51 try: 52 f = open(os.path.join(conf.reportPath, fileName), 'rb') 53 fileMsg = f.read() 54 except Exception: 55 log.logger.exception('open or read file [%s] failed,No such file or directory: %s' %(fileName, conf.reportPath)) 56 log.logger.info('open and read file [%s] successed!' %fileName) 57 else: 58 f.close() 59 # 邮件主题 60 subject = 'Python test report' # 61 # 邮件设置 62 msg = MIMEText(fileMsg, 'html', 'utf-8') 63 msg['subject'] = Header(subject, 'utf-8') 64 msg['from'] = self._sender 65 # 连接服务器,登录服务器,发送邮件 66 try: 67 smtp = smtplib.SMTP() 68 smtp.connect(self._server) 69 smtp.login(self._fromuser, self._frompassword) 70 except Exception: 71 log.logger.exception('connect [%s] server failed or username and password incorrect!' %smtp) 72 else: 73 log.logger.info('email server [%s] login success!' %smtp) 74 try: 75 smtp.sendmail(self._sender, self._receiver, msg.as_string()) 76 except Exception: 77 log.logger.exception('send email failed!') 78 else: 79 log.logger.info('send email successed!') 80 81 82 # 从文件中读取邮件接收人信息 83 def getReceiverInfo(fileName): 84 ''' 85 :param filename: 读取接收邮件人信息 86 :return: 接收邮件人信息 87 ''' 88 try: 89 openFile = open(os.path.join(conf.dataPath, fileName)) 90 except Exception: 91 log.logger.exception('open or read file [%s] failed,No such file or directory: %s' %(fileName, conf.dataPath)) 92 else: 93 log.logger.info('open file [%s] successed!' %fileName) 94 for line in openFile: 95 msg = [i.strip() for i in line.split(',')] 96 log.logger.info('reading [%s] and got receiver value is [%s]' %(fileName, msg)) 97 return msg 98 99 if __name__ == '__main__': 100 readMsg=getReceiverInfo('mail_receiver.txt') 101 sendmail = SendMail(readMsg) 102 sendmail.sendEmail('2018-09-21 17_44_04.html')
strhandle.py
1 ''' 2 Code description: string handle 3 Create time: 4 Developer: 5 ''' 6 7 import logging 8 from retail.test_case.models.log import Logger 9 10 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO) 11 def strhandle(str): 12 """ 13 14 :param str: 15 :return: 16 """ 17 #初始化字符、数字、空格、特殊字符的计数 18 try: 19 lowerCase = 0 20 upperCase = 0 21 number = 0 22 other = 0 23 for stritem in str: 24 #如果在字符串中有小写字母,那么小写字母的数量+1 25 if stritem.islower(): 26 lowerCase += 1 27 #如果在字符串中有数字,那么数字的数量+1 28 elif stritem.isdigit(): 29 number += 1 30 elif stritem.isupper():# 大写字母 31 upperCase +=1 32 #如果在字符串中有空格,那么空格的数量+1 33 else: 34 other += 1 35 return lowerCase, upperCase, number, other 36 except Exception as e: 37 log.logger.exception('string handle error , please check!', exc_info=True) 38 raise e 39 40 41 if __name__=='__main__': 42 list = ['qwert','erwer'] 43 lowercase, uppercase, number, other = strhandle(list[0]) 44 print ("该字符串中的小写字母有:%d" %lowercase) 45 print ("该字符串中的大写写字母有:%d" %uppercase) 46 print ("该字符串中的数字有:%d" %number) 47 print ("该字符串中的特殊字符有:%d" %other)
testreport.py
1 ''' 2 Code description:test report 3 Create time: 4 Developer: 5 ''' 6 7 8 import time 9 import logging 10 import unittest 11 from BeautifulReport import BeautifulReport 12 import HTMLTestRunner 13 from retail.config import conf 14 from retail.test_case.models.log import Logger 15 16 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO) 17 # 用HTMLTestRunner 实现的测试报告 18 def testreport(): 19 """ 20 21 :return: 22 """ 23 currTime = time.strftime('%Y-%m-%d %H_%M_%S') 24 fileName = conf.reportPath + r'\report' + currTime + '.html' 25 try: 26 fp = open(fileName, 'wb') 27 except Exception : 28 log.logger.exception('[%s] open error cause Failed to generate test report' %fileName) 29 else: 30 runner = HTMLTestRunner.HTMLTestRunner\ 31 (stream=fp, title='Retail sys测试报告', 32 description='处理器:Intel(R) Core(TM) ' 33 'i5-6200U CPU @ 2030GHz 2.40 GHz ' 34 '内存:8G 系统类型: 64位 版本: windows 10 家庭中文版') 35 log.logger.info('successed to generate test report [%s]' %fileName) 36 return runner, fp, fileName 37 # 38 def addTc(TCpath = conf.tcPath, rule = '*TC.py'): 39 """ 40 41 :param TCpath: 测试用例存放路径 42 :param rule: 匹配的测试用例文件 43 :return: 测试套件 44 """ 45 discover = unittest.defaultTestLoader.discover(TCpath, rule) 46 47 return discover 48 # 用BeautifulReport模块实现测试报告 49 def runTc(discover): 50 """ 51 52 :param discover: 测试套件 53 :return: 54 """ 55 currTime = time.strftime('%Y-%m-%d %H_%M_%S') 56 fileName = currTime+'.html' 57 try: 58 result = BeautifulReport(discover) 59 result.report(filename=fileName, description='测试报告', log_path=conf.reportPath) 60 except Exception: 61 log.logger.exception('Failed to generate test report', exc_info=True) 62 else: 63 log.logger.info('successed to generate test report [%s]' % fileName) 64 return fileName 65 66 if __name__ == '__main__': 67 testreport() 68 suite = addTc(rule = '*TC.py') 69 runTc(suite)
driver.py
1 ''' 2 Code description:save all driver info 3 Create time: 4 Developer: 5 ''' 6 7 from selenium import webdriver 8 import logging 9 import sys 10 from retail.test_case.models.log import Logger 11 12 13 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO) 14 class WDriver(object): 15 16 # Firefox driver 17 def fireFoxDriver(self): 18 """ 19 20 :return: 21 """ 22 try: 23 self.driver = webdriver.Firefox() 24 except Exception as e: 25 log.logger.exception('FireFoxDriverServer.exe executable needs to be in PATH. Please download!', exc_info=True) 26 raise e 27 else: 28 log.logger.info('%s:found the Firefox driver [%s] successed !' %(sys._getframe().f_code.co_name,self.driver)) 29 return self.driver 30 31 # chrom driver 32 def chromeDriver(self): 33 """ 34 35 :return: 36 """ 37 try: 38 # option = webdriver.ChromeOptions()# 实现不打开浏览器 执行web自动化测试脚本 39 # option.add_argument('headless')# 40 # self.driver = webdriver.Chrome(chrome_options=option) 41 self.driver = webdriver.Chrome() 42 except Exception as e: 43 log.logger.exception('ChromeDriverServer.exe executable needs to be in PATH. Please download!', 44 exc_info=True) 45 raise e 46 else: 47 log.logger.info('%s:found the chrome driver [%s] successed !' % (sys._getframe().f_code.co_name, self.driver)) 48 return self.driver 49 50 51 # Ie driver 52 def ieDriver(self): 53 """ 54 55 :return: 56 """ 57 try: 58 self.driver = webdriver.Ie() 59 except Exception as e: 60 log.logger.exception('IEDriverServer.exe executable needs to be in PATH. Please download!', 61 exc_info=True) 62 raise e 63 else: 64 log.logger.info('%s:found the IE driver [%s] successed !' % (sys._getframe().f_code.co_name, self.driver)) 65 return self.driver 66 67 68 if __name__ == '__main__': 69 WDrive=WDriver() 70 WDrive.fireFoxDriver()
myunittest.py
1 ''' 2 Code description:unittest framwork 3 Create time: 4 Developer: 5 ''' 6 7 from retail.test_case.models.driver import WDriver 8 import logging 9 import unittest 10 from retail.test_case.page_obj.login_page import LoginPage 11 from retail.test_case.models.log import Logger 12 from selenium import webdriver 13 14 log = Logger(__name__, CmdLevel=logging.INFO, FileLevel=logging.INFO) 15 class MyunitTest(unittest.TestCase): 16 """ 17 18 """ 19 20 # add by xuechao at 2018.09.19 21 @classmethod 22 def setUpClass(cls): # 一个测试类(文件)执行一次打开浏览器, 节约每个用例打开一次浏览器的时间 23 24 #cls.driver = WDriver().fireFoxDriver() 25 cls.driver = WDriver().chromeDriver() 26 cls.driver.maximize_window() 27 log.logger.info('opened the browser successed!') 28 # ---------------------------- 29 30 def setUp(self): 31 """ 32 33 :return: 34 """ 35 self.login = LoginPage(self.driver) 36 self.login.open() 37 log.logger.info('************************starting run test cases************************') 38 39 def tearDown(self): 40 """ 41 42 :return: 43 """ 44 self.driver.refresh() 45 log.logger.info('************************test case run completed************************') 46 47 # add by linuxchao at 2018.09.19 48 @classmethod 49 def tearDownClass(cls): 50 cls.driver.quit() 51 log.logger.info('quit the browser success!') 52 #---------------------------- 53 if __name__ == '__main__': 54 unittest.main()
目前为止,我需要的所有的公共方法都编写完了, 后期再需要别的方法可以加,下面我们就开始编写我们的测试用例,由于我们使用的是PageObject模式,那么我们需要设计一个basepage页面,所有的页面或者说模块全部继承这个basepage,basepage主要编写所有页面的公共方法
base_page.py
#登录页面
login_page.py
# 登录测试用例
LoginTc.py
# 修改密码页面
modifypw_page.py
# 修改密码测试用例
ModifyPw.py
# 会员档案查询页面
memberquery_page.py
# 会员档案查询用例
MemberQueryTc.py
# 执行测试用例
RunTc.py
from BeautifulReport import BeautifulReport 这个报告需要自己网上找一下(很多类似的测试报告源码,不一定非使用本案例中的报告模板)
报告展示
有付出才有汇报,接下来看下们的成果
1.截图:
创建规则失败时截图
登录成功截图
用例执行日志:
日志
测试报告:
这是我整理的《2024最新Python自动化测试全套教程》,以及配套的接口文档/项目实战【网盘资源】,需要的朋友可以下方视频的置顶评论获取。肯定会给你带来帮助和方向。
【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)