def main(): now = time.strftime("%Y-%m-%d %H_%M_%S") filename = REPORT_DIR + '/' + now + do_config("report", "html_report_name") file = open(filename, 'wb') runner = HTMLTestRunnerNew.HTMLTestRunner( stream=file, verbosity=do_config("report", "verbosity"), title=do_config("report", "title"), description=do_config("report", "description"), tester=do_config("report", "tester")) runner.run(suite) file.close() # 关闭文件对象
def test_invest(self, nt): case_id = nt.case_id url = self.base_url + nt.url data1 = ReText.invest_parametrization(nt.data) actual_response = self.one_request(nt.method, url, data1) # 发起投标请求 try: # 判断状态码是否200,异常状态不用做返回值的断言 self.assertEqual(200, actual_response.status_code, msg="测试【{}】时,请求失败!状态码为【{}】".format( nt.title, actual_response.status_code)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.invest_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e if actual_response.json().get('msg') == "加标成功": # 解决用例对loadid的依赖关系 check_sql = nt.check_sql if check_sql: sql = ReText.invest_parametrization(check_sql) loan_id = self.do_mysql(sql)['Id'] # ReText.loan_id = loan_id # 查库取出加标后最大id,动态为Retext类生成类属性 # 反射解决了不能互相导包,全局变量值更新的问题 setattr(ReText, "loan_id", loan_id) # 反射,动态为对象(类)生成实例属性(类属性) expected = json.loads(nt.expected, encoding='utf8') # 字典类型的期望值 try: self.assertEqual(expected, actual_response.json(), msg="执行[{}]失败".format(nt.title)) if expected.get( "msg") == "竞标成功": # 竞标成功的用例,断言数据库投资金额是否正确。用户剩余金额也可以断言,未写 check_sql = nt.check_sql sql = ReText.invest_parametrization(check_sql) actual_amount = self.do_mysql(sql).get( 'Amount') # 查库取出投资人投资记录,是Decimal类型,需要转换类型 actual_amount = round(float(actual_amount), 2) expect_amount = eval(data1).get( 'amount') # data1为字符串类型的字典, get期望充值金额 self.assertEqual( expect_amount, actual_amount, msg="执行[{}]失败,数据库中投资的金额错误,期望投资{},实际投资{}".format( nt.title, expect_amount, actual_amount)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.invest_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e else: self.invest_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "pass_msg"))
def test_login(self, nt): case_id = nt.case_id url = self.base_url + nt.url data1 = ReText.recharge_parametrization(nt.data) check_sql = nt.check_sql if check_sql: sql = ReText.recharge_parametrization(check_sql) # sql = "SELECT `LeaveAmount` FROM future.`member` WHERE `Id`=%s" before_amount = self.do_mysql(sql)[ 'LeaveAmount'] # 查库取出投资人充值前的金额,是Decimal类型,需要转换类型 before_amount = round(float(before_amount), 2) actual_response = self.one_request(nt.method, url, data1) # 发起充值请求 try: # 判断状态码是否200,异常状态不用做返回值的断言 self.assertEqual(200, actual_response.status_code, msg="测试【{}】时,请求失败!状态码为【{}】".format( nt.title, actual_response.status_code)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.recharge_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e try: self.assertIn(nt.expected, actual_response.text, msg="执行[{}]失败".format(nt.title)) # assertIn必须是两个序列类型 if check_sql: sql = ReText.recharge_parametrization(check_sql) after_amount = self.do_mysql(sql)[ 'LeaveAmount'] # 查库取出投资人充值前的金额,是Decimal类型,需要转换类型 after_amount = round(float(after_amount), 2) expect_amount = eval(data1).get( 'amount') # data1为字符串类型的字典, get期望充值金额 actual_amount = round(after_amount - before_amount, 2) # 实际充值金额 self.assertEqual( expect_amount, actual_amount, msg="执行[{}]失败,数据库中充值的金额错误,期望充值{},实际充值{}".format( nt.title, expect_amount, actual_amount)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.recharge_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e else: self.recharge_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "pass_msg"))
class TestRegister(unittest.TestCase): base_url = do_config("interface", "base_url") login_excel = ExcelHandler(CASE_DATA_DIR, 'login') @classmethod def setUpClass(cls): cls.one_request = HttpRequests() do_logger.info("\n{:=^50s}".format("开始执行登录接口用例")) @classmethod def tearDownClass(cls): cls.one_request.close() do_logger.info("\n{:=^50s}".format("执行登录接口用例结束")) @data(*login_excel.read_excel()) def test_login(self, nt): case_id = nt.case_id url = self.base_url + nt.url data1 = ReText.login_parametrization(nt.data) expected = json.loads(nt.expected) actual = self.one_request(nt.method, url, data1) try: self.assertEqual(expected, actual.json(), msg="执行[{}]失败".format(nt.title)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.login_excel.write_excel(case_id + 1, actual.text, do_config("msg", "fail_msg")) raise e else: self.login_excel.write_excel(case_id + 1, actual.text, do_config("msg", "pass_msg"))
def __init__(self, isconsole=True): self.logger = logging.getLogger(do_config('log', 'logger_name')) # 1 定义日志器的名字 self.logger.setLevel(logging.DEBUG) # 2 指定日志收集器的日志等级 file_log_dir = os.path.join(LOG_DIR, do_config("log", "log_file_name")) # 日志文件路径 # 有bug,PermissionError:[WinError 32] 另一个程序正在使用日志文件 # 解决方案1:每个模块都实例化一个日志器对象 # 方案2 安装并导入第三方模块 pip install concurrent-log-handler # file_handle = RotatingFileHandler(file_log_dir, # maxBytes=do_config('log', 'maxBytes'), # backupCount=do_config('log', 'backupCount'), encoding='utf8') # 3 定义文件handle对象,日志回滚 file_handle = ConcurrentRotatingFileHandler( file_log_dir, maxBytes=do_config('log', 'maxBytes'), backupCount=do_config('log', 'backupCount'), encoding='utf8') # 3 定义文件handle对象,日志回滚 file_handle.setLevel(do_config( 'log', 'file_handle_level')) # 4 指定文件handle对象的日志等级 formatter = logging.Formatter(do_config('log', 'formatter')) # 5 定义日志格式对象 file_handle.setFormatter(formatter) # 6 设置文件handle格式 self.logger.addHandler(file_handle) # 7 日志收集器与handle对接 if isinstance(isconsole, bool): if isconsole: console_handle = logging.StreamHandler() # 定义控制台handle对象 console_handle.setLevel( do_config('log', 'console_handle_level')) # 设置控制台handle对象的日志等级 console_handle.setFormatter(formatter) # 设置控制台handle格式 self.logger.addHandler(console_handle) # 日志收集器与控制台handle对接 else: raise ValueError("isconsole为布尔类型")
def test_login(self, nt): case_id = nt.case_id url = self.base_url + nt.url data1 = ReText.login_parametrization(nt.data) expected = json.loads(nt.expected) actual = self.one_request(nt.method, url, data1) try: self.assertEqual(expected, actual.json(), msg="执行[{}]失败".format(nt.title)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.login_excel.write_excel(case_id + 1, actual.text, do_config("msg", "fail_msg")) raise e else: self.login_excel.write_excel(case_id + 1, actual.text, do_config("msg", "pass_msg"))
class TestAdd(unittest.TestCase): base_url = do_config("interface", "base_url") add_excel = ExcelHandler(CASE_DATA_DIR, 'add') @classmethod def setUpClass(cls): cls.one_request = HttpRequests() cls.do_mysql = MysqlHandler() do_logger.info("\n{:=^50s}".format("开始执行加标接口用例")) @classmethod def tearDownClass(cls): cls.one_request.close() do_logger.info("\n{:=^50s}".format("执行加标接口用例结束")) cls.do_mysql.close() @data(*add_excel.read_excel()) def test_login(self, nt): case_id = nt.case_id url = self.base_url + nt.url data1 = ReText.add_parametrization(nt.data) check_sql = nt.check_sql if check_sql: sql = ReText.add_parametrization(check_sql) before_id = self.do_mysql(sql)['Id'] # 查库取出加标前最大id,可能是null actual_response = self.one_request(nt.method, url, data1) # 发起加标请求 try: # 判断状态码是否200,异常状态不用做返回值的断言 self.assertEqual(200, actual_response.status_code, msg="测试【{}】时,请求失败!状态码为【{}】".format( nt.title, actual_response.status_code)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.add_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e try: self.assertEqual(json.loads(nt.expected, encoding='utf8'), actual_response.json(), msg="执行[{}]失败".format(nt.title)) if check_sql: sql = ReText.add_parametrization(check_sql) after_id = self.do_mysql(sql)['Id'] # 查库取出加标后最大id # 最大标id不一致,则表明数据插入成功,断言成功 self.assertNotEqual(before_id, after_id, msg="执行[{}]失败,数据库中插入加标记录失败!".format( nt.title)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.add_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e else: self.add_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "pass_msg"))
def test_login(self, nt): case_id = nt.case_id url = self.base_url + nt.url data1 = ReText.add_parametrization(nt.data) check_sql = nt.check_sql if check_sql: sql = ReText.add_parametrization(check_sql) before_id = self.do_mysql(sql)['Id'] # 查库取出加标前最大id,可能是null actual_response = self.one_request(nt.method, url, data1) # 发起加标请求 try: # 判断状态码是否200,异常状态不用做返回值的断言 self.assertEqual(200, actual_response.status_code, msg="测试【{}】时,请求失败!状态码为【{}】".format( nt.title, actual_response.status_code)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.add_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e try: self.assertEqual(json.loads(nt.expected, encoding='utf8'), actual_response.json(), msg="执行[{}]失败".format(nt.title)) if check_sql: sql = ReText.add_parametrization(check_sql) after_id = self.do_mysql(sql)['Id'] # 查库取出加标后最大id # 最大标id不一致,则表明数据插入成功,断言成功 self.assertNotEqual(before_id, after_id, msg="执行[{}]失败,数据库中插入加标记录失败!".format( nt.title)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.add_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e else: self.add_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "pass_msg"))
def __init__(self, filename, sheetname='', max_col=do_config('excel', 'max_column')): """ 初始化 :param filename: excel文件路径名 :param sheetname: 需要定位的表单名 :param max_col: 读取的最大列数(扣除待写的列) """ self.filename = filename # 从常量文件中读取excel绝对路径 self.sheetname = sheetname self.data_list = [] self.wb = load_workbook(self.filename) self.ws = self.wb[sheetname] if sheetname != '' else self.wb.active self.max_col = max_col self.data_header = tuple( self.ws.iter_rows(max_row=1, max_col=self.max_col, values_only=True))[0] self.Cases = namedtuple("Cases", self.data_header)
def write_excel(self, row, *data_tuple): """ 写入excel :param row: 单元格的行号 int :param data_tuple: 待写入的值 """ other_wb = load_workbook(self.filename) other_ws = other_wb[self.sheetname] if isinstance(row, int) and (1 < row <= other_ws.max_row): try: columns = do_config( 'excel', 'columns', iseval=True) # 读取配置文件中单元格的列号,类型可以为int,list,tuple,'' except SyntaxError: # columns选项值为空时或者类型不正确时,默认写入列号为max_col的后两个 if len(data_tuple) != 2: do_logger.error('写入的列数与值的个数不匹配,写入失败!') raise IndexError('tuple index out of range') other_ws.cell(row, self.max_col + 1, value=data_tuple[0]) other_ws.cell(row, self.max_col + 2, value=data_tuple[1]) other_wb.save(self.filename) else: if isinstance(columns, (list, tuple)): sort_columns = list(set(columns)) sort_columns.sort() if len(data_tuple) != len(sort_columns): do_logger.error('写入的列数与值的个数不匹配,写入失败!') raise IndexError('tuple index out of range') for i in sort_columns: other_ws.cell(row, i, value=data_tuple[sort_columns.index(i)]) other_wb.save(self.filename) elif isinstance(columns, int): other_ws.cell(row, columns, value=data_tuple[0]) other_wb.save(self.filename) else: do_logger.error('传入的行号错误,行号必须是大于1的整数!写入失败!')
def create_new_user(regname, pwd="12345678"): handle_mysql = MysqlHandler() request = HttpRequests() sql = "SELECT `Id` FROM future.`member` WHERE `MobilePhone`=%s" url = do_config('interface', 'base_url') + "/member/register" while True: phone = handle_mysql.create_unregist_phone() data = {"mobilephone": phone, "pwd": pwd, "regname": regname} request('post', url, data) result = handle_mysql(sql, (phone, )) if result: id = result['Id'] break user_dict = { regname: { 'id': id, 'regname': regname, 'pwd': pwd, 'phone': phone } } request.close() handle_mysql.close() return user_dict
class TestRecharge(unittest.TestCase): base_url = do_config("interface", "base_url") recharge_excel = ExcelHandler(CASE_DATA_DIR, 'recharge') @classmethod def setUpClass(cls): cls.one_request = HttpRequests() user_data = { "mobilephone": user_config("invest_user", "phone"), "pwd": user_config("invest_user", "pwd") } cls.one_request('post', cls.base_url + '/member/login', user_data) cls.do_mysql = MysqlHandler() do_logger.info("\n{:=^50s}".format("开始执行充值接口用例")) @classmethod def tearDownClass(cls): cls.one_request.close() do_logger.info("\n{:=^50s}".format("执行充值接口用例结束")) cls.do_mysql.close() @data(*recharge_excel.read_excel()) def test_login(self, nt): case_id = nt.case_id url = self.base_url + nt.url data1 = ReText.recharge_parametrization(nt.data) check_sql = nt.check_sql if check_sql: sql = ReText.recharge_parametrization(check_sql) # sql = "SELECT `LeaveAmount` FROM future.`member` WHERE `Id`=%s" before_amount = self.do_mysql(sql)[ 'LeaveAmount'] # 查库取出投资人充值前的金额,是Decimal类型,需要转换类型 before_amount = round(float(before_amount), 2) actual_response = self.one_request(nt.method, url, data1) # 发起充值请求 try: # 判断状态码是否200,异常状态不用做返回值的断言 self.assertEqual(200, actual_response.status_code, msg="测试【{}】时,请求失败!状态码为【{}】".format( nt.title, actual_response.status_code)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.recharge_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e try: self.assertIn(nt.expected, actual_response.text, msg="执行[{}]失败".format(nt.title)) # assertIn必须是两个序列类型 if check_sql: sql = ReText.recharge_parametrization(check_sql) after_amount = self.do_mysql(sql)[ 'LeaveAmount'] # 查库取出投资人充值前的金额,是Decimal类型,需要转换类型 after_amount = round(float(after_amount), 2) expect_amount = eval(data1).get( 'amount') # data1为字符串类型的字典, get期望充值金额 actual_amount = round(after_amount - before_amount, 2) # 实际充值金额 self.assertEqual( expect_amount, actual_amount, msg="执行[{}]失败,数据库中充值的金额错误,期望充值{},实际充值{}".format( nt.title, expect_amount, actual_amount)) except AssertionError as e: do_logger.error('用例执行失败!具体异常为:{}'.format(e)) self.recharge_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "fail_msg")) raise e else: self.recharge_excel.write_excel(case_id + 1, actual_response.text, do_config("msg", "pass_msg"))