基于python实现的http+json协议接口自动化测试框架(实用改进版)

感情迁移 提交于 2020-04-06 21:57:59

目录
1、 写在前面 .................................................................................................................................................................. 1
2、 开发环境 .................................................................................................................................................................. 1
3、 大致流程 .................................................................................................................................................................. 2
4、 框架简介 .................................................................................................................................................................. 2
5、 运行结果展示 .......................................................................................................................................................... 3
6、 文件与配置 .............................................................................................................................................................. 3
7、 测试接口实例 .......................................................................................................................................................... 4
 1.登陆接口................................................................................................................................................................... 4
 2.支付密码更改接口 ................................................................................................................................................... 6
8、 数据库设计 .............................................................................................................................................................. 7
9、 测试用例、测试数据准备....................................................................................................................................... 8
10、 模块与类、函数设计......................................................................................................................................... 10

11、 代码实现............................................................................................................................................................. 10

 a) class congfighttp.ConfigHttp ................................................................................................................. 10 

b) class getdb.GetDB ....................................................................................................................................... 12 

c) class configrunmode.ConfigRunMode ....................................................................................................... 13 

d) class globalconfig.Global ....................................................................................................................... 14 

e) class datastruct.DataStruct ................................................................................................................... 15 

f) class test_interface_case.TestInterfaceCase,test_interface_case.ParametrizedTestCase . 16 

g) class runcase.RunCase ............................................................................................................................... 20 

h) htmlreport. HtmlReport .............................................................................................................................. 22 

i) main................................................................................................................................................................. 25

12、 源码下载............................................................................................................................................................. 26

 

1、 写在前面

抛砖引玉,仅供参考

 

2、 开发环境
win7 64位
JetBrains PyCharm 4.0.5
Python 3.3.5
MariaDB-5.5.45-centos6-x86_64
文件下载地址:http://pan.baidu.com/s/1sj1Lzw5
CentOS 6.5-x86_64
下载地址:http://www.centoscn.com/CentosSoft/iso/2013/1205/2196.html
Mysql Connector/Python Windows (x86, 64-bit), MSI Installer Python 3.3
下载地址:http://dev.mysql.com/downloads/connector/python/

其它:公司Linux mysql数据库服务器、应用服务器

 

3、 大致流程

下图展示了框架实现的业务流程

 

 

4、 框架简介
1、可通过配置文件http_config.ini,对要测试接口服务器的IP、域名,和端口信息进行灵活配置。
2、可通过配置文件db_config.ini,对测试数据库,应用数据库服务器主机IP,端口,用户名,密码等灵活配置。
3、可通过配置文件run_case_config.ini灵活配置需要用例运行模式,需要运行的用例ID列表
4、对常见HTTP的POST,GET请求方法进行封装(支持自由扩展以便增加其它方法
5、支持JSON(含嵌套对象格式的json数据,如{ "orderTotalPrice":95, "goods":[{"shopId":987654354,"goodsId":108, "goodsNumber":1}]}})格式数据提交
博客:http://blog.sina.com.cn/ishouke 软件性能测试交流 QQ 群:7156436
6、通过数据库对接口测试用例、前置(数据)条件进行管理,可做到每个用例之间相互独立,互不依赖
7、针对接口返回结果,支持数据库级别的数据校验
8、可按测试时间及给定文件名,生成对应时间的html可视化报告,报告内容包含测试耗时,测试执行用例总数,执行成功、失败、出错用例数统计;还有单个用例的执行情况(ID,用例名称,(自定义)接口名称,接口URL,接口参数,运行结果等)

9、可根据实际情况,在此框架的基础上进行修改、扩展

 

5、 运行结果展示

 

 

 

6、 文件与配置
1) http配置文件
用途:配置接口服务器IP,端口
http_config.ini
[DEFAULT]
[HTTP]
host = 192.168.1.174
port = 9101
2) 用例配置文件
run_case_config.ini
[RUNCASECONFIG]
runmode = 1
case_id = [1,2]
说明:runmode:1--运行全部用例 0--运行指定用例,case_id:list,存放runmode=0时需要运行用例的用例ID,ID之间采用英文逗号分割
3) 数据库配置文件
db_config.ini
[DATABASE1]
host = 192.168.30.80
port = 3306
user = testacc
passwd = test1234
db = testdb
charset = utf8
[DATABASE2]
host = 192.168.1.161
port = 3306
user = yinheonline
passwd = 123456
db = yh_yinheonline
charset = utf8

