在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正规则表达式学习指南
Aug 02 Python
python如何查看系统网络流量的信息
Sep 12 Python
python使用sqlite3时游标使用方法
Mar 13 Python
Python实现App自动签到领取积分功能
Sep 29 Python
基于pycharm导入模块显示不存在的解决方法
Oct 13 Python
对python生成业务报表的实例详解
Feb 03 Python
通过PYTHON来实现图像分割详解
Jun 26 Python
Python解压 rar、zip、tar文件的方法
Nov 19 Python
Tensorflow训练模型越来越慢的2种解决方案
Feb 07 Python
在python3中实现查找数组中最接近与某值的元素操作
Feb 29 Python
Python递归函数特点及原理解析
Mar 04 Python
离线状态下在jupyter notebook中使用plotly实例
Apr 24 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
PHP json_encode中文乱码问题的解决办法
2013/09/09 PHP
新浪SAE云平台下使用codeigniter的数据库配置
2014/06/12 PHP
PHP中函数gzuncompress无法使用的解决方法
2017/03/02 PHP
jQuery ajax+PHP实现的级联下拉列表框功能示例
2019/02/12 PHP
使用tp框架和SQL语句查询数据表中的某字段包含某值
2019/10/18 PHP
input+select(multiple) 实现下拉框输入值
2009/05/21 Javascript
JS实现两个大数(整数)相乘
2014/04/28 Javascript
微信小程序 获取设备信息 API实例详解
2016/10/02 Javascript
JS弹出窗口的运用与技巧大全
2016/11/01 Javascript
关于js函数解释(包括内嵌,对象等)
2016/11/20 Javascript
jquery uploadify隐藏上传进度的实现方法
2017/02/06 Javascript
JS匿名函数和匿名自执行函数概念与用法分析
2018/03/16 Javascript
vue 2.1.3 实时显示当前时间,每秒更新的方法
2018/09/16 Javascript
vue移动端项目缓存问题实践记录
2018/10/29 Javascript
原生js实现五子棋游戏
2020/05/28 Javascript
[01:35:53]完美世界DOTA2联赛PWL S3 Magma vs GXR 第二场 12.13
2020/12/17 DOTA
Python 中 Meta Classes详解
2016/02/13 Python
Django与JS交互的示例代码
2017/08/23 Python
Python使用pyh生成HTML文档的方法示例
2018/03/10 Python
Django实战之用户认证(初始配置)
2018/07/16 Python
pytorch 数据集图片显示方法
2018/07/26 Python
Django实现一对多表模型的跨表查询方法
2018/12/18 Python
Python装饰器用法实例分析
2019/01/14 Python
Python eval的常见错误封装及利用原理详解
2019/03/26 Python
python中对数据进行各种排序的方法
2019/07/02 Python
Python Selenium 设置元素等待的三种方式
2020/03/18 Python
python库skimage给灰度图像染色的方法示例
2020/04/27 Python
Python在centos7.6上安装python3.9的详细教程(默认python版本为2.7.5)
2020/10/15 Python
python自动生成证件号的方法示例
2021/01/14 Python
求职信模版
2013/11/30 职场文书
中学门卫岗位职责
2013/12/26 职场文书
机关党建工作汇报材料
2014/08/20 职场文书
2014年政工师工作总结
2014/12/18 职场文书
大学生如何逃脱“毕业季创业队即散伙”魔咒?
2019/08/19 职场文书
一些让Python代码简洁的实用技巧总结
2021/08/23 Python
MySQL图形化管理工具Navicat安装步骤
2021/12/04 MySQL