在Django下测试与调试REST API的方法详解


Posted in Python onAugust 29, 2019

对于大多数研发人员来说,都期望能找到一个良好的测试/调试方法,来提高工作效率和快速解决问题。所谓调试,偏重于对某个bug的查找、定位、修复;所谓测试,是检验某个功能是否达到预期效果。测试发现问题后进行调试,从而解决问题。

对于后台研发来说,往往没有客户端研发(Windows/Android等等)那样简单有效的DEBUG方法,比如Step by Step。虽然目前有很多IDE可以实现本地调试,但是因为后台研发的环境复杂,你很难在一台机器上模拟所有的环境,比如线上的数据库只能在内网访问等等,所以很多时候需要在服务器(DEV/STG/PROD)上进行调试和测试工作。本文尝试介绍一些Django Web服务下调试/测试REST API的方法,因为是方法,那自然仁者见仁智者见智,所以也许你觉得会有用,也许会觉得毫无道理。

首先要说的是,在服务器环境,针对Django Web服务,print log是最简单有效的方法。python的语言特性决定了你可以随时添加log,重新run一下就可以看到结果。本文接下来要说的,并不是什么超脱于pring log的超级技巧,而是如何编写代码、利用工具进行有效的调试/测试工作,并尽量做到减少重复性工作。

对于任何一个REST API来说,需要进行测试的内容包括两部分:请求中的各个功能模块是否正常、整个请求是否正常。对于研发人员来说,写完代码后当然要进行一番测试来确认代码是否正常工作、是否符合需求。这时,有人选择先测试一下整个请求是否正常,如果正常就万事大吉,交付给测试同事测试了,如果不正常则进一步查看是哪个功能模块出现问题;有人选择先逐一测试各个功能模块是否正常,在确保各功能模块正常后,再进一步测试整个请求是否正常,然后交付测试。这两种行事风格并没有特别的利弊之说,最好是在不同的情形下选择使用,比如对于简单的功能API,可以使用第一种,而对于复杂的功能API,选择第二种。不管行事风格如何,都会涉及都前面所述的两部分测试内容,即分与合的两部分测试。Django下,常见的有这么两种方法,或者说目前在我们项目组使用最广泛的两种方法:

1. 对于功能模块,使用python manage.py shell来进行调试与测试。该命令帮助我们启动一个python 的shell环境,并自动加载了Django的上下文信息,我们可以在里面import任何函数和类,构建参数,调试/测试其相关的功能。如果需要定位问题,一种方法是在源文件中加入更多的log,一种方法是将该功能模块逐行敲一遍,看看在哪边出现了问题。

2. 对于整个请求,使用工具curl(linux下)或者Advanced REST Client(Windows下,Chome的一个插件)来构建一个Request,发送给后台服务。笔者使用更多的是第二个,其可以构建各个Method的Request,并且可以保存相应的请求,还可以分享你保存的列表给别人使用。发送请求后,通过后台log来查看请求是否正常及相关的异常错误问题,你可以使用tail -f /var/log/test.log来查看动态log信息。

在合适的位置增加规范化的log对于后台服务来说,是一件非常重要的事情,能帮助我们在线上环境及时发现问题,log的书写应该包括相应的Tag,ErrorCode等等信息,方便查找。

在Django下测试与调试REST API的方法详解

上述两个方法,可以很好的对Django下的Web服务进行调试/测试,但是却有着一些缺憾。对应第一种方法,通过python manage.py shell进行功能测试,意味着每次都需要启动一个shell,每次都需要将相应的代码敲一遍,存在着重复性的工作,其实这种方法更适用于应对特殊的CASE,而不是针对Common的功能进行调试/测试。对于第二种方法,首先,当你想增加一些log时,你需要重启一下服务才能生效,而这种情况在生产环境就比较棘手;其次,通过这种方法进行测试,你没有办法在程序中处理一些Response的结果。

Python和Django自带了一些测试框架和工具,而充分利用这些,正好可以解决上述两种方法的缺憾。常用的一些测试框架和工具有:

1)单元测试框架TestCase;

2)Django的Test Client,类似curl的工具,是一个类似内置浏览器的工具,可以发起一个请求,并拿到对应的response内容;

3)RequestFactory,允许构建一个request参数,实现象调用函数一样调用一个view的请求函数,可以绕过middleware。

结合前面两种方法的缺憾和上述的测试框架/工具,下面介绍两种笔者常用的两种调试/测试方法:

1. 对于功能模块,采用单元测试的方法。单元测试常见于TDD的研发流程中,一方面可以验证所写的程序模块运行后的行为是否符合设计的测试用例,另一方面可以在修改代码后,快速验证是否改出了问题,是否与原有行为保持一致。采用单元测试的方法,既可以实现功能模块的调试/测试功能,也能有效的避免重复性工作,有效的弥补了前面所述的第一种方法的缺点。

python本身自带了一套unittest框架,而diango对于它又做了一层封装,可以根据喜好选择使用,笔者通常还是习惯使用python自带的单元测试框架。一个简单的单元测试代码如下:

import unittest
 
class TestStringMethods(unittest.TestCase):
 
 def test_upper(self):
  self.assertEqual('foo'.upper(), 'FOO')
 
 def test_isupper(self):
  self.assertTrue('FOO'.isupper())
  self.assertFalse('Foo'.isupper())
 
 def test_split(self):
  s = 'hello world'
  self.assertEqual(s.split(), ['hello', 'world'])
  # check that s.split fails when the separator is not a string
  with self.assertRaises(TypeError):
   s.split(2)
 