说明:DATABASE1 测试数据库testdb的配置,DATABASE2存放企业应用数据库服务器配置

 

7、 测试接口实例
以下两个接口为例,以代码形式对进行框架使用进行简单说明
 1.登陆接口
用于用户登陆。
 接口方向
客户端 -> 服务端
 接口协议
接口地址:$1dcp_Home/interface/user/login
接口协议:JSON

HTTP请求方式:GET

 消息请求 

字段名                                                 数据类型                                   默认值                       必填项                          备注

mobile                                                string                                                                            是                                  手机号
password                                          string                                                                            是                                  用户密码,采用MD5加密
model                                                 string                                                                            条件                              手机型号,比如:IPHONE 5 32GB BLACK
osInfo                                                 string                                                                            是                                   操作系统信息,比如iOS_6.1.4
SN                                                       string                                                                            条件                              设备的序列号或唯一标识设备的编码。
说明:必填项为条件表示根据系统的安全级别做限制和约束。客户端和服务端做同步约定。

消息请求样例:

 ?mobile=13812345678&password=xxsdfjddafd&model=iphone5&osInfo=iOS_6.1.4&SN=041552E97A96

 消息响应
服务器验证成功后,通过在HTTP response的header中设置cookie实现session机制。客户端收到cookie后,需要在后续的HTTP request请求中携带cookie信息,否则服务端将鉴权不通过,返回错误码:30003(无效的cookie信息)。

字段元素定义如下: 

字段名                                                  数据类型                               默认值                               必填项                              备注

userId                                                  int                                                                                      是                                      用户ID
imgBig                                                String                                                                                 否                                     头像(大)
imgSmall                                           String                                                                                 否                                      头像(小)
nikeName                                          String                                                                                 是                                      昵称
sex                                                      int                                                                                        是                                      性别;0-男,1-女
address                                             String                                                                                  否                                      长居地
cityId                                                    int                                                                                        否                                      市ID
cityName                                            String                                                                                  否                                      市名
payPasswordFlag                            int                                                                                        是                                      支付密码标识:0-未设定 1-已设定

成功时,返回JSON数据包:

 { 

           "code":0, 

            "msg":"登录成功!", 

            "data":{ 

                           "userId":"1223434", 

                            "imgBig":"/user/img/001.png",

                            "imgSmall":"/user/img/001_small.png",

                            "nikeName":"贪吃羊",

                            "sex":0,

                             "address":"深圳" 

                          }

}

 

 

 2.支付密码更改接口
会员修改基本资料时,可以对支付密码进行单独修改。
 接口方向
客户端 -> 服务端
 接口协议
接口地址:$1dcp_Home/interface/user/modifyPayPwd
接口协议:JSON
HTTP请求方式:POST
 消息请求

字段列表如下:

 字段名                                      数据类型                                    默认值                                 必填项                                         备注

userId                                       int                                                                                             是                                                 会员ID
payPassword                         String                                                                                       是                                                 原支付密码
newPayPwd                            String                                                                                       是                                                 新支付密码
confNewPayPwd                   String                                                                                       是                                                  确认新支付密码

消息请求样例:

 { 

               "userId":2345, 

"payPassword":"3iu4oi5u3o5o3iu5o3453o3o3",

  "newPayPwd":"40986546i45io6j45o6j3o45", 

"confNewPayPwd":"40986546i45io6j45o6j3o45"

}

 

 

8、 数据库设计
1)创建数据库

CREATE DATABASE IF NOT EXISTS testdb DEFAULT CHARACTER SET utf8;

 

