在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中统计函数运行耗时的方法
May 05 Python
深入解答关于Python的11道基本面试题
Apr 01 Python
Python递归函数定义与用法示例
Jun 02 Python
Python基础教程之浅拷贝和深拷贝实例详解
Jul 15 Python
python实现全盘扫描搜索功能的方法
Feb 14 Python
Django中使用CORS实现跨域请求过程解析
Aug 05 Python
python selenium登录豆瓣网过程解析
Aug 10 Python
动态设置django的model field的默认值操作步骤
Mar 30 Python
学习Python爬虫的几点建议
Aug 05 Python
python开发入门——set的使用
Sep 03 Python
用Python自动清理系统垃圾的实现
Jan 18 Python
Python基于opencv的简单图像轮廓形状识别(全网最简单最少代码)
Jan 28 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
libmysql.dll与php.ini是否真的要拷贝到c:\windows目录下呢
2010/03/15 PHP
php并发加锁示例
2016/10/17 PHP
PHP获取访问设备信息的方法示例
2019/02/20 PHP
javascript操作JSON的要领总结
2012/12/09 Javascript
Jquery 改变radio/checkbox选中状态,获取选中的值(示例代码)
2013/12/12 Javascript
qq悬浮代码(兼容各个浏览器)
2014/01/29 Javascript
jQuery实现简单网页遮罩层/弹出层效果兼容IE6、IE7
2014/06/16 Javascript
JavaScript自学笔记(必看篇)
2016/06/23 Javascript
微信小程序 input输入框详解及简单实例
2017/01/10 Javascript
Vue组件开发初探
2017/02/14 Javascript
javascript简写常用的12个技巧(可以大大减少你的js代码量)
2020/03/28 Javascript
js链表操作(实例讲解)
2017/08/29 Javascript
jquery实现图片跟随鼠标的实例
2017/10/17 jQuery
vue组件中使用iframe元素的示例代码
2017/12/13 Javascript
JS中获取 DOM 元素的绝对位置实例详解
2018/04/23 Javascript
JavaScript中的"=、==、==="区别讲解
2019/01/22 Javascript
Vue使用Canvas绘制图片、矩形、线条、文字,下载图片
2019/04/26 Javascript
jQuery事件委托代码实践详解
2019/06/21 jQuery
如何给element添加一个抽屉组件的方法步骤
2019/07/14 Javascript
D3.js 实现带伸缩时间轴拓扑图的示例代码
2020/01/20 Javascript
JS sort排序详细使用方法示例解析
2020/09/27 Javascript
[01:01]青春无憾,一战成名——DOTA2全国高校联赛开启
2018/02/25 DOTA
python实现图片处理和特征提取详解
2017/11/13 Python
python与C、C++混编的四种方式(小结)
2019/07/15 Python
Python Django Vue 项目创建过程详解
2019/07/29 Python
python爬取百度贴吧前1000页内容(requests库面向对象思想实现)
2019/08/10 Python
python如何保存文本文件
2020/06/07 Python
CSS3的calc()做响应模式布局的实现方法
2017/09/06 HTML / CSS
html5 更新图片颜色示例代码
2014/07/29 HTML / CSS
zooplus波兰:在线宠物店
2019/07/21 全球购物
日本动漫周边服饰销售网站:Atsuko
2019/12/16 全球购物
String s = new String(“xyz”);创建了几个String Object?
2015/08/05 面试题
市场拓展计划书
2014/05/03 职场文书
冬季安全检查方案
2014/05/23 职场文书
2016年质量月活动总结报告
2016/04/05 职场文书
用golang如何替换某个文件中的字符串
2021/04/25 Golang