测试人挣破年入20万的束缚,从第一个python+selenium项目开始!

news/2024/11/24 9:45:09/

今天整理一下实战项目的代码共大家学习。(注:项目是针对我们公司内部系统的测试,只能内部网络访问,外部网络无法访问)

 

问:

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昂吧!


http://www.ppmy.cn/news/188692.html

相关文章

多传感器融合SLAM --- 1.多传感器融合的基础知识

目录 1 什么是多传感器融合 1.1 有哪些传感器 1.2 它们的优缺点有哪些

Go 字节跳动—从需求到上线全流程

走进后端开发流程 整个课程会带大家先从理论出发,思考为什么有流程 大家以后工作的团队可能不一样,那么不同的团队也会有不同的流程,这背后的逻辑是什么 然后会带大家按照走一遍从需求到上线的全流程,告诉大家在流程的每个阶段&am…

nvidia 专业显卡解码能力

专业显卡问题 p620 解码:有时我们经常遇到专业显卡,专业显卡和非专业显卡在使用opengl 等底层调用时表现不一样,值得注意的是:专业显卡解码能力到了p400 以上才有显著的提升,p620 家族为开始又有提升,p620…

P440测距模块简介

PulsOn 440 模块(简称 P440)是一种波段在 3.1G 到 4.8GHz 之间的超宽带无线收发器,它可以实现如下功能:采用双向飞行时间(TW-TOF)方式在 2 个或者 2 个以上的模块之间进行测距,测量准确度可达 2…

ElementUI的NavMenu 导航菜单水平展开左侧菜单时,默认打开子菜单(open方法:手动打开子菜单)

文章目录 情景知识点htmljs效果 情景 ElementUI的NavMenu 导航菜单model为vertical(默认)时,可以通过控制collaspe来控制菜单的水平折叠与展开,但是开启折叠的时候子菜单是关闭状态,现在是希望水平展开菜单的同时&…

《数字图像处理》题库5:计算题 ②

前言 这是我在学习数字图像处理这门课程时,从网络上以及相关书籍中搜集到的一些题目, 这些题目主要是针对期末考试的。 做题之前你需要注意以下几点: 这篇文章整理了第5种题型,即计算题的第2部分。由于题目或答案中会出现许多的公…

http 400错误解决

产品上线后,在试单过程中,出现一个硬bug,我们系统接入的是百度系统,出现一个http 400错误,这硬bug难倒10几个英雄汉。接下来描述下问题,及我们解决问题的方法及教训: 我们的系统与百度系统是专…

microhard p900数传配置方法

配置好的两个(多个)数传电台可以通过串口直接相互通讯,两个(多个)数传之间无线连接。 数传电台可以配置多种通讯方式:点对点、点对多、mesh组网(电台数量≥2) 主要参考p900的英文说明…