2)testdb数据库中建立数据表
test_data存放测试用例(接口)相关数据
CREATE TABLE test_data
(
case_id INT NOT NULL UNIQUE, # 用例ID,唯一
http_method VARCHAR(5) NOT NULL, # http方法(POST、GET
request_name VARCHAR(30), # 自定义接口名称 建议格式:接口名-测试简单说明
request_url VARCHAR(200) NOT NULL, # 接口URL
request_param VARCHAR(1000) NOT NULL, # 接口所需的全部或部分参数--python字典形式的字符串
test_method VARCHAR(50) NOT NULL, # 测试方法,一个测试用例对应一个方法
test_desc VARCHAR(2000) NOT NULL # 测试描述--主要描述这个用例的测试点、测试目的
);
pre_condition_data存放完成接口运行前置条件所需的数据
CREATE TABLE pre_condition_data
(
case_id INT NOT NULL, # 用例ID
step INT NOT NULL, # 执行该用例ID需要的第一步、第一个前提条件的step ID
request_url VARCHAR(200) NOT NULL, # 接口URL
request_param VARCHAR(1000) NOT NULL, # 接口参数--python字典形式的字符串
other VARCHAR(1000), # 保留字段,可能是执行用例需要预先执行的sql语句等
test_desc VARCHAR(2000) NOT NULL, # 数据描述--描述这条数据用途
PRIMARY KEY(case_id, step)

);

 

说明:接口的前提条件往往是另一个接口的预先执行、或预先执行后生成的数据,也就说前一个接口的输出是后一个接口的输入,所以这里主要设计为存储接口url和接口参数,供预先执行前一个接口时使用

 

test_result存放测试结果
CREATE TABLE test_result
(
case_id INT NOT NULL UNIQUE, # 用例ID
http_method VARCHAR(5) NOT NULL, # http方法(POST、GET
request_name VARCHAR(30), # 自定义接口名称
request_url VARCHAR(200) NOT NULL, # 接口URL
request_param VARCHAR(1000) NOT NULL, # 接口所需的全部参数--python字典形式的字符串
test_method VARCHAR(50) NOT NULL, # 接口测试方法
test_desc VARCHAR(2000) NOT NULL, # 数据描述--描述测试目的
result VARCHAR(20) NOT NULL, # 测试结果
reason VARCHAR(20) # 测试失败原因

);

 

9、 测试用例、测试数据准备
# 测试登录用例数据
INSERT INTO test_data(
case_id,
http_method,
request_name,
request_url,
request_param,
test_method,
test_desc)
VALUES(1,
'GET',
'login-normal',

'/appServer/interface/user/login?',

'{"mobile":"18259001552",

 "password":"e10adc3949ba59abbe56e057f20f883e", 

"model":"小米2S", 

"SN":"041552E97A96",

 "osInfo":"android4.0"}',

'test_login_normal',
'测试登录,正向'

);

 

#测试修改支付密码的前置条件数据准备
INSERT INTO pre_condition_data(
case_id,
step,
request_url,
request_param,
other,
test_desc
)
VALUES(2,
1,
'/appServer/interface/user/login?',
'{"mobile":"18259001552",
"password":"e10adc3949ba59abbe56e057f20f883e",
"osInfo":"android4.0"}',
'',
'测试修改密码步骤1:登录'
);
# 测试修改支付密码数据准备
INSERT INTO test_data(
case_id,
http_method,
request_name,
request_url,
request_param,
test_method,
test_desc)
VALUES(2,
'POST',
'modifyPayPwd_normal',
'/appServer/interface/user/modifyPayPwd',
'{"userId":2910057590,
"payPassword":"e10adc3949ba59abbe56e057f20f883e",
"newPayPwd":"e10adc3949ba59abbe56e057f20f883e",
"confNewPayPwd":"e10adc3949ba59abbe56e057f20f883e"}',
'test_chpasswd_normal',
'测试更改密码,正向'

);

 

10、 模块与类、函数设计

 a) class confighttp. ConfigHttp

配置要测试接口服务器的ip、端口、域名等信息,封装http请求方法,http头设置等

b) class getdb. GetDB

负责配置测试数据库,应用数据库服务器的ip、端口,用户名,密码等信息,返回数据库连接

c) class configrunmode. ConfigRunMode

负责配置并获取运行模式,运行用例ID

d) class globalconfig.Global

负责全局初始化配置

e) class runner.DataStruct

定义结构体,于接收从测试数据库testdb中test_data表读取的测试数据,记录要写入测试报告的数据

f) class test_interface_case.TestInterfaceCase, test_interface_case.ParametrizedTestCase

负责管理测试用例对应的测试方法,相关的数据处理

g) runcase. RunCase

负责运行测试用例及相关的数据处理

