Django 数据管理及接口测试

本文主要介绍了django连接mysql操作,及测试用例封装,jekin集成并通过邮件发送测试报告的全过程。

作者:桃子

一 Django 数据管理

通过数据管理可以解决接口之间数据间的相互干扰,导致断言失败时不知道是接口引起的还是数据引起的错误

数据场景:

测试数据库,将数据每次测试前初始化

安装 mysql 数据库

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

下载安装 navicat 数据管理工具

下载地址: https://www.navicat.com.cn/

drango 迁移 mysql

setting 配置文件修改如下信息:

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'",

                  }

    }

}

安装数据库驱动

drango_restful 下的_init.py-文件修改代码如下:

import pymysql

pymysql.install_as_MySQLdb()

连接数据库

打开 navicat,点击连接新建如下图


新建名称 django_restful 的数据库

建 models

models 用来创建和存储数据

打开 api 下的 models.py,输入以下代码创建 user 和 group 表

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


导入 models

在 serializers.py 和 views.py 文件导入 models,同时去掉 django 默认的数据库


views.py

数据库迁移

在 cmd 输入:python manage.py makemigrations api


python manage.py migrate

如果提示 access denied error,很有可能 setting 文件中 password 不一致


python manage.py createsuperuser


重启服务网页登录,创建用户返回 navicat 查看数据,如果存在说明迁移成功

python manage.py runserver

#没有问题请忽略这一步

问题 1:误把数据库里的表格删除,重新迁移会失败

解决方案:

把整个数据库删除 django_restful,然后重新迁移执行上面的命令

封装初始化操作

包括:数据连接、清除、插入、关闭数据库

在 api 下面新建目录 test_project,然后新建 sql_action.py,输入下面的命令

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='xinsheng2',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):

        logging.info('inser 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 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)

封装初始化数据

使用 yaml 来封装数据

如果没有安装 yaml 可以在 cmd 中输入 pip install  pyyaml 安装

在 test_project  新建 datas.yaml 文件,输入下面语句

api_user:

  - id: 1

    username: sutune

    email: sutune@163.com

    api_user.groups: http://127.0.0.1:8000/groups/1/

  - id: 2

    username: 51zxw

    email: 51zxw@163.com

    api_user.groups: http://127.0.0.1:8000/groups/2/

api_group:

  - id: 1

    name: Developer

  - id: 2

    name: Tester

运行 sql_action.py,查看数据库内容如下图


问题 1:遇到一个问题无论怎么运行都提示“Ran 0 tests in 0.000s”,查了好多资料刚开始以为是代码写错了,后来发现原来是运行错误

右键选择 run as ->python run 就 ok 了

问题 2:

执行 mysql_action.py 时报错:pymysql.err.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'groups) values('1','sutune','sutune@163.com','http://127.0.0.1:8000/groups/1/')' at line 1")。是 datas.yaml 文件里的 groups 与 MySQL 的关键词冲突,导致执行 insert 语句报错,如何解决

解决方案:把 datas_yaml 文件里面的 groups 字段改成:api_user.groups 亲测有效

测试用例封装

在 test_project  新建 test_django_restful.py 文件,实现增删改查操作,每次回归就不用担心数据环境问题了,在文件中输入下面语句

import requests

import unittest

from mysql_action import DB

import yaml

from django.contrib.auth.context_processors import auth

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


    def test_002_add_user(self):

        form_data={'id':3,'username':'zxw666','email':'51zxdddw@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')   



    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':'wx@162.com'}

        r=requests.patch(self.base_url+'/1/',auth=self.auth,data=form_data)

        result=r.json() 

        self.assertEqual(result['email'],'wx@162.com')

#       

#    def test_005_no_auth(self):

#        r=requests.get(self.base_url)

#        result=r.json()

#        self.assertEqual(result['detail'],'Authentications creadentianls 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_updata_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_003_delete_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)

        unnttest.main()



执行测试用例及测试报告生成

在 test_project  新建 report 文件夹(用于存放测试结果),新建 run.py 文件,写入一下代码

import unittest

from BSTestRunner import BSTestRunner

import time,yaml

from mysql_action import DB

db=DB()

f=open('datas.yaml','r',encoding='GBK')

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)

运行完成后,在 report 目录下查看生成的测试报告

注意:report 文件夹需要执行刷新操作才显示文件


日志配置

通过配置日志信息可以快速帮我们定位问题

在 test_project  新建 log 文件夹(用于存放日志信息),新建 log.conf 文件,写入以下代码

