前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python接口测试数据管理

Python接口测试数据管理

作者头像
清风穆云
发布2021-08-09 11:26:32
5940
发布2021-08-09 11:26:32
举报
文章被收录于专栏:QA一隅

问题思考

在接口测试过程中,由于有些接口类型并不是安全的,比如DELETE类型,上一次请求之后下一次再请求结果就不一样了。甚至有时接口之间的数据还会相互干扰, 导致接口断言失败时不能断定到底是接口程序引起的错误,还是测试数据变化引起的错误,那么该如何有效解决这个问题呢?

解决思路

通过测试数据库,每轮测试之前将数据初始化,这样避免数据干扰。

Django数据库管理

在之前我们的接口项目django_resutful使用的数据库是Python自带的SQLite3

Django还支持以下几种数据库:

  • PostgreSQL
  • MySQL
  • Oracle

接下来我们将会以Mysql来进行演示。

mysql简介

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件。

mysql下载安装

下载地址:https://dev.mysql.com/downloads/installer/

安装教程:自学网-Mysql教程

Navicat

Navicat是一套数据库管理工具,专为简化数据库的管理及降低系统管理成本而设。Navicat 是以直觉化的图形用户界面而建的,让你可以以安全并且简单的方式创建、组织、访问并共用信息。

Django迁移MySql

修改Setting配置

首先打开setting.pyDATABASES 修改如下:

代码语言:javascript
复制
DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'ENGINE':'django.db.backends.mysql',
        'HOST':'127.0.0.1',
        'PORT':'3306',
        'NAME':'django_restful',
        'USER':'root',
        'PASSWORD':'',
        'OPTIONS':{
                   'isolation_level':None,
                   'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", },

    }
}

也就是将原来sqllite换成Mysql

安装MySQLdb驱动

再打开django_restful中的__init__.py,添加如下代码:

代码语言:javascript
复制
import  pymysql
pymysql.install_as_MySQLdb()

上面代码表示安装MySQLdb驱动。

连接数据库

使用Navicat连接数据库如下所示:

然后创建数据库django_restful

创建Models

Django提供了完善的模型(model)层来创建和存取数据,它包含你所储存数据的必要字段和行为。通常,每个模型对应数据库中唯一的一张表。

打开api中的models.py 创建如下代码:

代码语言:javascript
复制
from django.db import models

# Create your models here.
class User(models.Model):
      username=models.CharField(max_length=100)
      email=models.CharField(max_length=100)
      groups=models.CharField(max_length=100)

      def __str__(self):
          return self.username


class Group(models.Model):
    name=models.CharField(max_length=100)

    def __str__(self):
        return self.name

上面代码表示创建数据库的表,一个是User,另外一个是Group

其中def __str__()的作用是美化字段的显示,方便查看。如果没有__st__方法,显示的结果是类似<__main__.Test object at 0x0000022D6D1387B8>

Django模型字段常用类型

导入Models

创建好Model后需要分别在serializers.pyviews.py来导入,同时去掉danjo默认的数据库。

serializers.py

代码语言:javascript
复制
# from django.contrib.auth.models import User,Group
from rest_framework import serializers
from api.models import User,Group

views.py

代码语言:javascript
复制
# from django.contrib.auth.models import User,Group
from rest_framework import viewsets
from api.serializers import UserSerializer,GroupSerializer
from api.models import User,Group

数据库迁移

代码语言:javascript
复制
python manage.py makemigrations api

python manage.py migrate

迁移完成之后需要重新设置一个超级管理员账户,然后登录。

代码语言:javascript
复制
python manage.py createsuperuser

数据库初始化

封装初始化操作

数据初始化操作主要包括:数据库连接,数据清除、数据插入、关闭数据库。在api项目下面新建一个目录test_project 然后创建文件:mysql_action.py

代码语言:javascript
复制
from pymysql import connect
import yaml