h) htmlreport.HtmlReport

负责生成测试报告

i) main

程序运行入口文件

 

11、 代码实现 

a) class congfighttp.ConfigHttp

#!/usr/bin/env python 

# -*- coding:utf-8 -*- 

__author__ = 'shouke' 

import urllib.request 

import http.cookiejar

import urllib.parse

 import json 

import configparser 

# 配置类 

class ConfigHttp: '''配置要测试接口服务器的ip、端口、域名等信息,封装http请求方法,http头设置''' 

def __init__(self, ini_file): 

config = configparser.ConfigParser() 

# 从配置文件中读取接口服务器IP、域名,端口 

config.read(ini_file) 

self.host = config['HTTP']['host'] 

self.port = config['HTTP']['port'] 

self.headers = {}  # http 头

 #install cookie 

cj = http.cookiejar.CookieJar() 

opener = 

urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) urllib.request.install_opener(opener) 

def set_host(self, host): 

self.host = host 

def get_host(self): 

return self.host 

def set_port(self, port): 

self.port = port 

def get_port(self): 

return self.port 

# 设置http头 

def set_header(self, headers): 

self.headers = headers 

# 封装HTTP GET请求方法 

def get(self, url, params): 

params = urllib.parse.urlencode(eval(params)) # 将参数转为url编码字符串 

url = 'http://' + self.host + ':' + str(self.port) + url + params

request = urllib.request.Request(url, headers=self.headers) 

try: 

response = urllib.request.urlopen(request) 

response = response.read().decode('utf-8') ## decode函数对获取的字节数据进行解码 

json_response = json.loads(response) # 将返回数据转为json格式的数据 

return json_response 

except Exception as e: 

print('%s' % e) 

return {}

 # 封装HTTP POST请求方法 

def post(self, url, data): 

data = json.dumps(eval(data)) 

data = data.encode('utf-8') 

url = 'http://' + self.host + ':' + str(self.port) + url 

try: 

request = urllib.request.Request(url, headers=self.headers) 

response = urllib.request.urlopen(request, data) 

response = response.read().decode('utf-8') 

json_response = json.loads(response) 

return json_response 

except Exception as e: 

print('%s' % e) 

return {} 

# 封装HTTP xxx请求方法 

# 自由扩展

b) class getdb.GetDB 

#!/usr/bin/env python 

# -*- coding:utf-8 -*- 

__author__ = 'shouke' 

import configparser 

import mysql.connector 

import sys

class GetDB: 

'''配置数据库IP,端口等信息,获取数据库连接''' 

def __init__(self, ini_file, db): 

config = configparser.ConfigParser() 

# 从配置文件中读取数据库服务器IP、域名,端口 

config.read(ini_file) 

self.host = config[db]['host'] 

self.port = config[db]['port'] 

self.user = config[db]['user'] 

self.passwd = config[db]['passwd'] 

self.db = config[db]['db'] 

self.charset = config[db]['charset'] 

def get_conn(self):

 try: 

conn = mysql.connector.connect(host=self.host, port=self.port, user=self.user, password=self.passwd, database=self.db, charset=self.charset) 

return conn 

except Exception as e: 

print('%s', e) 

sys.exit()

 

c) class configrunmode.ConfigRunMode

 #!/usr/bin/env python

# -*- coding:utf-8 -*- 

__author__ = 'shouke' 

import configparser 

class ConfigRunMode: 

def __init__(self, run_case_config_file): 

config = configparser.ConfigParser() 

# 从配置文件中读取运行模式 

config.read(run_case_config_file)

 try: 

self.run_mode = config['RUNCASECONFIG']['runmode'] 

self.run_mode = int(self.run_mode) 

self.case_list = config['RUNCASECONFIG']['case_id'] 

self.case_list = eval(self.case_list) # 把字符串类型的list转换为list 

except Exception as e: 

print('%s', e) 

def get_run_mode(self): 

return self.run_mode 

def get_case_list(self): 

return self.case_list

 

d) class globalconfig.Global

 #!/usr/bin/env python 

# -*- coding:utf-8 -*- 

__author__ = 'shouke' from getdb 

import GetDB from confighttp 

import ConfigHttp from configrunmode 

import ConfigRunMode 

class Global: 

def __init__(self): 

