欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python+pytest接口自动化框架搭建

程序员文章站 2022-03-11 14:50:25
一、Pycharm中创建项目结构1.新建一个工程testProject2.在工程的根目录下新建一个conftest.py(测试用例的一些fixture配置)和pytest.ini(改变pytest的运行方式)3.在工程下创建以下package包和文件夹common:这个包放一些公共的方法,如:读取excel文件方法,读取mysql、oracle的脚本config:放一些配置文件,如邮箱的一些参数:收件人,发件人,密码等logs:这里存放日志信息report:这里存放测试报告test_ca...

一、Pycharm中创建项目结构

1.新建一个工程testProject
2.在工程的根目录下新建一个conftest.py(测试用例的一些fixture配置)和pytest.ini(改变pytest的运行方式)
3.在工程下创建以下package包和文件夹
python+pytest接口自动化框架搭建

  • common:这个包放一些公共的方法,如:读取excel文件方法,读取mysql、oracle的脚本
  • config:放一些配置文件,如邮箱的一些参数:收件人,发件人,密码等
  • logs:这里存放日志信息
  • report:这里存放测试报告
  • test_case:这个包放test开头的测试用例,也可以放一些非test开头的封装接口方法
    注意:
    每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。

二、在common包下实现一些常用工具类

1)常用的logger日志类封装
# -*- coding:utf-8 -*-

#默认日志格式
DEFAULT_LOG_FMT='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s: %(message)s'
#默认时间格式
DEFUALT_LOG_DATEFMT='%Y-%m-%d %H:%M:%S'
#输出日志路径
import os
LOG_OUT_PATH=os.path.abspath('..')+'/logs/'

import sys
import logging
from time import strftime

class Logger(object):
	def __init__(self):
		self._logger=logging.getLogger()
		self.DEFAULT_LOG_FILENAME='{0}{1}.log'.format(LOG_OUT_PATH,strftime("%Y-%m-%d"))
		self.formatter=logging.Formatter(fmt=DEFAULT_LOG_FMT,datefmt=DEFUALT_LOG_DATEFMT)
		self._logger.addHandler(self._get_file_handler(self.DEFAULT_LOG_FILENAME))
		self._logger.addHandler(self._get_console_handler())
		self._logger.setLevel(logging.INFO) #默认等级

	def _get_file_handler(self,filename):
		filehandler=logging.FileHandler(filename,encoding="utf-8")
		filehandler.setFormatter(self.formatter)
		return  filehandler

	def _get_console_handler(self):
		console_handler=logging.StreamHandler(sys.stdout)
		console_handler.setFormatter(self.formatter)
		return console_handler

	@property
	def logger(self):
		return self._logger

if __name__ == '__main__':
	import datetime
	logging=Logger().logger
	logging.info(u"{}:开始XXX操作".format(datetime.datetime.now()))

2)常用的readConfig类封装
# -*- coding:utf-8 -*-
import configparser


class ReadConfig(object):
	# 构造函数
	def __init__(self, file_name=None):
		'''
		:param file_name: 配置文件地址
		:param node: 节点名字
		'''
		# 容错处理
		if file_name == None:
			# 默认地址
			self.file_name = r'../config/config.ini'
		else:
			self.file_name = file_name
		self.cf = self.load_ini(self.file_name)

	# 加载文件
	def load_ini(self, file_name):
		cf = configparser.ConfigParser()
		cf.read(file_name, encoding='utf-8')
		return cf

	# 获取value得值
	def get_value(self, node, key):
		data = self.cf.get(node, key)
		return data

	# 存储token值到config。ini
	def set_value(self, group, instance, value):
		self.cf.set(group, instance, value)

	# 保存config.ini
	def save(self,filename):
		with open(filename, 'w') as f:
			self.cf.write(f)


if __name__ == '__main__':
	cf = ReadConfig()
	print(cf.get_value('env', 'ip'))

config.ini文件格式:

# cat ../config/config.ini
[db]
user = root
password = 123456
host = dev.mysql.com
port = 3306

[redis]
host = dev.redis.com
port = 6379
password = 123456
3)常用的读取Mysql操作封装
# -*- coding:utf-8 -*-
import pymysql
import sys
class Mc(object):
	#  这里 注释连接的方法,是为了 实例化对象时,就创建连接。不许要单独处理连接了。
	#
	# def connDataBase(self):
	#     ''' 数据库连接 '''
	#
	#     self.db = pymysql.connect(self.host,self.username,self.password,self.port,self.database)
	#
	#     # self.cursor = self.db.cursor()
	#
	#     return self.db
	def __init__(self,db_host="*****", username="root", pw="**********",port=3306, dbname="*****"):
		self.db_host=db_host
		self.username=username
		self.pw=pw
		self.dbname=dbname
		self.port=port
		self.db=pymysql.connect(self.db_host,self.username,self.pw,self.dbname,self.port,charset='utf8')
		#print(dbname)

	def insertDB(self, sql):
		''' 插入数据库操作 '''

		self.cursor = self.db.cursor()

		try:
			# 执行sql
			self.cursor.execute(sql)
			# tt = self.cursor.execute(sql)  # 返回 插入数据 条数 可以根据 返回值 判定处理结果
			# print(tt)
			self.db.commit()
		except:
			# 发生错误时回滚
			self.db.rollback()
		finally:
			self.cursor.close()

	def deleteDB(self, sql):
		''' 操作数据库数据删除 '''
		self.cursor = self.db.cursor()

		try:
			# 执行sql
			self.cursor.execute(sql)
			# tt = self.cursor.execute(sql) # 返回 删除数据 条数 可以根据 返回值 判定处理结果
			# print(tt)
			self.db.commit()
		except:
			# 发生错误时回滚
			self.db.rollback()
		finally:
			self.cursor.close()

	def updateDb(self, sql):
		''' 更新数据库操作 '''

		self.cursor = self.db.cursor()

		try:
			# 执行sql
			self.cursor.execute(sql)
			# tt = self.cursor.execute(sql) # 返回 更新数据 条数 可以根据 返回值 判定处理结果
			# print(tt)
			self.db.commit()
		except:
			# 发生错误时回滚
			self.db.rollback()
		finally:
			self.cursor.close()

	def selectDb(self, sql):
		''' 数据库查询 '''
		self.cursor = self.db.cursor()
		try:
			self.cursor.execute(sql)  # 返回 查询数据 条数 可以根据 返回值 判定处理结果

			data = self.cursor.fetchall()  # 返回所有记录列表
			#print(type(data))
			#print(data[0][0])
		except:
			print('Error: unable to fecth data')
		finally:
			self.cursor.close()
		return data
	def closeDb(self):
		''' 数据库连接关闭 '''
		self.db.close()


