接口测试

1、使用脚本测试接口与使用接口工具的对比

  • 测试数据不可控
    使用工具测试接口,必须手动向数据库添加数据,而脚本可以直接写入数据库
  • 无法测试加密接口1、使用脚本测试接口与使用接口工具的对比
  • 测试数据不可控
    使用工具测试接口,必须手动向数据库添加数据,而脚本可以直接写入数据库
  • 无法测试加密接口
    -可扩展功能不足
    不能生产HTML格式的测试报告,不能将报告自动发送到指定邮箱,不能定时任务,不能持续集成
    2、一般的接口测试工具测试过程
  • 接口工具调用被测系统的接口,并传参
  • 系统根据传参向数据库进行查询数据
  • 将查询结果组装成一定格式的数据返回给被调用者
  • 人工或者通过工具的断言功能检查接口测试的正确性
    3、接口自动化测试过程
  • 接口测试项目向测试数据库中插入测试数据(张三的个人信息)
  • 调用系统接口(传参)
  • 系统接口根据传参查询数据库,得到张三的个人信息
  • 将查询结果组装成一定格式的数据返回给被调用者
  • 通过单元测试框架断言查询结果并生产测试报告
    为了避免正式数据库被污染,建议使用测试数据库
    4、使用requests进行请求
    Requests 支持HTTP 连接保持和连接池,支持使用cookie 保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的URL 和POST 数据自动编码。现代、国际化、人性化。
    5、接口自动化测试框架


    image.png

    pyrequests 框架:
    db_fixture/: 初始化接口测试数据。
    interface/: 用于编写接口自动化测试用例。
    report/: 生成接口自动化测试报告。
    db_config.ini : 数据库配置文件。
    HTMLTestRunner.py unittest 单元测试框架扩展,生成HTML 格式的测试报告。
    run_tests.py : 执行所有接口测试用例。
    6、接口自动化整体思路
    1)配置数据库
    2)创建测试数据
    3)编写接口测试用例
    7、数据库配置

  • 在被测系统中新建一个测试数据库,并且将系统的数据库指向被测数据库
    ![Upload image.png failed. Please try again.]
  • 修改了数据库配置之后需要重新执行“python3 manage.py migrate”生成数据库表结构
  • 创建数据库配置文件db_config.ini
 Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
# 原来的数据库配置
DATABASES = {
    'default': {
        # 驱动
        'ENGINE': 'django.db.backends.mysql',
        # 主机地址
        'HOST': '127.0.0.1',
        # 端口
        'PORT': '3306',
        # 数据库名称
        'NAME': 'guest',
        # 用户名
        'USER': 'root',
        # 密码
        'PASSWORD': 'root',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
        },
    }
}
  • 简单封装数据库操作,数据库表数据的插入和清除,.../db_fixture/mysql_db.py
    该步骤是为了初始化测试数据库做准备
# coding=utf-8
import pymysql.cursors
import os
import configparser as cparser


# ======== Reading db_config.ini setting ===========
base_dir = str(os.path.dirname(os.path.dirname(__file__)))
base_dir = base_dir.replace('\\', '/')
file_path = base_dir + "/db_config.ini"

cf = cparser.ConfigParser()

cf.read(file_path,encoding='utf8')
host = cf.get("mysqlconf", "host")
port = cf.get("mysqlconf", "port")
db   = cf.get("mysqlconf", "db_name")
user = cf.get("mysqlconf", "user")
password = cf.get("mysqlconf", "password")


# ======== MySql base operating ===================
class DB:

    def __init__(self):
        try:
            # Connect to the database
            self.connection = pymysql.connect(host=host,
                                              port=int(port),
                                              user=user,
                                              password=password,
                                              db=db,
                                              charset='utf8mb4',
                                              cursorclass=pymysql.cursors.DictCursor)
        except pymysql.err.OperationalError as e:
            print("Mysql Error %d: %s" % (e.args[0], e.args[1]))

    # clear table data
    def clear(self, table_name):
        # real_sql = "truncate table " + table_name + ";"
        real_sql = "delete from " + table_name + ";"
        with self.connection.cursor() as cursor:
            cursor.execute("SET FOREIGN_KEY_CHECKS=0;")
            cursor.execute(real_sql)
        self.connection.commit()

    # insert sql statement
    def insert(self, table_name, table_data):
        for key in table_data:
            table_data[key] = "'"+str(table_data[key])+"'"
        key   = ','.join(table_data.keys())
        value = ','.join(table_data.values())
        real_sql = "INSERT INTO " + table_name + " (" + key + ") VALUES (" + value + ")"
        #print(real_sql)

        with self.connection.cursor() as cursor:
            cursor.execute(real_sql)

        self.connection.commit()

    # close database
    def close(self):
        self.connection.close()

    # init data
    def init_data(self, datas):
        for table, data in datas.items():
            self.clear(table)
            for d in data:
                self.insert(table, d)
        self.close()

"""
if __name__ == '__main__':

    db = DB()
    table_name = "sign_event"
    data = {'id':1,'name':'红米','`limit`':2000,'status':1,'address':'北京会展中心','start_time':'2016-08-20 00:25:42'}
    table_name2 = "sign_guest"
    data2 = {'realname':'alen','phone':12312341234,'email':'alen@mail.com','sign':0,'event_id':1}

    db.clear(table_name)
    db.insert(table_name, data)
    db.close()
"""

首先要读取数据库配置,获取数据库信息,如主机地址,用户名,密码,端口等
其次通过pymysql库中的connect方法连接数据库
然后封装clear方法和insert方法
8、创建测试数据