# 读取并配置接口服务器IP,端口等信息 

self.http = ConfigHttp('../http_conf.ini')

# 读取并配置数据库服务器IP,端口等信息 

self.db1 = GetDB('../db_config.ini', 'DATABASE1') 

self.db2 = GetDB('../db_config.ini', 'DATABASE2') 

# 读取运行模式配置 

self.run_mode_config = ConfigRunMode('../run_case_config.ini') 

def get_http(self): 

return self.http 

# 返回测试数据库连接 

def get_db1_conn(self): 

return self.db1.get_conn() 

# 返回应用数据库连接 

def get_db2_conn(self): 

return self.db2.get_conn() 

# 获取运行模式配置 

def get_run_mode(self): 

return self.run_mode_config.get_run_mode() 

# 获取需要单独运行的用例列表 

def get_run_case_list(self): 

return self.run_mode_config.get_case_list() 

# 释放资源 

def clear(self): 

# 关闭数据库连接 

self.db1.get_conn().close() 

self.db2.get_conn().close()

 

e) class datastruct.DataStruct

 #!/usr/bin/env python 

# -*- coding:utf-8 -*- 

__author__ = 'shouke' 

# 定义结构体

class DataStruct: 

'''于接收读取的测试数据,记录要写入测试报告的数据''' 

def __init__(self): 

self.case_id = 0 #用例ID 

self.http_method = '' #接口http方法 

self.request_name = '' #接口ming 

self.request_url = '' #接口请求url 

self.request_param = ''#请求参数 

self.test_method = '' #测试方法 

self.test_desc = '' #测试(用力)描述 

self.result = '' #测试结果 

self.reason = '' #失败原因

 

f) class test_interface_case.TestInterfaceCase,test_interface_case.ParametrizedTestCase 

#!/usr/bin/env python 

# -*- coding:utf-8 -*- 

__author__ = 'shouke' 

import unittest 

# 测试用例(组)类 

class ParametrizedTestCase(unittest.TestCase): 

""" TestCase classes that want to be parametrized should inherit from this class. """ 

def __init__(self, methodName='runTest', test_data=None, http=None, db1_cursor=None, db2_cursor=None): 

super(ParametrizedTestCase, self).__init__(methodName) 

self.test_data = test_data 

self.http = http self.db1_cursor = db1_cursor 

self.db2_cursor = db2_cursor

class TestInterfaceCase(ParametrizedTestCase): 

def setUp(self): 

pass 

# 测试接口1

def test_login_normal(self): 

# 根据被测接口的实际情况,合理的添加HTTP头 

# header = {'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

 # 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; rv:29.0) Gecko/20100101 Firefox/29.0' 

# } 

# self.http.set_header(header) 

response = self.http.get(self.test_data.request_url, 

self.test_data.request_param) 

if {} == response: 

self.test_data.result = 'Error' 

try: 

# 更新结果表中的用例运行结果 

self.cursor.execute('UPDATE test_result SET result = %s WHERE case_id = %s', (self.test_data.result, self.test_data.case_id))self.cursor.execute('commit') 

except Exception as e: 

print('%s' % e) 

self.cursor.execute('rollback') 

return 

try: 

# 如果有需要,连接数据库,读取数据库相关值,用于和接口请求返回结果做比较 

self.db2_cursor.execute('SELECT user_id FROM 1dcq_user WHERE mobile = %s',(eval(self.test_data.request_param)['mobile'],)) 

user_id = self.db2_cursor.fetchone()[0] self.db2_cursor.close() 

# 断言 

self.assertEqual(response['code'], 0, msg='返回code不等于0') 

self.assertEqual(response['msg'], '登录成功', msg='登录失败') 

self.assertEqual(response['data']['sex'], 2, msg='sex错误') 

self.assertEqual(response['data']['cityId'], None, msg='cityId错误') 

self.assertEqual(response['data']['nikeName'], None, msg='nikeName错误') 

self.assertEqual(response['data']['cityName'], None, msg='cityName错误')

self.assertEqual(response['data']['userId'], user_id, msg='userId错误') #2910057590 

self.assertEqual(response['data']['cityName'], None, msg='cityName错误') 

self.assertEqual(response['data']['payPasswordFlag'], 1, msg='payPasswordFlag错误') 