class DB():
        def __init__(self):
        '''连接数据库'''
        print('connect db')
        self.conn=connect(host='127.0.0.1',user='root',
                     password='',db='django_restful')

    def clear(self,table_name):
        '''清除表中数据'''
        print('clear db...')
        clear_sql = 'truncate ' + table_name + ';'
        with self.conn.cursor() as cursor:
            #清除外键约束
            cursor.execute("set foreign_key_checks=0;")
            cursor.execute(clear_sql)
        self.conn.commit()

    def insert(self, table_name, table_data):
        '''插入数据'''
        print('insert db...')

        #遍历数据
        for key in table_data:
            table_data[key] = "'" + str(table_data[key]) + "'"

        key = ','.join(table_data.keys())
        value = ','.join(table_data.values())

        print(key)
        print(value)

        insert_sql = "insert into " + table_name + "(" + key + ")" + "values" + "(" + value + ")"
        print(inser_sql)

        with self.conn.cursor() as cursor:
            cursor.execute(insert_sql)
        self.conn.commit()

    def close(self):
        '''关闭数据库连接'''
        print('close db')
        self.conn.close()

    def init_data(self, datas):
        '''初始化数据'''
        print('init db...')
        for table, data in datas.items():
            self.clear(table)
            for d in data:
                self.insert(table, d)
        self.close()


if __name__ == '__main__':
    db=DB()
    #调试各个方法
    db.clear('api_user')
    db.clear('api_group')
    user_data={'id':1,'username':'51zxw','email':'51zxw@163.com'}
    db.insert('api_user',user_data)
    group_data={'id':1,'name':'Developer'}
    db.insert('api_group',group_data)
    db.close()

    #初始化数据
    f=open('datas.yaml','r')
    datas=yaml.load(f)
    db.init_data(datas)

封装初始化数据

我们将初始化数据使用Yaml来封装,可以将数据与代码分离,方便测试数据的维护。在test_project目录中创建datas.yaml数据内容如下:

代码语言:javascript
复制
api_user:
   - id: 1
     username: sutune
     email: sutune@163.com
     groups: http://127.0.0.1:8000/groups/1/

   - id: 2
     username: 51zxw
     email: 51zxw@163.com
     groups: http://127.0.0.1:8000/groups/2/

api_group:
   - id: 1
     name: Developer
   - id: 2
     name: Tester

执行初始化后数据如下:

测试用例封装

test_project下面创建一个测试模块test_diango_restful.py

test_django_restful.py

代码语言:javascript
复制

import requests
import unittest
from mysql_action import DB
import yaml

class UserTest(unittest.TestCase):
    def setUp(self):
        self.base_url='http://127.0.0.1:8000/users'
        self.auth=('51zxw','zxw20182018')

    def test_001_get_user(self):
        r=requests.get(self.base_url+'/1/',auth=self.auth)
        result=r.json()

        self.assertEqual(result['username'],'sutune')
        self.assertEqual(result['email'],'sutune@163.com')

    # @unittest.skip('skip add user')
    def test_002_add_user(self):
        form_data={'id':3,'username':'zxw666','email':'zxw666@qq.com','groups':'http://127.0.0.1:8000/groups/2/'}
        r=requests.post(self.base_url+'/',data=form_data,auth=self.auth)
        result=r.json()

        self.assertEqual(result['username'],'zxw666')

    # @unittest.skip('skip test_delete_user')
    def test_003_delete_user(self):
        r=requests.delete(self.base_url+'/2/',auth=self.auth)

        self.assertEqual(r.status_code,204)

    def test_004_update_user(self):
        form_data={'email':'zxw2018@163.com'}
        r=requests.patch(self.base_url+'/1/',auth=self.auth,data=form_data)
        result=r.json()

        self.assertEqual(result['email'],'zxw2018@163.com')

    def test_005_no_auth(self):
        r=requests.get(self.base_url)
        result=r.json()

        self.assertEqual(result['detail'],'Authentication credentials were not provided.')

