在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 08 Python
Python列表和元组的定义与使用操作示例
Jul 26 Python
python使用opencv按一定间隔截取视频帧
Mar 06 Python
解决python3.5 正常安装 却不能直接使用Tkinter包的问题
Feb 22 Python
python中pip的使用和修改下载源的方法
Jul 08 Python
用Python实现BP神经网络(附代码)
Jul 10 Python
Python网络编程之使用TCP方式传输文件操作示例
Nov 01 Python
python将四元数变换为旋转矩阵的实例
Dec 04 Python
python计算波峰波谷值的方法(极值点)
Feb 18 Python
python exit出错原因整理
Aug 31 Python
利用Python将图片中扭曲矩形的复原
Sep 07 Python
python面向对象版学生信息管理系统
Jun 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 获取完整url地址
2008/12/20 PHP
一个php导出oracle库的php代码
2009/04/20 PHP
PHP日期时间函数的高级应用技巧
2009/05/16 PHP
学习使用curl采集curl使用方法
2012/01/11 PHP
PHP面向对象的进阶学习(抽像类、接口、final、类常量)
2012/05/07 PHP
php求正负数数组中连续元素最大值示例
2014/04/11 PHP
php中的动态调用实例分析
2015/01/07 PHP
PHP与Perl之间知识点区别整理
2019/03/19 PHP
Laravel框架源码解析之模型Model原理与用法解析
2020/05/14 PHP
javascript Excel操作知识点
2009/04/24 Javascript
jquery 的 $("#id").html() 无内容的解决方法
2010/06/07 Javascript
Extjs gridpanel 出现横向滚动条问题的解决方法
2011/07/04 Javascript
深入理解JQuery keyUp和keyDown的区别
2013/12/12 Javascript
倒记时60刷新网页的js代码
2014/02/18 Javascript
JS实现div居中示例
2014/04/17 Javascript
vue+canvas实现炫酷时钟效果的倒计时插件(已发布到npm的vue2插件,开箱即用)
2018/11/05 Javascript
jquery.tagsinput.js实现记录checkbox勾选的顺序
2019/09/21 jQuery
Vue 3.0双向绑定原理的实现方法
2019/10/23 Javascript
javascript实现放大镜功能
2020/12/09 Javascript
[01:21:58]守擂赛DOTA2第一周决赛
2020/04/22 DOTA
分数霸榜! python助你微信跳一跳拿高分
2018/01/08 Python
TensorFlow入门使用 tf.train.Saver()保存模型
2018/04/24 Python
启动Atom并运行python文件的步骤
2018/11/09 Python
使用Python的toolz库开始函数式编程的方法
2018/11/15 Python
Python列表对象实现原理详解
2019/07/01 Python
python内置模块collections知识点总结
2019/12/19 Python
Python如何急速下载第三方库详解
2020/11/02 Python
Python request post上传文件常见要点
2020/11/20 Python
matplotlib bar()实现多组数据并列柱状图通用简便创建方法
2021/02/24 Python
python实现简单文件读写函数
2021/02/25 Python
Rakuten Kobo台湾:电子书、eReaders和Reading应用程式
2017/11/24 全球购物
暑期社会实践感言
2014/02/25 职场文书
《回乡偶书》教学反思
2014/04/12 职场文书
2014年村支部书记四风对照检查材料思想汇报
2014/10/02 职场文书
企业年检委托书范本
2014/10/14 职场文书
常用的文件对应的MIME类型汇总
2022/04/26 HTML / CSS