if __name__ == '__main__':
	mc=Mc()
	sql='SELECT * from student;'
	result=mc.selectDb(sql)
	print(result)

注意:
连接数据库时报错“AttributeError: ‘NoneType’ object has no attribute ‘encoding’”
解决办法:将下面部分的utf-8改为utf8
self.db=pymysql.connect(self.db_host,self.username,self.pw,self.dbname,self.port,charset=‘utf-8’)

4)操作Excel类封装

在自动化测试中经常将测试数据放入到Excel中,为了操作简便,将一些操作封装到readExcel类中

# -*- coding:utf-8 -*-
# author_='ting.chun'
# date:2020/10/22 19:05
import xlrd
from xlutils import copy
class ReadExcel:
	'''
	此类专门用于读取Excel
	'''
	#__workbook用于存放excel文件的对象
	__workbook=None
	__sheet=None
	__print=False #用于开启全局打印
	def __init__(self,file_name,sheet_name):
		'''
		类的初始化方法,在类似初始化的时候被调用
		:param file_name: excel的文件名
		:param sheet_name: excel中需要访问的sheet名
		'''
		self.file_name=file_name
		ReadExcel.__workbook=xlrd.open_workbook(file_name)
		#根据sheet名称获取sheet内容
		ReadExcel.__sheet=ReadExcel.__workbook.sheet_by_name(sheet_name)
	def get_number_of_rows(self):
		'''
		 获取表格中内容的最大行数
		:return:__number_of_row
		'''
		__rows_number=self.__sheet.nrows
		if ReadExcel.__print is True:
			print(__rows_number)
		return __rows_number

	def get_number_of_cols(self):
		'''
		获取表格中内容最大的列数
		:return: __number_of_cols
		'''
		__cols_number=self.__sheet.ncols
		if ReadExcel.__print is True:
			print(__cols_number)
		return __cols_number

	def get_value_of_row(self,row_index):
		"""
		获取某一行的所有值构成列表
		:param row_index: 行号
		:return: 行内容组成的列表
		"""
		__row_value=self.__sheet.row_values(row_index)
		if ReadExcel.__print is True:
			print(__row_value)
		return __row_value

	def get_value_of_col(self, col_index):
		"""
		获取某一列的所有值构成的列表
		:param col_index: 列号
		:return: 列中内容组成的列表
		"""
		__col_value = self.__sheet.col_values(col_index)
		if ReadExcel.__print is True:
			print(__col_value)
		return __col_value

	def get_value_of_cell(self, row_index, col_index):
		"""
		获取某一个单元格中的值
		:param row_index: 行号
		:param col_index: 列号
		:return: 单元格中的内容
		"""
		# 第row_index行 col_index列是内容
		__cell_value = self.__sheet.cell(row_index, col_index).value
		if ReadExcel.__print is True:
			print(__cell_value)
		return __cell_value

	def write(self,sheet,row,col,value):
		new_workbook=copy.copy(ReadExcel.__workbook)
		w_sheet=new_workbook.get_sheet(sheet)
		w_sheet.write(row,col,value)
		new_workbook.save(self.file_name) #保存文件

if __name__=='__main__':
	filename=r'..\config\api_info.xls'
	el=ReadExcel(filename,'Sheet1')
	el.write(0,2,1,"你好吗")

5)tool工具代码介绍
# -*- coding:utf-8 -*-
# author_='ting.chun'
# date:2020/10/22 11:26
import time,datetime
import random
import string
class Tool:
	#获取注册的email
	def get_email(self):
		value='{}@163.com'.format(str(int(time.time())))
		return value
	#使用时间戳作为一个唯一标识
	def get_unique(self):
		unique=int(time.time())
		return unique
	#生成11的手机号
	def get_phone(self):
		# 运营商的号码前缀
		prefix = [
			'130', '131', '132', '133', '134', '135', '136', '137', '138', '139',
			'145', '147', '149', '150', '151', '152', '153', '155', '156', '157',
			'158', '159', '165', '171', '172', '173', '174', '175', '176', '177',
			'178', '180', '181', '182', '183', '184', '185', '186', '187', '188',
			'189', '191'
		]
		#随机取一个手机号前缀
		pos=random.randint(0,len(prefix)-1)
		#随机生成后8为数字,string。digits是数字0到9
		suffix=''.join(random.sample(string.digits,8))
		#拼接返回11位手机号
		return prefix[pos]+suffix


#print(str(int(time.time()))+"@163.com")
if __name__ == '__main__':
	tool=Tool()
	phone=tool.get_email()
	print(phone)

上述主要受common包组成模块代码,后续会继续更新,喜欢的请关注哟!

本文地址:https://blog.csdn.net/chuntingting/article/details/109258198