class GroupTest(unittest.TestCase):
    def setUp(self):
        self.base_url='http://127.0.0.1:8000/groups'
        self.auth=('51zxw','zxw20182018')

    def test_001_group_developer(self):
        r=requests.get(self.base_url+'/1/',auth=self.auth)
        result=r.json()

        self.assertEqual(result['name'],'Developer')

  
    def test_002_add_group(self):
        form_data={'name':'Pm'}
        r=requests.post(self.base_url+'/',auth=self.auth,data=form_data)
        result=r.json()

        self.assertEqual(result['name'],'Pm')

    def test_003_update_group(self):
        form_data={'name':'Boss'}
        r=requests.patch(self.base_url+'/2/',auth=self.auth,data=form_data)
        result=r.json()

        self.assertEqual(result['name'],'Boss')

    def test_004_detele_group(self):
        r=requests.delete(self.base_url+'/1/',auth=self.auth)

        self.assertEqual(r.status_code,204)


if __name__ == '__main__':
    db=DB()
    f = open('datas.yaml', 'r')
    datas = yaml.load(f)
    db.init_data(datas)
    unittest.main()


这样每次迭代回归测试就不用担心数据环境相互干扰的问题了。

执行用例&测试报告

test_project目录下面创建reports目录,然后新建run.py模块。

代码语言:javascript
复制
import unittest
from BSTestRunner import BSTestRunner
from mysql_action import DB
import time
import yaml

#数据初始化操作
db=DB()
f = open('datas.yaml', 'r')
datas = yaml.load(f)
db.init_data(datas)

#指定测试用例和测试报告的路径
test_dir = '.'
report_dir = './reports'

#加载测试用例
discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_django_restful.py')

#定义报告的文件格式
now = time.strftime("%Y-%m-%d %H_%M_%S")
report_name = report_dir + '/' + now + ' test_report.html'

#运行用例并生成测试报告
with open(report_name, 'wb') as f:
    runner = BSTestRunner(stream=f, title=" API Test Report", description="Django Restful Api Test Report")
    runner.run(discover)

最后生成的测试报告如下:

日志配置

在自动化测试项目中,日志是非常重要的一个部分,特别是当运行出现错误时,需要查看日志来分析定位解决问题。在test_project目录下面创建日志配置文件 log.conf 下面日志配置文件,定义的日志的输出格式,输出路径等信息。然后创建文件夹logs存放日志信息。

代码语言:javascript
复制
[loggers]
keys=root,infoLogger