[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 引入配置文件

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',encoding='GBK')

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 添加日志

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='root',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):

        logging.info('inser 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 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()

    f=open('datas.yaml','r')

    datas=yaml.load(f)

    db.init_data(datas)

在test_django_restful.py添加日志













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_0001_get_user')

        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_0002_add_user')

        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_updata_group(self):

        logging.info('test_0003_delete_user')

        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_delete_group(self):

        logging.info('test_0004_delete_user')

        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)

        unnttest.main()




运行run.py文件,在log文件夹下查看log信息

jekin 集成

jekin 是一个自动化测试平台,持续集成(CI)的工具

安装包路径:链接:https://pan.baidu.com/s/1JPMAb-2n2I7_7VsuZ9xmHg

提取码:jbg0

创建项目django_restful_api

构建中执行windows批处理程序

点击执行

控制台输出显示success

返回程序中可以看到日志及报告内容

定时构建每30分钟执行一次


邮件发送

在runl.py文件,更新代码,实现发送带附件的邮件执行用例、更新数据库等功能

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

import unittest

from BSTestRunner import BSTestRunner

import time,yaml

from mysql_action import DB

import logging.config

import smtplib                         

from email.mime.text import MIMEText

from email.header import Header       

import os

CON_LOG='log.conf'

logging.config.fileConfig(CON_LOG)

logging=logging.getLogger()

db=DB()

f=open('datas.yaml','r',encoding='GBK')

datas=yaml.load(f)

db.init_data(datas)

def send_mail(latest_report):

    f=open(latest_report,'rb')

    mail_content=f.read()

    f.close()

    smtpserver='smtp.163.com'

    # 发送邮箱用户名密码

    user='zxy13941778515@163.com'

    password='LSCCTNZSAQDNKWHO'

    sender='zxy13941778515@163.com'

    receives=['luoditao@126.com','zxy13941778515@163.com']

    # 发送邮件主题和内容

    subject = 'hello'

    # HTML邮件正文

    msg = MIMEText(mail_content, 'html', 'utf-8')

    msg['Subject'] = Header(subject, 'utf-8')

    msg['From'] = sender

    msg['To'] = ','.join(receives)

    smtp = smtplib.SMTP_SSL(smtpserver, 465)

    # HELO 向服务器标识用户身份

    smtp.helo(smtpserver)

    # 服务器返回结果确认

    smtp.ehlo(smtpserver)

    # 登录邮箱服务器用户名和密码

    smtp.login(user, password)

    print("Start send Email...")

    smtp.sendmail(sender, receives, msg.as_string())

    smtp.quit()

    print("Send Email end!")

def latest_report(report_dir):

    lists = os.listdir(report_dir)

    # 按时间顺序对该目录文件夹下面的文件进行排序

    lists.sort(key=lambda fn: os.path.getatime(report_dir + '\\' + fn))

    print(("new report is :" + lists[-1]))

    file = os.path.join(report_dir, lists[-1])

    print(file)

    return file

if __name__ == '__main__':

    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+' report.html'



    with open (report_name,'wb') as f:


        runner=BSTestRunner(stream=f,title='API  Report',description='Django Restful API  Report')

        logging.info('====api test ======')

        runner.run(discover)

    f.close()

    #h获取最新测试报告

    latest_report=latest_report(report_dir)

    #发送邮件报告

    send_mail(latest_report)


打开邮箱查看测试报告:

错误:运行run文件报错554

smtplib.SMTPDataError: (554, b'DT:SPM 163 smtp3,G9xpCgC3vS0Qd4Fe_IpUAQ--.78S3 15

85542929,please see http://mail.163.com/help/help_spam_16.htm?ip=60.16.239.253&h

ostid=smtp3&time=1585542929')

查看了网上的解决办法:

1.主题和附件内容不能包含test

2.将收件人包含有发件人的邮箱

总结各文件实现的功能

回顾一下每个为念实现的功能是什么


django-restful文件夹

setting.py文件 实现django数据迁移到mysql

_init_.py  实现安装数据库驱动功能

urls.py  代码配置路由信息

test_project 文件夹

models.py  在数据库中创建 user 和 group 表的model

serializers.py  定义API返回形式,返回哪些字段,返回怎样的格式等

views.py 文件  定义视图的展现形式,如何向用户展示数据,展示什么数据等

datas.yaml 文件  使用 yaml 来封装数据

test_django_restful.py 文件 实现增删改查操作,用例封装功能

run.py 文件 现发送带附件的邮件执行用例、生成测试报告、更新数据库等功能

log.conf 文件  存放日志信息

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343