本文主要介绍了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 文件 存放日志信息