if __name__ == '__main__':
 unittest.main()

2. 对于整个请求,采用RequestFactory来实现类函数式调用的方法,从而可以实现调用view的请求函数。采用这个方法,可以有效避免前面第二种方法中需要重启服务的缺陷,当你修改了代码后,只要重新reload一下模块,即可实现新的调用;同时,你也可以在代码中拿到对应的response内容,做必要的校验处理。

然而,对于我们使用rest framework 框架来做REST API开发来说,Django原生的RequestFactory还是有一点缺陷,就是不能做authenticate。因为rest framework中的authenticate是做在request前面的,而不是在middleware中,所以采用原生的RequestFactory无法绕过authenticate。不过,rest framework又重新封装了一个APIRequestFactory类,提供了相关的模拟鉴权功能,常见的用法如下:

from rest_framework.test import APIRequestFactory
from rest_framework.test import force_authenticate
 
# Using the standard RequestFactory API to create a form POST request
factory = APIRequestFactory()
request = factory.post('/notes/', {'title': 'new idea'}, format='json')
 
# force authenticate
user = User.objects.get(username='olivia')
force_authenticate(request, user=user)
 
# call the view request
view = AccountDetail.as_view()
response = view(request)

上面所述的方法,只是笔者目前常用的四种方法,在实际工作中根据需要进行选择使用。当然,我相信还有更多更好的其他方法。

这篇在Django下测试与调试REST API的方法详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
在Python中用get()方法获取字典键值的教程
May 21 Python
Python模块搜索概念介绍及模块安装方法介绍
Jun 03 Python
python 添加用户设置密码并发邮件给root用户
Jul 25 Python
Python下的Softmax回归函数的实现方法(推荐)
Jan 26 Python
如何利用python查找电脑文件
Apr 27 Python
Django rest framework实现分页的示例
May 24 Python
Python Socket编程之多线程聊天室
Jul 28 Python
Python进阶之@property动态属性的实现
Apr 01 Python
Python如何实现邮件功能
May 27 Python
pycharm 实现本地写代码,服务器运行的操作
Jun 08 Python
Python使用for生成列表实现过程解析
Sep 22 Python
Python+Opencv实现把图片、视频互转的示例
Dec 17 Python
阿里云ECS服务器部署django的方法
Aug 29 #Python
树莓派3 搭建 django 服务器的实例
Aug 29 #Python
使用Django搭建web服务器的例子(最最正确的方式)
Aug 29 #Python
Python处理session的方法整理
Aug 29 #Python
django自带调试服务器的使用详解
Aug 29 #Python
Python中的相关分析correlation analysis的实现
Aug 29 #Python
python中单下划线(_)和双下划线(__)的特殊用法
Aug 29 #Python
You might like
session 的生命周期是多长
2006/10/09 PHP
深入php list()函数的详解
2013/06/05 PHP
Yii视图CGridView列表用法实例分析
2016/07/12 PHP
图文详解PHP环境搭建教程
2016/07/16 PHP
php 数据结构之链表队列
2017/10/17 PHP
经常用的图片在容器中的水平垂直居中实例
2007/06/10 Javascript
js 方法实现返回多个数据的代码
2009/04/30 Javascript
JS删除数组元素的函数介绍
2013/03/27 Javascript
jQuery中$(function() {});问题详解
2015/08/10 Javascript
原生js实现下拉框功能(支持键盘事件)
2017/01/13 Javascript
angularJS利用ng-repeat遍历二维数组的实例代码
2017/06/03 Javascript
Vue.js+Layer表格数据绑定与实现更新的实例
2018/03/07 Javascript
element-ui 设置菜单栏展开的方法
2018/08/22 Javascript
Node.js中Koa2在控制台输出请求日志的方法示例
2019/05/02 Javascript
JavaScript修改注册表实例代码
2020/01/05 Javascript
js实现随机圆与矩形功能
2020/10/29 Javascript
如何管理Vue中的缓存页面
2021/02/06 Vue.js
[04:48]DOTA2上海特锦赛小组赛第三日 TOP10精彩集锦
2016/02/28 DOTA
[25:45]2018DOTA2亚洲邀请赛4.5SOLO赛 Sylar vs Paparazi
2018/04/06 DOTA
python和C语言混合编程实例
2014/06/04 Python
python列表操作之extend和append的区别实例分析
2015/07/28 Python
Python 装饰器使用详解
2017/07/29 Python
基于Python函数的作用域规则和闭包(详解)
2017/11/29 Python
python读取图片的方式,以及将图片以三维数组的形式输出方法
2019/07/03 Python
python实现倒计时小工具
2019/07/29 Python
Python3.7黑帽编程之病毒篇(基础篇)
2020/02/04 Python
python zip,lambda,map函数代码实例
2020/04/04 Python
Python PyQt5运行程序把输出信息展示到GUI图形界面上
2020/04/27 Python
卫校毕业生自我鉴定
2013/10/31 职场文书
营销总经理的岗位职责
2013/12/15 职场文书
会计电算化专业毕业生自荐信
2013/12/20 职场文书
料理师求职信
2014/01/30 职场文书
成语的广告词
2014/03/19 职场文书
大学生活动总结怎么写
2014/04/29 职场文书
党组织领导班子整改方案
2014/10/25 职场文书
2014年销售助理工作总结
2014/12/01 职场文书