self.assertEqual(response['data']['imgSmall'], None, msg='imgSmall错误') 

self.assertEqual(response['data']['imgBig'], None, msg='imgBig错误') 

self.test_data.result = 'Pass' 

except AssertionError as e: 

print('%s' % e) 

self.test_data.result = 'Fail' 

self.test_data.reason = '%s' % e # 记录失败原因 

# 更新结果表中的用例运行结果 

try: 

self.db1_cursor.execute('UPDATE test_result SET result = %s WHERE case_id = %s', (self.test_data.result, self.test_data.case_id))self.db1_cursor.execute('UPDATE test_result SET reason = %s WHERE case_id = %s', (self.test_data.reason, self.test_data.case_id))self.db1_cursor.execute('commit') 

except Exception as e:

print('%s' % e) 

self.db1_cursor.execute('rollback') 

# 测试接口2 

def test_chpasswd_normal(self): 

header = {'Content-Type':'application/json','charset':'utf-8'} 

self.http.set_header(header) 

# 步骤1-登录 

self.db1_cursor.execute('SELECT request_url, request_param FROM pre_condition_data WHERE case_id = %s and step=1', (self.test_data.case_id,)) 

temp_result = self.db1_cursor.fetchone() 

request_url = temp_result[0] 

request_param = temp_result[1] 

lgin_response = self.http.get(request_url, request_param) 

# 修改密码 

user_id = lgin_response['data']['userId'] 

# 获取登录接口返回的

user_id payPassword = eval(request_param)['password'] 

# 获取原密码即登录密码 # 拼接参数,作为修改支付密码接口的传入参数 

tmp_dic = {"userId":user_id, "payPassword":payPassword}

self.test_data.request_param = eval(self.test_data.request_param) 

self.test_data.request_param.update(tmp_dic) 

# 修改密码 

response = self.http.post(self.test_data.request_url, str(self.test_data.request_param)) 

if {} == response: 

self.test_data.result = 'Error' 

try: 

# 更新结果表中的用例运行结果 

self.db1_cursor.execute('UPDATE test_result SET 

result = %s WHERE case_id = %s', (self.test_data.result, self.test_data.case_id)) 

self.db1_cursor.execute('commit') 

except Exception as e: 

print('%s' % e) 

self.db1_cursor.execute('rollback') 

return 

try: 

self.assertEqual(response['code'], 0, msg='返回code不等于0') 

self.assertEqual(response['msg'],'支付密码修改成功', msg='修改支付密码失败') 

self.assertEqual(response['data'],None, msg='data不为N') 

self.test_data.result = 'Pass' 

except AssertionError as e: 

print('%s' % e) 

self.test_data.result = 'Fail' self.test_data.reason = '%s' % e # 记录失败原因 

# 更新结果表中的用例运行结果 

try: 

self.db1_cursor.execute('UPDATE test_result SET request_param = %s WHERE case_id = %s', (str(self.test_data.request_param), self.test_data.case_id))self.db1_cursor.execute('UPDATE test_result SET result = %s WHERE case_id = %s', (self.test_data.result, self.test_data.case_id))self.db1_cursor.execute('UPDATE test_result SET reason = %s WHERE case_id = %s', (self.test_data.reason, self.test_data.case_id))self.db1_cursor.execute('commit') 

except Exception as e: 

print('%s' % e) 

self.db1_cursor.execute('rollback') 

def tearDown(self): 

pass

注意:断言如果抛出了断言异常,接下去的语句也是不会执行的。

 

g) class runcase.RunCase 

#!/usr/bin/env python 

# -*- coding:utf-8 -*-

__author__ = 'shouke' 

import unittest from test_interface_case 

import TestInterfaceCase from datastruct 

import DataStruct 

global test_data 

test_data = DataStruct() 

class RunCase: 

'''运行测试用例''' 

def __init__(self): 

pass 

# 运行测试用例函数 

def run_case(self, runner, run_mode, run_case_list, db1_conn, db2_conn, http): 

global test_data 

if 1 == run_mode: # 运行全部用例 

db1_cursor = db1_conn.cursor() 

# 获取用例个数 

