今天整理一下实战项目的代码共大家学习。(注:项目是针对我们公司内部系统的测试,只能内部网络访问,外部网络无法访问)
问:
1.外部网络无法访问,代码也无法运行,那还看这个项目有啥用
2.如何学习本项目
3.如何学习自动化测试(python+selenium)
答:
1.其实代码并不重要,希望大家完完整整的看完这个项目后,自己会有思路有想法,学会这个项目的框架结构和设计思想,把这些能应用到自己的项目中,那么目的就达到了(项目中涉及到的一些公共方法是可以单独运行的,大家可以拿来执行用到自己的项目中)
2.首先希望大家带着目标来学习这个项目1. 项目的目录结构(每个目录中存放什么东西)2.项目如何使用框架(本项目使用的是unittest框架)3.设计模式是如何应用在本项目中的(本项目应用page object设计模式)
3.个人而言
1)如果你没有任何的编程基础,建议先学习一门编程语言,包括环境的搭建,自己动手写代码,遇到问题多想多琢磨,这样一定会加深自己的印象。如果你有一定的编程基础那么直接看看python的基础语法和selenium就ok(我的自动化测试经验也有限,可能给不了大家太多的建议 ,当然会的越多越好 呵!)
2)自己动手搭个框架,手写一个实战的项目,这时候你会发现你还有好多东西不会,那么线路就来了,哪里不会就去学哪里,边学边写,直到你的项目完成,再次回味就会发现你会了好多,当然不会的东西更多了因为你的思路慢慢的扩宽了,你会想到无人值守,集成等等的想法
3)可以参加培训机构的培训,说实话现在的培训机构越来越多,个人认为有些机构的老师确实是没什么水准的,因为他们教的是基础没有太多的拔高内容,但是有一点是好了,你可以很系统的学习一系列的自动化知识
ok 说了很多废话,大家不要介意!直接上项目
项目简介
项目名称:**公司电子零售会员系统
项目目的:实现电子零售会员系统项目自动化测试执行
项目版本:v1.0
项目目录
Retail_TestProDocs# 存放项目的相关文档 01测试计划02测试大纲03测试用例04测试报告05测试进度06技术文档07测试申请Package# 存放第三方插件HTMLTestRunner.pyRetail Config__init__.pyConf.py# 读配置文件获取项目跟目录路径 并获取所有欲使用的目录文件的路径Config.ini# 存放项目跟目录的路径DataTestData__init__.pyelementDate.xlsx# 存放项目中所有的元素信息及测试数据Email_receiver.txt# 存放邮件的接受者信息Report# 测试报告ImageFail# 存放用例执行失败时的截图Pass# 存放用例执行成功时的截图Log# 存放用例执行过程中的log信息TestReport# 存放测试用例执行完成后生成的测试报告Test_case# 测试用例信息Models # 存放一些公共方法Doconfini.py# 读配置文件Doexcel.py# 读excel文件Driver.py# 存放driverLog.py# 生成logMyunit.py# 继承unittest.TestcaseSendmail.py# 发送邮件Strhandle.py# 字符串处理Tcinfo.py# 测试用例基本信息Testreport.py# 测试报告Page_obj# 测试模块Activerule_page.pyBase_page.pyCompany_page.pyCreaterule_page.pyMemberquery_page.pyModifypw_page.pyPointquery_page.pyActiveRuleTc.pyCompanyQueryTc.pyCreateRuleTc.pyLoginTc.pyMemberQueryTc.pyModifyPwTc.pyPointQueryTc.pyrunTc.py# 执行测试用例
项目环境
本版
python 36
pip insatll selenium
PyCharm
Windows 10 10.0
HTMLTestRunner.py
项目框架
unittest单元测试框架
pageobject 设计模式
UI对象库思想
项目设计
1.一个模块(被测项目的页面)对应一个py文件及一个测试类(测试文件)
2.每一个测试页面(系统的页面)中存储页面元素及此页面中涉及到的功能
3.每一个用例组合在一个测试类里面生成一个py文件
项目目标
我们在写自动化测试项目的时候一定要想好你的脚本都要哪些功能,页面元素平凡改动的时候是否需要大批量的修改脚本,及测试不同数据时是否也要修改脚本,那么能想到这些我们的初始目标差不多就有了
1. 生成测试用例执行结果报告
2.生成测试用例执行日志
3.用例执行失败或者执行完成后自动发送邮件报告
4. 用例执行失败或者成功时截取图片
5.数据驱动(读取测试数据,减少脚本维护成本)
项目代码
config.ini # 存放项目跟路径
[project]
project_path = D:\Petrochina_Retail_Test_Project
elementData.xlsx # 存放所有的测试数据及元素
一个excel文件,不方便贴里面内容(先过,别管里面是啥了 哈哈 后面再找吧)
mail_receiver.txt# 存放邮件接收者的账号 , 可以添加多个账号以‘,’号分割
**@qq.com
公共方法models下面的文件:
doconfini.py
1 '''2 Code description:read conf file3 Create time:4 Developer:5 '''6 7 import logging8 import configparser9 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 values3 Create time:4 Developer:5 '''6 7 import xlrd8 import os9 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 info3 Create time:4 Developer:5 '''6 7 import logging8 import time9
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 email3 Create time:4 Developer:5 '''6 7 import smtplib8 from email.mime.text import MIMEText9 from email.header import Header10 import os11 from retail.config import conf12 from retail.test_case.models.log import Logger13 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 = server38 self._fromuser = fromuser39 self._frompassword = frompassword40 self._sender = sender41 self._receiver = receiver42 self._subject = subject43 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._sender65 # 连接服务器,登录服务器,发送邮件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 msg98 99 if __name__ == '__main__':
100 readMsg=getReceiverInfo('mail_receiver.txt')
101 sendmail = SendMail(readMsg)
102 sendmail.sendEmail('2022-09-21 17_44_04.html')
strhandle.py
1 '''2 Code description: string handle3 Create time:4 Developer:5 '''6 7 import logging8 from retail.test_case.models.log import Logger9
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 report3 Create time:4 Developer:5 '''6 7 8 import time9 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 info3 Create time:4 Developer:5 '''6 7 from selenium import webdriver8 import logging9 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 framwork3 Create time:4 Developer:5 '''6 7 from retail.test_case.models.driver import WDriver8 import logging9 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 2022.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 2022.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.截图:
创建规则失败时截图
登录成功截图
用例执行日志:
日志
测试报告:
总结
看到结果还是挺有成就感的,邮件的截图我没发,因为我是内网不知道什么原因邮件服务器连接不上,但是使用外网单独测试邮件发送是没什么问题的!
就写这么多吧,其他页面的用例设计思路都是一样的,因为所有的用例彼此都是独立的,所以多与少都不影响! 要完整项目源码的同学可以DD昂吧!