def read_excel(): LogPrint().info("----------------读取配置文件中测试用例文件的路径信息----------------") dcc = DealCommonCfg() my_list = dcc.read_config('test_case_file') # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0]] = i[1] # python3的写法,下面是python2的写法 values = list(my_dic.values()) return values
def read_file_path(txt_type): dcc = DealCommonCfg() if txt_type == 'random_param': LogPrint().info( "----------------读取配置文件中随机函数的txt文件路径信息----------------") elif txt_type == 'form_data_template': LogPrint().info( "----------------读取配置文件中以form_data方式请求的函数所需的txt文件路径信息----------------" ) else: LogPrint().info( "----------------txt的类型错误,读取不到配置文件中的路径信息----------------") my_list = dcc.read_config(txt_type) # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0]] = i[1] return my_dic
class MailSend: LogPrint().info("----------------读取配置文件中邮箱信息----------------") dcc = DealCommonCfg() my_list = dcc.read_config('email_qq') # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0]] = i[1] # python3的写法,下面是python2的写法 # my_dic[i[0].encode('UTF-8')] = i[1].encode('UTF-8') smtp_server = my_dic['smtp_server'] # QQ的SMTP服务器地址 sender_qq_adr = my_dic['sender_qq_adr'] # 发送人的邮箱地址 password = my_dic['password'] # QQ邮箱的授权码 receiver_qq_adr = my_dic['receiver_qq_adr'] # 收件人的邮箱地址 mail_to_copy = my_dic['mail_to_copy'] # 抄送人的邮箱地址 def overtime_warn(self, mail_title): LogPrint().info("----------------接口超时,开始发送邮件----------------") mail_content = 'warn,interface request overtime,please look out!!' # 邮件的正文内容 mail_title = mail_title # 邮件标题 smtp = SMTP_SSL(self.smtp_server) # SSL登录 # smtp.set_debuglevel(1) # 用来调试的,1为开启调试模式,可以在控制台打印出和SMTP服务器交互的所有信息 smtp.ehlo(self.smtp_server) # what't mean smtp.login(self.sender_qq_adr, self.password) # 登录SMTP服务器 # 邮件主题、如何显示发件人、收件人等信息并不是通过SMTP协议发给MTA,而是包含在发给MTA的文本中的 msg = MIMEText(mail_content, 'plain', 'utf-8') # 构造MIMEText对象 msg['Subject'] = Header(mail_title, 'utf-8') msg['From'] = self.sender_qq_adr # 将邮件主题、发件人和收件人添加到MIMEText中 msg['To'] = self.receiver_qq_adr # 邮件正文是一个str,所以需要将MIMEText对象变成str类型,这一个特别注意,是将对象转换成字符串类型的方法 smtp.sendmail(self.sender_qq_adr, self.receiver_qq_adr, msg.as_string()) LogPrint().info("----------------超时邮件发送成功----------------") smtp.quit() # 接口请求测试完成后的通知邮件,发送html格式的邮件 def send_mail(self, text): LogPrint().info("----------------开始发送测试报告----------------") subject = '[AutomationTest]接口自动化测试报告通知' # 邮件标题 username = '******' # 用户邮箱的账号 msg = MIMEText(text, 'html', 'utf-8') msg['Subject'] = subject msg['From'] = self.sender_qq_adr msg['To'] = ';'.join([self.receiver_qq_adr]) msg['Cc'] = ';'.join([self.mail_to_copy]) smtp = SMTP_SSL(self.smtp_server) smtp.connect(self.smtp_server) smtp.login(username, self.password) smtp.sendmail(self.sender_qq_adr, [self.receiver_qq_adr] + [self.mail_to_copy], msg.as_string()) LogPrint().info("----------------测试报告发送成功----------------") smtp.quit()
class MysqlDeal: dcc = DealCommonCfg() my_list = dcc.read_config('mysql') # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0].encode('UTF-8')] = i[1].encode( 'UTF-8') # 这里将获得的数据由unicode类型转换为str类型,然后存入字典中 host = my_dic['host'] user = my_dic['user'] password = my_dic['password'] port = int(my_dic['port']) charset = my_dic['charset'] db = my_dic['db'] def conn_db(self): LogPrint().info("----------------正在连接mysql服务器----------------") conn = pymysql.connect( # 连接数据库,其实就是建立一个pymysql.connect()的实例对象conn,该对象有commit()、rollback()、cursor()等属性 host=self.host, user=self.user, password=self.password, port=self.port, charset=self.charset, db=self.db) LogPrint().info("----------------连接服务器成功----------------") cur = conn.cursor() # 通过游标(指针)cursor的方式操作数据库,该代码作用是得到当前指向数据库的指针 return (conn, cur) @staticmethod def select_db(cur, sql): # 查询数据库,查询条件中需要使用传入的参数 cur.execute(sql) result = cur.fetchall() if len(result) == 0: LogPrint().info("----------------查询结束,数据库无数据----------------") return 0 else: return result @staticmethod def other_operate_db(conn, cur, sql): # 增、删、改数据库的操作 cur.execute(sql) conn.commit() @staticmethod def close_db(conn, cur): cur.close() conn.close()
class MongodbDeal: dcc = DealCommonCfg() my_list = dcc.read_config('mongodb') # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0]] = i[1] # my_dic[i[0].encode('UTF-8')] = i[1].encode('UTF-8') # 这里将获得的数据由unicode类型转换为str类型,然后存入字典中 host = my_dic['host'] password = my_dic['password'] port = int(my_dic['port']) user = my_dic['user'] def conn_db(self, db, collection): LogPrint().info("----------------正在连接mongo服务器----------------") client = MongoClient(self.host, self.port) # 建立与数据库系统的连接 db = client[db] # 连接数据库 db.authenticate(self.user, self.password) # 认证用户密码 collection = db[collection] LogPrint().info("----------------连接服务器成功----------------") return db, collection @staticmethod # 这里statement格式的示例如{'id': '20190410','name': 'Jordan','age': 26,'gender': 'male'} def operate(handle, statement): md = MongodbDeal() db, collection = md.conn_db('test_itf_one', 'test_itf_one') if handle == 'insert': collection.insert_one( statement) # 同时插入多条数据使用insert_many()方法,参数需要以列表形式传递 elif handle == 'find': result = collection.find_one( statement) # 这里find_one()查询得到的是单个结果,find()则返回一个生成器对象 return result elif handle == 'delete': collection.remove(statement) # 另外两个删除方法delete_one()和delete_many() elif handle == 'update': collection.update(statement) else: LogPrint().info("----------------操作类型或执行语句错误----------------") def close_db(self): client = MongoClient(self.host, self.port) # 建立与数据库系统的连接 client.close()
class RedisDeal: dcc = DealCommonCfg() my_list = dcc.read_config('redis') # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0]] = i[1] # python3的写法,以下是python2的写法 # my_dic[i[0].encode('UTF-8')] = i[1].encode('UTF-8') # 这里将获得的数据由unicode类型转换为str类型,然后存入字典中 host = my_dic['host'] password = my_dic['password'] port = int(my_dic['port']) def conn_db(self): LogPrint().info("----------------正在连接redis服务器----------------") r = redis.Redis(host=self.host, port=self.port) LogPrint().info("----------------连接服务器成功----------------") return r @staticmethod def operate( *dim): # 这里可变参数dim至少有2个值,第一个是操作类型如set、get等,第二个是name值,第三个是value值可不传 r = RedisDeal().conn_db() list_one = [] for i in dim: list_one.append(i) if list_one[0] == 'set': r.set(name=list_one[1], value=list_one[2]) elif list_one[0] == 'delete': r.delete(list_one[1]) elif list_one[0] == 'get': return r.get(name=list_one[1]) else: LogPrint().info("----------------操作类型或key值错误----------------") @staticmethod def close_db(): r = RedisDeal().conn_db() r.connection_pool.disconnect()
class MysqlDeal: lock = threading.Lock() dcc = DealCommonCfg() my_list = dcc.read_config('mysql') # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0]] = i[1] host = my_dic['host'] user = my_dic['user'] password = my_dic['password'] port = int(my_dic['port']) charset = my_dic['charset'] db = my_dic['db'] def conn_db(self): # LogPrint().info("----------------正在连接mysql服务器----------------") conn = pymysql.connect( # 连接数据库,其实就是建立一个pymysql.connect()的实例对象conn,该对象有commit()、rollback()、cursor()等属性 host=self.host, user=self.user, password=self.password, port=self.port, charset=self.charset, db=self.db) # LogPrint().info("----------------连接mysql服务器成功----------------") cur = conn.cursor() # 通过游标(指针)cursor的方式操作数据库,该代码作用是得到当前指向数据库的指针 return conn, cur def select_db(self, conn, cur, sql): # 查询数据库,查询条件中需要使用传入的参数 self.lock.acquire() try: cur.execute(sql) except pymysql.err.InterfaceError: MysqlDeal.reconnection_db(conn, cur, sql) finally: self.lock.release() result = cur.fetchall() if len(result) == 0: # LogPrint().info("----------------查询结束,数据库无数据----------------") return 0 else: return result def other_operate_db(self, conn, cur, sql): # 增、删、改数据库的操作 self.lock.acquire() # 获取锁 try: cur.execute(sql) conn.commit() except pymysql.err.InterfaceError: MysqlDeal.reconnection_db(conn, cur, sql) finally: self.lock.release() # 释放锁 @staticmethod def close_db(conn, cur): cur.close() conn.close() # 数据库断开异常时,用来重连 @staticmethod def reconnection_db(conn, cur, sql): conn.ping(reconnect=True) cur.execute(sql) conn.commit()
class InterfaceDeal: # s = requests.session() # s.keep_alive = False # 关闭多余连接 md = MysqlDeal() conn, cur = md.conn_db() dcc = DealCommonCfg() my_list = dcc.read_config('operating_environment') # 获得配置文件中的信息内容 my_dic = {} # 将获得的内容转换为字典类型 for i in my_list: my_dic[i[0]] = i[1] host = my_dic['one_test_host'] assert_result = '' # 接口请求返回的数据断言后的结果,三种类型success、fail、error response_last = '' # 接口请求的最后结果,需要存储到数据库中的response字段的值 def __init__(self, num, api_purpose, request_url, request_method, request_data_type, request_data, check_point, test_describe, relevance_case): self.num = num # 用例编号 self.api_purpose = api_purpose # 接口名称 self.api_host = self.host # 接口域名 self.request_url = request_url # 接口地址 self.request_method = request_method # 请求方法 self.request_data_type = request_data_type # 请求数据类型 self.request_data = request_data # 请求数据 self.check_point = check_point # 断言内容 self.test_describe = test_describe # 测试描述 self.relevance_case = relevance_case # 关联用例 # 接口调用函数 def interface_test(self): if self.request_method == 'POST' and self.request_data_type != 'File': assert_result = self.post_deal() return assert_result, self.response_last elif self.request_method == 'GET': assert_result = self.get_deal() return assert_result, self.response_last elif self.request_data_type == 'File': assert_result = self.post_deal() return assert_result, self.response_last else: LogPrint().info("----------------请求方法或类型有误----------------") def post_deal(self): data = eval(self.request_data) # 将str类型转换成字典类型 payload = json.dumps(data) headers = {'content-type': "application/json"} # headers = InterfaceHeadDeal().sign_headers(payload, self.request_data_type) url = self.api_host + self.request_url LogPrint().info('-------------调用第' + self.num + '个测试用例的接口-------------:' + url) try: response = '' if self.request_data_type in ('Data', 'File'): response = requests.post(url=url, data=payload, headers=headers, timeout=5) elif self.request_data_type == 'Form': # 以form形式发送post请求,只需将请求的参数构造成一个字典,传入给request.post()的data参数即可 response = requests.post(url=url, data=data, timeout=5) status = response.status_code resp1 = response.text if status == 200: resp2 = CommonMethod.response_data_deal(resp1) # 对接口返回的数据进行处理,如结果中存在转义字符需要存储到数据库中,则需要进行数据的相应处理 else: resp2 = resp1 # 如果接口请求是404、415等非正常的状态码,那么返回的数据就不需要进行处理,直接存入数据库中即可 assert_result = self.common_code_one(status, resp2) self.response_last = resp2 return assert_result except Timeout: assert_result = self.common_code_two(url) self.response_last = "null" return assert_result def get_deal(self): url = self.api_host + self.request_url LogPrint().info('-------------调用第' + self.num + '个测试用例的接口-------------:' + url) try: response = requests.get(url=url, params=self.request_data, timeout=5) status = response.status_code resp1 = response.text.encode("utf-8") # resp = response.read() if type(resp1 == "bytes"): resp1 = str(resp1, encoding="utf-8") assert_result = self.common_code_one(status, resp1) return assert_result except Timeout: assert_result = self.common_code_two(url) return assert_result # 公共函数,主要用于结果判断和处理 def common_code_one(self, status, resp): sql = self.sql_deal('fail', resp) if status == 200: LogPrint().info('----------------返回结果成功----------------:' + resp) self.assert_deal(resp) if self.assert_result == 'success': LogPrint().info('----------------测试断言结果----------------:' + '第' + str(self.num) + '个测试用例断言:通过') sql = self.sql_deal('success', resp) self.md.other_operate_db(self.conn, self.cur, sql) assert_result = 'success' else: LogPrint().info('----------------测试断言结果----------------:' + '第' + str(self.num) + '个测试用例断言:失败') self.md.other_operate_db(self.conn, self.cur, sql) assert_result = 'fail' else: LogPrint().error('----------------返回结果失败----------------:' + '第' + str(self.num) + '个测试用例的接口请求失败!! [ ' + str(status) + ' ], ' + resp) assert_result = 'error' self.md.other_operate_db(self.conn, self.cur, sql) return assert_result def common_code_two(self, url): LogPrint().error('----------------返回结果失败----------------:' + '第' + str(self.num) + '个测试用例的接口请求超时响应,请注意!! [ ' + url + ' ]') assert_result = 'error' # 测试结果失败的用例信息存入数据库 sql = self.sql_deal('fail', ' ') self.md.other_operate_db(self.conn, self.cur, sql) mail_title = '接口响应超时: ' + self.api_purpose + ':' + url MailSend().overtime_warn(mail_title) return assert_result """ 断言结果函数,断言的方式有两种,一种是code和msg的值断言(包括正常和异常断言),另一种是接口关键数据断言,校验具体返回的数据字段值,所以excel中的 check_point数据格式必须是这三种:包含code、包含msg、或者"${openId}=test"格式,最后格式的符号和预期值可以变化 """ def assert_deal(self, resp): check_point_list = self.check_point.split(",") check_result_list = [] for i in check_point_list: # check_point中可能需要多个校验的数据,所以要用逗号分隔符对字符串进行切片 if 'code' in i or 'msg' in i: # 这里是判断是否是code和msg断言,前两种是正常和异常的code、msg断言 if i in resp: check_result_list.append(True) else: check_result_list.append(False) else: # 这种情况是接口关键数据断言,校验具体返回的数据字段值 # i必须是'{"openId":"$openId"}'这种格式,这里是对excel中的check_point格式进行了转换,excel中的格式必须是"${openId}>0"这种 i_one = i.split("{") i_two = i_one[1].split("}") i_three = '$' + i_two[0] i_four = str({i_two[0]: i_three}) request_data_last = CommonMethod.request_data_deal(i_four, resp) i_nine = i_two[1] i_ten = i_nine.split("\"")[0] if i_ten[1] != '=' and i_ten[1] != '<' and i_ten[1] != '>': i_five = i_ten[0] # 断言语句中的符号 i_six = i_ten[1:] # 断言语句中的预期数据字段值 else: i_five = i_ten[:2] i_six = i_ten[2:] i_seven = eval(request_data_last) i_eight = i_seven[i_two[0]] # 从resp中拿出来的需要校验的实际数据字段值 if i_five == '>': check_result_list.append(str(i_eight) > str(i_six)) elif i_five == '<': check_result_list.append(str(i_eight) < str(i_six)) elif i_five == '=': check_result_list.append(str(i_eight) == str(i_six)) elif i_five == '>=': check_result_list.append(str(i_eight) >= str(i_six)) elif i_five == '<=': check_result_list.append(str(i_eight) <= str(i_six)) elif i_five == '!=': check_result_list.append(str(i_eight) != str(i_six)) else: LogPrint().info('--------断言语句中的比较符号有误,暂时只支持6种,请核对!---------') if False in check_result_list: self.assert_result = 'fail' else: self.assert_result = 'success' def sql_deal(self, result, response): # 如果请求的接口关联用例的值不为空,则需要从数据库中查询该关联的用例url值, A接口关联B接口后,因为A执行之前其关联的B用例接口一定执行了,所以直接从数据库中查找即可 if int(self.relevance_case) != 0: sql_two = 'select url from test_case where case_id = %d order by create_time desc ' \ 'limit 1' % int(self.relevance_case) result = self.md.select_db(self.conn, self.cur, sql_two) relevance_case_url = result[0][0] # 所关联的接口的url值,需要存储到数据库中relevance_case_url字段的值 else: relevance_case_url = "" url = self.api_host + self.request_url create_time = datetime.datetime.now() center_temp = '\"' if center_temp in str(self.request_data): # 这里接口请求的参数中包含"符号,需要将这个符号转义,这里r的意思是原始字符串,r'\"'的意思就是\"的字符,不是转义的意思 request_data_last = "\'" + str(self.request_data).replace(center_temp, r'\"') + "\'" else: request_data_last = "\'" + str(self.request_data) + "\'" if center_temp in str(self.check_point): check_point_last = "\'" + str(self.check_point).replace(center_temp, r'\"') + "\'" else: check_point_last = "\'" + str(self.request_data) + "\'" if result == 'success': status = 1 else: status = 0 # 这里需要将参数都加上''符号,不然在sql语句中不是string类型,如请求方法时post,在sql中需要变为'post'才正确 sql = "insert into test_case(case_id,request_method,request_data_type,interface_name,url," \ "request_data,assert_fail_reason,test_describe,status,response,relevance_case_url,create_time) " \ "values(%d,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)" % \ (int(self.num), "\'" + self.request_method + "\'", "\'" + self.request_data_type + "\'", "\'" + self.api_purpose + "\'", "\'" + url + "\'", request_data_last, check_point_last, "\'" + self.test_describe + "\'", status, "\'" + response + "\'", "\'" + relevance_case_url + "\'", "\'" + str(create_time) + "\'") return sql