db1_cursor.execute('SELECT count(case_id) FROM 

test_data') test_case_num = db1_cursor.fetchone()[0] 

db1_cursor.close() 

 

# 循环执行测试用例 

for case_id in range(1, test_case_num+1): 

db1_cursor = db1_conn.cursor() 

db2_cursor = db2_conn.cursor() 

db1_cursor.execute('SELECT http_method, request_name, request_url, request_param, test_method, test_desc ' 'FROM test_data WHERE case_id = %s',(case_id,)) 

# 记录数据 

tmp_result = db1_cursor.fetchone() 

test_data.case_id = case_id 

test_data.http_method = tmp_result[0] 

test_data.request_name = tmp_result[1]

test_data.request_url = tmp_result[2] 

test_data.request_param = tmp_result[3] 

test_data.test_method = tmp_result[4] 

test_data.test_desc = tmp_result[5] test_data.result = '' test_data.reason = '' 

try: 

query = ('INSERT INTO test_result(case_id, http_method, request_name, request_url,' 'request_param, test_method, test_desc, result, reason) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s)') 

data = (test_data.case_id,test_data.http_method,test_data.request_name, test_data.request_url, test_data.request_param, test_data.test_method, test_data.test_desc, test_data.result, test_data.reason) 

db1_cursor.execute(query, data) 

db1_cursor.execute('commit') 

except Exception as e: 

# 回滚 

print('%s' % e) 

db1_cursor.execute('rollback') 

test_suite = unittest.TestSuite() 

test_suite.addTest(TestInterfaceCase(test_data.test_method, test_data, http, db1_cursor, db2_cursor)) runner.run(test_suite) 

db1_cursor.close() 

db2_cursor.close() 

else: # 运行部分用例 

# 循环执行测试用例 

for case_id in run_case_list: 

db1_cursor = db1_conn.cursor() 

db2_cursor = db2_conn.cursor() 

db1_cursor.execute('SELECT http_method, request_name, request_url, request_param, test_method, test_desc ' 'FROM test_data WHERE case_id = %s',(case_id,)) 

# 记录数据 

tmp_result = db1_cursor.fetchone() 

test_data.case_id = case_id 

test_data.http_method = tmp_result[0]

test_data.request_name = tmp_result[1] 

test_data.request_url = tmp_result[2] test_data.request_param = tmp_result[3] 

test_data.test_method = tmp_result[4] test_data.

test_desc = tmp_result[5] test_data.result = '' test_data.reason = '' 

try: 

query = ('INSERT INTO test_result(case_id, http_method, request_name, request_url,' 'request_param, test_method, test_desc, result, reason) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s)') 

data = (test_data.case_id,test_data.http_method,test_data.request_name, test_data.request_url, test_data.request_param, test_data.test_method, test_data.test_desc, test_data.result, test_data.reason) 

db1_cursor.execute(query, data) db1_cursor.execute('commit') 

except Exception as e: 

# 回滚 

print('%s' % e) 

db1_cursor.execute('rollback') 

test_suite = unittest.TestSuite() 

test_suite.addTest(TestInterfaceCase(test_data.test_method, test_data, http, db1_cursor, db2_cursor)) 

runner.run(test_suite) 

db1_cursor.close()

 

h) htmlreport. HtmlReport #!/usr/bin/env python 

# -*- coding:utf-8 -*- __author__ = 'shouke' 

from pyh import * 

import time 

import os 

class HtmlReport: 

def __init__(self, cursor):

self.title = 'test_report_page' # 网页标签名称 

self.filename = '' # 结果文件名 self.time_took = '00:00:00' # 测试耗时 

self.success_num = 0 # 测试通过的用例数 self.fail_num = 0 # 测试失败的用例数 

self.error_num = 0 # 运行出错的用例数 

self.case_total = 0 # 运行测试用例总数 

self.cursor = cursor 

# 生成HTML报告 

def generate_html(self,head, file): 

page = PyH(self.title) 

page << h1(head, align='center') # 标题居中 

page << p('测试总耗时:' + self.time_took) 

# 查询测试用例总数 

query = ('SELECT count(case_id) FROM test_result') 

self.cursor.execute(query) self.case_total = self.cursor.fetchone()[0] 

# 查询测试失败的用例数 

self.cursor.execute('SELECT count(case_id) FROM test_result WHERE result = %s',('Fail',)) 

self.fail_num = self.cursor.fetchone()[0] 