import sys

sys.path.append('D:\\pydj\\guest\\pyrequest\\db_fixture')
try:
    from mysql_db import DB
except ImportError:
    from .mysql_db import DB

# create data
datas = {
    'sign_event': [
        {'id': 1, 'name': '红米Pro发布会', '`limit`': 2000, 'status': 1, 'address': '北京会展中心',
         'start_time': '2017-08-20 14:00:00'},
        {'id': 2, 'name': '可参加人数为0', '`limit`': 0, 'status': 1, 'address': '北京会展中心',
         'start_time': '2017-08-20 14:00:00'},
        {'id': 3, 'name': '当前状态为0关闭', '`limit`': 2000, 'status': 0, 'address': '北京会展中心',
         'start_time': '2017-08-20 14:00:00'},
        {'id': 4, 'name': '发布会已结束', '`limit`': 2000, 'status': 1, 'address': '北京会展中心',
         'start_time': '2001-08-20 14:00:00'},
        {'id': 5, 'name': '小米5发布会', '`limit`': 2000, 'status': 1, 'address': '北京国家会议中心',
         'start_time': '2017-08-20 14:00:00'},
    ],
    'sign_guest': [
        {'id': 1, 'realname': 'alen', 'phone': 13511001100, 'email': 'alen@mail.com', 'sign': 0, 'event_id': 1},
        {'id': 2, 'realname': 'has sign', 'phone': 13511001101, 'email': 'sign@mail.com', 'sign': 1, 'event_id': 1},
        {'id': 3, 'realname': 'tom', 'phone': 13511001102, 'email': 'tom@mail.com', 'sign': 0, 'event_id': 5},
    ],
}


# Inster table datas
def init_data():
    """
    db = DB()
    for table, data in datas.items():
        db.clear(table)
        for d in data:
            db.insert(table, d)
    db.close()
    """
    DB().init_data(datas)


if __name__ == '__main__':
    init_data()

1)创建一个字典datas,键表示表名,值表示表里的数据
2)init_data()函数用于读取datas 字典中的数据,调用DB 类中的clear()方法清除数据库,然后,调用insert()方法插入表数据。
3)之后的测试用例就已经这些数据进行编写
9、编写测试用例


image.png
import unittest
import requests
import os, sys

parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parentdir)
from pyrequest.db_fixture import test_data


class AddEventTest(unittest.TestCase):
    """添加发布会"""

    def setUp(self):
        self.base_url = "http://127.0.0.1:8000/api/add_event/"

    def tearDown(self):
        print(self.result)

    def test_add_event_all_null(self):
        """ 所有参数为空 """
        payload = {'eid': '', '': '', 'limit': '', 'address': "", 'start_time': ''}
        r = requests.post(self.base_url, data=payload)
        self.result = r.json()
        self.assertEqual(self.result['status'], 10021)
        self.assertEqual(self.result['message'], 'parameter error')

    def test_add_event_eid_exist(self):
        """ id已经存在 """
        payload = {'eid': 1, 'name': '一加4发布会', 'limit': 2000, 'address': "深圳宝体", 'start_time': '2017'}
        r = requests.post(self.base_url, data=payload)
        self.result = r.json()
        self.assertEqual(self.result['status'], 10022)
        self.assertEqual(self.result['message'], 'event id already exists')

    def test_add_event_name_exist(self):
        """ 名称已经存在 """
        payload = {'eid': 16, 'name': '红米Pro发布会', 'limit': 2000, 'address': "深圳宝体", 'start_time': '2017'}
        r = requests.post(self.base_url, data=payload)
        self.result = r.json()
        self.assertEqual(self.result['status'], 10023)
        self.assertEqual(self.result['message'], 'event name already exists')

    def test_add_event_data_type_error(self):
        """ 日期格式错误 """
        payload = {'eid': 14, 'name': '一加5手机发布会', 'limit': 2000, 'address': "深圳宝体", 'start_time': '2017'}
        r = requests.post(self.base_url, data=payload)
        self.result = r.json()
        self.assertEqual(self.result['status'], 10024)
        self.assertIn('start_time format error.', self.result['message'])

    def test_add_event_success(self):
        """ 添加成功 """
        payload = {'eid': 17, 'name': '一加7手机发布会', 'limit': 2000, 'address': "深圳宝体", 'start_time': '2017-05-10 12:00:00'}
        r = requests.post(self.base_url, data=payload)
        self.result = r.json()
        self.assertEqual(self.result['status'], 200)
        self.assertEqual(self.result['message'], 'add event success')


if __name__ == '__main__':
    test_data.init_data()  # 初始化接口测试数据
    unittest.main()

10、集合测试用例并执行

from pyrequest.HTMLTestRunner import HTMLTestRunner
import unittest
from pyrequest.db_fixture import test_data
import time
import sys

sys.path.append('./interface')
sys.path.append('./db_fixture')

# 指定测试用例为当前文件夹下的 interface 目录
test_dir = 'D:\pydj\guest\pyrequest\interface'
print(test_dir)
discover = unittest.defaultTestLoader.discover(test_dir, pattern='*_test.py')

if __name__ == "__main__":
    # 初始化接口测试数据
    test_data.init_data()

    now = time.strftime("%Y-%m-%d %H_%M_%S")
    filename = 'D:/pydj/guest/pyrequest/report/' + now + '_result.html'
    fp = open(filename, 'wb')
    runner = HTMLTestRunner(stream=fp,
                            title='Guest Manage System Interface Test Report',
                            description='Implementation Example with: ')
    runner.run(discover)
    fp.close()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容