基于Python Excel的自动化测试工具
搞小工具是一件挺有趣味的一件事情,尤其是还能提高工作效率的时候,这里简要介绍下基于python和excel的自动化接口测试小工具。
流程:
一般而言,对接口测试 的配置项如下:
4. 状态管理:
接口编码 是否使用
5. 接口定义
接口名称 接口描述 接口地址 请求方法 请求头 payload
6. 相应判断
HTTP响应状态码判断 HTTP返回值包含字符串判断 HTTP值相等判断
'''
测试案例POJO
'''
class TestCase:
def __init__(self, code, name ,description, url, method, header ,payload, check_rule_set):
self.code = code
self.name = name
self.description = description
self.url = url
self.method = method
self.header = header
self.payload = payload
self.check_rule_set = check_rule_set
'''
测试结果
'''
class CheckResult:
def __init__(self, result, reason):
self.result = result
self.reason = reason
开始干活,首先写一个Http 类。
class HttpUtil:
@classmethod
def post(cls, url, headers, payload ):
logging.info("request url: %s, headers: %s, payload: %s", url, headers, payload)
pre_headers = json.loads(headers)
post_headers = cls.valid(pre_headers)
response = requests.post(url, json=json.loads(payload), headers=post_headers)
logging.info("response: %s, %s", response.status_code, response.json())
return response
@classmethod
def get(cls, url, headers):
logging.info("request url: %s, headers: %s", url, headers)
pre_headers = json.loads(headers)
post_headers = cls.valid(pre_headers)
response = requests.get(url, headers=post_headers)
logging.info("response: %s, %s", response.status_code, response.json())
return response
@classmethod
def put(cls, url, headers, payload ):
logging.info("request url: %s, headers: %s, payload: %s", url, headers, payload)
pre_headers = json.loads(headers)
post_headers = cls.valid(pre_headers)
response = requests.put(url, json=json.loads(payload), headers=post_headers)
logging.info("response: %s, %s", response.status_code, response.json())
return response
接着是Excel操作类:
'''
Csv操作类
'''
class ExcelUtil:
max_col = 0
max_row = 0
constant_value_set = {}
case_set = {}
input_api_codes = []
log_dir = "./"
out_put_file = "./defaul_log.log"
@classmethod
def load(cls, file_name):
'''
加载配置
'''
xl = xlrd.open_workbook(file_name)
table = xl.sheet_by_name(u"测试用例")
cls.max_col = table.ncols
cls.max_row = table.nrows
log_dir_value = table.cell(1,1).value
if log_dir_value != None:
cls.log_dir = log_dir_value
out_put_file_value = table.cell(2,1).value
if out_put_file_value != None:
cls.out_put_file = out_put_file_value
'''
加载变量
'''
for i in range(1,cls.max_col):
if table.cell(6,i).value == 'Y':
cls.constant_value_set[table.cell(4,i).value] = table.cell(5,i).value
'''
加载case
'''
for j in range(9,cls.max_row):
if table.cell(j,1).value == 'Y':
cls.input_api_codes.append(table.cell(j,0).value)
cls.case_set[table.cell(j,0).value] = cls.parse_row_to_case(table.row(j))
'''
常量替换
'''
for _, case in cls.case_set.items():
for const_key, const_value in cls.constant_value_set.items():
const_key_plus = "${" + const_key +"}"
case.url = case.url.replace(const_key_plus, const_value)
case.header = case.header.replace(const_key_plus, const_value)
case.payload = case.payload.replace(const_key_plus, const_value)
for key, rule in case.check_rule_set.items() :
rule = rule.replace(const_key_plus, const_value)
case.check_rule_set[key] = rule
@classmethod
def parse_row_to_case(cls,row_value):
code = row_value[0].value
name = row_value[2].value
description = row_value[3].value
url = row_value[4].value
method = row_value[5].value
header = row_value[6].value
payload = row_value[7].value
check_rule_set = {}
expected_http_code = row_value[8].value
expected_contains_value = row_value[9].value
expected_equal_value = row_value[10].value
if expected_http_code != None and expected_http_code != '':
check_rule_set[CHECK_HTTPCODE] = str(int(expected_http_code))
if expected_contains_value != None and expected_contains_value != '':
check_rule_set[CHECK_VALUE_CONTAINS] = expected_contains_value
if expected_equal_value != None and expected_equal_value!= '':
check_rule_set[CHECK_VALUE_EQUAL] = expected_equal_value
return TestCase(code, name, description, url, method, header, payload, check_rule_set)
@classmethod
def get_case(cls):
return cls.case_set
@classmethod
def get_input_api_codes(cls):
return cls.input_api_codes
@classmethod
def get_output_file(cls):
return cls.out_put_file
@classmethod
def get_log_dir(cls):
return cls.log_dir
上面都没啥说的,很简单,接着写测试主类和适配各种规则的校验方法:
'''
测试工具类
'''
class TestTools:
def __init__(self):
self.input_case = {}
self.test_result = {}
def init(self):
self.input_case = ExcelUtil.get_case()
self.input_api_codes = ExcelUtil.get_input_api_codes()
self.output_file = ExcelUtil.get_output_file()
def do_test(self):
self.init()
if self.input_api_codes == None:
logging.error("input_api is None")
return
for api_code in self.input_api_codes:
if api_code in self.input_case.keys():
case = self.input_case[api_code]
try :
logging.info("tset api code is: %s", api_code)
self.test_item(case)
except Exception as e:
logging.error("exception: %s", e.message)
check_result = CheckResult(False, "exception :" + e.message)
self.test_result[case.code] = check_result
else:
logging.error("No such case: %s", api_code)
self.generate_test_result()
def test_item(self, case):
if case != None:
method = case.method
response = None
logging.info("method is :%s", method)
if method == HTTP_METHOD_GET:
response = HttpUtil.get(case.url, case.header)
elif method == HTTP_METHOD_POST:
response = HttpUtil.post (case.url, case.header, case.payload)
elif method == HTTP_METHOD_PUT:
response = HttpUtil.http_get(case.url, case.header, case.payload)
else:
pass
check_result = self.check(response,case.check_rule_set)
else:
check_result = CheckResult(False,"case is none")
self.test_result[case.code] = check_result
def check(self,response, check_rule_set):
logging.info("response: %s, check_rule_set keys: %s", response, check_rule_set.keys())
try:
if check_rule_set == None:
return CheckResult(True, "check_rule_set is None" )
if response == None:
return CheckResult(False, "response is None" )
else:
for key,value in check_rule_set.items():
if key == CHECK_HTTPCODE:
if str(response.status_code) != value:
return CheckResult(False, " error :CHECK_HTTPCODE, response code is: " + str(response.status_code) + ', expected: ' + str(value))
else:
logging.info(" pass CHECK_HTTPCODE ")
if key == CHECK_VALUE_CONTAINS:
if str(response.json()).find(value) == False:
return CheckResult(False, "error :CHECK_VALUE_CONTAINS, response not contains: " + value)
else:
logging.info(" pass CHECK_VALUE_CONTAINS ")
if key == CHECK_VALUE_EQUAL:
if SPLIT_MARK not in value:
chek_result = self.check_equal(response, value)
if chek_result.result == False:
return chek_result
else:
check_equal_items = value.split(SPLIT_MARK)
for check_equal_item in check_equal_items:
chek_result = self.check_equal(response, check_equal_item)
if chek_result.result == False:
return chek_result
logging.info(" pass CHECK_VALUE_EQUAL ")
return CheckResult(True, "all rule set check succeed" )
except Exception as e:
logging.error("catch exception: %s", e.message)
check_result = CheckResult(False, "exception :" + e.message)
logging.info("check result: %s", str(check_result.result)+ " " +check_result.reason)
return check_result
def check_equal(self ,response, check_equal_item):
response_json = response.json()
check_equal_item_relation = check_equal_item.split(EQUAL_MARK)
if len(check_equal_item_relation) != 2:
return CheckResult(False, "invalid rules: "+ check_equal_item)
response_value = None
expected_value = check_equal_item_relation[1]
try:
key_list = check_equal_item_relation[0].split(POINT_SPLIT_MARK)
print key_list
temp_response_json = response_json
for key_item in key_list:
temp_response_json = temp_response_json[key_item]
response_value = temp_response_json
except Exception as e:
logging.error("check_equal_item failed ,rule: %s, error: %s",check_equal_item, e.message)
if response_value == None:
return CheckResult(False, "check rules response value is None ,rules: "+ check_equal_item)
if str(response_value) == str(expected_value):
return CheckResult(True, "parse equal check rule: "+ check_equal_item)
else:
return CheckResult(False, "error when equal check rule: "+ check_equal_item + "response value is: " + str(response_value)+ ", expected value is: " + str(expected_value))
def generate_test_result(self):
count = len(self.test_result)
success_count = 0
for _, result_item in self.test_result.items():
if result_item.result:
success_count = success_count + 1
failed_count = count - success_count
with codecs.open(self.output_file, 'a+', encoding='utf-8') as f:
f.write('***********************TEST RESSULT**************************\n')
format_time='%Y-%m-%d_%a_%H-%M-%S'
time_tup = time.localtime(time.time())
cur_time = time.strftime(format_time, time_tup)
time_string = cur_time + ' count: ' + str(count) + ', success: ' + str(success_count) +', failed: '+ str(failed_count) + '\n'
f.write(time_string)
for key ,result in self.test_result.items():
result_string = key + " " + self.input_case[key].name + " " + str(result.result) + " " + result.reason + "\n"
f.write(result_string)
f.write('\n\n')
包含上述描述的各校验规则及统计 序列化方法(还不全完善),最后写main方法:
def main():
file_name = "自动化测试Case.xlsx"
import sys
if len(sys.argv) > 1:
file_name = sys.argv[1]
ExcelUtil.load(file_name)
set_log(ExcelUtil.get_log_dir())
test_tool = TestTools()
test_tool.do_test()
if __name__ == "__main__":
main()
TEST RESSULT***
2019-09-05_Thu_19-17-58 count: 2, success: 1, failed: 1
IAM_API_1 根据用户ID获取用户信息 False exception :Value for header {companyid: 2} must be of type str or bytes, not
VIN_API_1 车辆信息测试 True all rule set check succeed
TEST RESSULT***
2019-09-05_Thu_19-28-26 count: 2, success: 1, failed: 1
IAM_API_1 根据用户ID获取用户信息 False error when equal check rule: data.iovCurrentVehicle=144115205311821369response value is: 144116206311726088, expected value is: 144115205311821369
VIN_API_1 车辆信息测试 True all rule set check succeed
TEST RESSULT***
2019-09-05_Thu_19-29-44 count: 2, success: 2, failed: 0
IAM_API_1 根据用户ID获取用户信息 True all rule set check succeed
VIN_API_1 车辆信息测试 True all rule set check succeed
还可以,就当随便练练手了