# 查询测试通过的用例数 

self.cursor.execute('SELECT count(case_id) FROM test_result WHERE result = %s',('Pass',)) 

self.success_num = self.cursor.fetchone()[0] 

# 查询测试出错的用例数 

self.cursor.execute('SELECT count(case_id) FROM test_result WHERE result = %s',('Error',)) 

self.error_num = self.cursor.fetchone()[0] 

page << p('测试用例数:' + str(self.case_total) + '&nbsp'*10 + '成功用例数:' + str(self.success_num) + '&nbsp'*10 + '失败用例数:' + str(self.fail_num) + '&nbsp'*10 + '出错用例数:' + str(self.error_num)) 

# 表格标题caption 表格边框border 单元边沿与其内容之间的空白cellpadding 单元格之间间隔为cellspacing 

tab = table( border='1', cellpadding='1', cellspacing='0', cl='table')

tab1 = page << tab tab1 << tr(td('用例ID', bgcolor='#ABABAB', align='center') + td('HTTP方法', bgcolor='#ABABAB', align='center') + td('接口名称', bgcolor='#ABABAB', align='center') + td('请求URL', bgcolor='#ABABAB', align='center') + td('请求参数/数据', bgcolor='#ABABAB', align='center') + td('测试方法', bgcolor='#ABABAB', align='center') + td('测试描述', bgcolor='#ABABAB', align='center') + td('测试结果', bgcolor='#ABABAB', align='center') + td('失败原因', bgcolor='#ABABAB', align='center')) 

# 查询所有测试结果并记录到html文档 

query = ('SELECT case_id, http_method, request_name, request_url,' 'request_param, test_method, test_desc, result, reason FROM test_result')self.cursor.execute(query) 

query_result = self.cursor.fetchall() 

for row in query_result: 

tab1<< tr(td(int(row[0]), align='center') + td(row[1]) + td(row[2]) + td(row[3], align='center') + td(row[4]) + td(row[5]) + td(row[6]) + td(row[7], align='center') + td(row[8])) 

self._set_result_filename(file) 

page.printOut(self.filename) try: 

query = ('DELETE FROM test_result') 

self.cursor.execute(query) 

self.cursor.execute('commit') 

except Exception as e:

 # 回滚 

print('%s' % e) 

self.cursor.execute('rollback') 

self.cursor.close() 

# 设置结果文件名 

def _set_result_filename(self, filename): 

self.filename = filename 

#判断是否为目录 

if os.path.isdir(self.filename): 

raise IOError("%s must point to a file" % path) 

elif '' == self.filename:

raise IOError('filename can not be empty') 

else: 

parent_path, ext = os.path.splitext(filename) 

tm = time.strftime('%Y%m%d%H%M%S', time.localtime()) 

self.filename = parent_path + tm + ext 

# 统计运行耗时 

def set_time_took(self, time): 

self.time_took = time 

return self.time_took

 

i) main #!/usr/bin/env python 

# -*- coding:utf-8 -*- 

__author__ = 'shouke' 

import datetime 

import unittest from runcase 

import RunCase from globalconfig 

import Global from htmlreport 

import HtmlReport 

if __name__ == '__main__': 

# 记录测试开始时间 

start_time = datetime.datetime.now() 

# 全局配置 

global_config = Global() 

run_mode = global_config.get_run_mode() # 运行模式 

run_case_list = global_config.get_run_case_list() # 需要运行的用例列表 

db1_conn = global_config.get_db1_conn() # 数据库连接 

db2_conn = global_config.get_db2_conn()  # 数据库连接 

http = global_config.get_http()  # http 

# 运行测试用例 

runner = unittest.TextTestRunner() 

case_runner = RunCase() 

case_runner.run_case(runner, run_mode, run_case_list, db1_conn, db2_conn, http) 

# 记录测试结束时间 

end_time = datetime.datetime.now()

# 构造测试报告 

html_report = HtmlReport(db1_conn.cursor()) 

html_report.set_time_took(str(end_time - start_time)) # 计算测试消耗时间 

# 生成测试报告 

html_report.generate_html('test report', '../report.html')

 # # 释放数据库连接资源 

global_config.clear()

12、 源码下载
http://pan.baidu.com/s/1dDoHsOx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!