[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler

[logger_infoLogger]
handlers=consoleHandler,fileHandler
qualname=infoLogger
propagate=0

[handlers]
keys=consoleHandler,fileHandler

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=form01
args=('./logs/runlog.log', 'a')

[formatters]
keys=form01,form02

[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

run.py引入配置文件

代码语言:javascript
复制
import unittest
from BSTestRunner import BSTestRunner
import time,yaml
from mysql_action import DB

import logging.config

#引入日志配置文件
CON_LOG='log.conf'
logging.config.fileConfig(CON_LOG)
logging=logging.getLogger()

#初始化数据
db=DB()
f=open('datas.yaml','r')
datas=yaml.load(f)
db.init_data(datas)

test_dir='.'
report_dir='./reports'

discover=unittest.defaultTestLoader.discover(test_dir,pattern='test_django_restful.py')

now=time.strftime('%Y-%m-%d %H_%M_%S')
report_name=report_dir+'/'+now+' test_report.html'

with open (report_name,'wb') as f:
    runner=BSTestRunner(stream=f,title='API Test Report',description='Django Restful API Test Report')
    logging.info('============API Test================')
    runner.run(discover)

mysql_action.py添加日志

代码语言:javascript
复制
from pymysql import connect
import yaml
import logging

class DB():
    def __init__(self):
        logging.info('=============init data=================')
        logging.info('connect db...')
        self.conn=connect(host='127.0.0.1',user='root',password='',db='django_restful')

    def clear(self,table_name):
        logging.info('clear db...')
        clear_sql='truncate '+table_name+';'
        with self.conn.cursor() as cursor:
            cursor.execute('set foreign_key_checks=0;')
            cursor.execute(clear_sql)
        self.conn.commit()

    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())

        logging.info(key)
        logging.info(value)

        insert_sql='insert into '+table_name+'('+key+')'+'values'+'('+value+')'
        logging.info(insert_sql)

        with self.conn.cursor() as cursor:
            cursor.execute(insert_sql)
        self.conn.commit()

    def close(self):
        logging.info('close db')
        self.conn.close()
        logging.info('==============init data finished!=============')

    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()
    # db.clear('api_user')
    # db.clear('api_group')
    # user_data={'id':1,'username':'zxw2018','email':'zxw2018@163.com'}
    # db.insert('api_user',user_data)
    # db.close()

    f=open('datas.yaml','r')
    datas=yaml.load(f)
    db.init_data(datas)

最后在test_django_restful.py添加日志。

代码语言:javascript
复制
import requests
import unittest
from  mysql_action import DB
import yaml
import logging

class UserTest(unittest.TestCase):
    def setUp(self):
        self.base_url='http://127.0.0.1:8000/users'
        self.auth=('51zxw','zxw20182018')


    def test_001_get_user(self):
        logging.info('test_001_get_user')
        r=requests.get(self.base_url+'/1/',auth=self.auth)
        result=r.json()

        self.assertEqual(result['username'],'sutune')
        self.assertEqual(result['email'],'sutune@163.com')

    def test_002_add_user(self):
        logging.info('test_002_add_user')
        form_data={'username':'zxw666','email':'zxw666@163.com','groups':'http://127.0.0.1:8000/groups/2/'}
        r=requests.post(self.base_url+'/',data=form_data,auth=self.auth)
        result=r.json()

        self.assertEqual(result['username'],'zxw666')
        self.assertEqual(result['email'],'zxw666@163.com')

    
    def test_003_update_user(self):
        logging.info('test_003_update_user')
        form_data={'email':'zxw2018@163.com'}
        r=requests.patch(self.base_url+'/1/',data=form_data,auth=self.auth)
        result=r.json()

        self.assertEqual(result['email'],'zxw2018@163.com')

    def test_004_delete_user(self):
        logging.info('test_004_delete_user')
        r=requests.delete(self.base_url+'/2/',auth=self.auth)

        self.assertEqual(r.status_code,204)

    def test_005_no_auth(self):
        logging.info('test_005_no_auth')
        r=requests.get(self.base_url)
        result=r.json()

        self.assertEqual(result['detail'],'Authentication credentials were not provided.')


class GroupTest(unittest.TestCase):
    def setUp(self):
        self.base_url='http://127.0.0.1:8000/groups'
        self.auth=('51zxw','zxw20182018')


    def test_001_group_developer(self):
        logging.info('test_001_group_developer')
        r=requests.get(self.base_url+'/1/',auth=self.auth)
        result=r.json()

        self.assertEqual(result['name'],'Developer')

    def test_002_add_group(self):
        logging.info('test_002_add_group')
        form_data={'name':'Pm'}
        r=requests.post(self.base_url+'/',data=form_data,auth=self.auth)
        result=r.json()

        self.assertEqual(result['name'],'Pm')

    def test_003_update_group(self):
        logging.info('test_003_update_group')
        form_data={'name':'Boss'}
        r=requests.patch(self.base_url+'/2/',data=form_data,auth=self.auth)
        result=r.json()

        self.assertEqual(result['name'],'Boss')

    def test_004_delete_group(self):
        logging.info('test_004_delete_group')
        r=requests.delete(self.base_url+'/1/',auth=self.auth)
        self.assertEqual(r.status_code,204)

if __name__ == '__main__':
    db=DB()
    f=open('datas.yaml','r')
    datas=yaml.load(f)
    db.init_data(datas)
    unittest.main()

运行完成之后可以在logs目录里面看到如下log:

代码语言:javascript
复制
2018-08-20 11:25:57,456 mysql_action.py[line:7] INFO =============init data=================
2018-08-20 11:25:57,456 mysql_action.py[line:8] INFO connect db...
2018-08-20 11:25:57,465 mysql_action.py[line:12] INFO clear db...
2018-08-20 11:25:57,469 mysql_action.py[line:26] INFO email,username,id,groups
2018-08-20 11:25:57,469 mysql_action.py[line:27] INFO 'sutune@163.com','sutune','1','http://127.0.0.1:8000/groups/1/'
2018-08-20 11:25:57,470 mysql_action.py[line:30] INFO insert into api_user(email,username,id,groups)values('sutune@163.com','sutune','1','http://127.0.0.1:8000/groups/1/')
2018-08-20 11:25:57,471 mysql_action.py[line:26] INFO email,username,id,groups
2018-08-20 11:25:57,471 mysql_action.py[line:27] INFO '51zxw@163.com','51zxw','2','http://127.0.0.1:8000/groups/2/'
2018-08-20 11:25:57,473 mysql_action.py[line:30] INFO insert into api_user(email,username,id,groups)values('51zxw@163.com','51zxw','2','http://127.0.0.1:8000/groups/2/')
2018-08-20 11:25:57,473 mysql_action.py[line:12] INFO clear db...
2018-08-20 11:25:57,477 mysql_action.py[line:26] INFO name,id
2018-08-20 11:25:57,477 mysql_action.py[line:27] INFO 'Developer','1'
2018-08-20 11:25:57,477 mysql_action.py[line:30] INFO insert into api_group(name,id)values('Developer','1')
2018-08-20 11:25:57,478 mysql_action.py[line:26] INFO name,id
2018-08-20 11:25:57,478 mysql_action.py[line:27] INFO 'Tester','2'
2018-08-20 11:25:57,479 mysql_action.py[line:30] INFO insert into api_group(name,id)values('Tester','2')
2018-08-20 11:25:57,479 mysql_action.py[line:37] INFO close db
2018-08-20 11:25:57,479 mysql_action.py[line:39] INFO ==============init data finished!=============
2018-08-20 11:25:57,658 run.py[line:28] INFO ============API Test================
2018-08-20 11:25:57,659 test_django_restful.py[line:60] INFO test_001_group_developer
2018-08-20 11:25:57,783 test_django_restful.py[line:67] INFO test_002_add_group
2018-08-20 11:25:57,903 test_django_restful.py[line:75] INFO test_003_update_group
2018-08-20 11:25:58,009 test_django_restful.py[line:83] INFO test_004_delete_group
2018-08-20 11:25:58,106 test_django_restful.py[line:14] INFO test_001_get_user
2018-08-20 11:25:58,211 test_django_restful.py[line:22] INFO test_002_add_user
2018-08-20 11:25:58,309 test_django_restful.py[line:32] INFO test_003_update_user
2018-08-20 11:25:58,405 test_django_restful.py[line:40] INFO test_004_delete_user
2018-08-20 11:25:58,496 test_django_restful.py[line:46] INFO test_005_no_auth

集成Jenkins

使用Jenkins持续集成平台我们可以自动定时执行自动化任务,自动发送邮件推送测试报告,这样会有效提高自动化测试执行效率。

打开Jenkins创建项目django_restful_api 然后在构建中选择 "执行Windows批处理" 填入如下内容:

代码语言:javascript
复制
d:
cd D:\django_restful\api\test_project
C:\Python35\python.exe run.py

执行完成后可以查看到控制台输出

最后在对应的报告文件夹可以看到生成对应的测试报告。

Jenkins定时构建语法
代码语言:javascript
复制
* * * * *

(五颗星,中间用空格隔开)

  • 第一个*表示分钟,取值0~59
  • 第二个*表示小时,取值0~23
  • 第三个*表示一个月的第几天,取值1~31
  • 第四个*表示第几月,取值1~12
  • 第五个*表示一周中的第几天,取值0~7,其中0和7代表的都是周日
使用案例

每天下午18点定时构建一次

代码语言:javascript
复制
0 18 * * 1-5

每天早上8点构建一次

代码语言:javascript
复制
0 8 * * *

每30分钟构建一次:

代码语言:javascript
复制
H/30 * * * *
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 QA一隅 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题思考
  • 解决思路
  • Django数据库管理
    • mysql简介
      • mysql下载安装
        • Navicat
        • Django迁移MySql
          • 修改Setting配置
            • 安装MySQLdb驱动
              • 连接数据库
                • 创建Models
                  • 导入Models
                    • 数据库迁移
                    • 数据库初始化
                      • 封装初始化操作
                        • 封装初始化数据
                          • 测试用例封装
                            • 执行用例&测试报告
                              • 日志配置
                                • 集成Jenkins
                                  • Jenkins定时构建语法
                              相关产品与服务
                              云数据库 SQL Server
                              腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档