api token 什么意思_Python构建RESTful网络服务[Django篇:编写API测试]

v2-37921ea75c0963589780cce91f1cc4f3_1440w.jpg?source=172ae18b

系列文章介绍

本系列文章将详细介绍将Django官方引导教程中的投票项目改写为RESTful网络服务。Django官方教程地址https://docs.djangoproject.com/zh-hans/2.1/intro/tutorial01/

前置步骤

  • Step-1:RESTful与Django
  • Step-2:创建项目和应用
  • Step-3:使用原生Django编写API
  • Step-4:序列化与反序列化
  • Step-5:基于DRF的视图类的视图
  • Step-6:基于DRF的视图集的视图
  • Step-7:用户接入控制
  • Step-8:生成API文档

前置步骤文章见文末列表。

Step-9 测试

测试的重要性是不言而喻的。关于测试驱动开发,推荐一本不错的书:《Python测试驱动开发:使用Django、Selenium和JavaScript进行Web编程》,也是就传说中的山羊书。以前这本书还不叫这个名字,很多人都不知道这是一本Django主题的书。这本书详细讲解了如何用Django开发一个项目,如何从头开始就测试介入、测试驱动开发,如何使用Selenium,如何进行前端测试,甚至包含如何进行Git代码管理。现在Django主题的书已经汗牛充栋了,国人主写的Django书籍质量也非常高,而且更适合国内读者阅读。在这种大环境下这本书的阅读必要性也就没那么强了。

v2-c948bd2da4ea8edfc4777641add081a3_b.jpg

有人问我关于Django有什么书籍推荐,我认为,现在国人主笔的Django书籍都是经得起检验的,在书市上好好挑一本阅读,必有收获。如果要推荐一本外国人写的Django书,也许《轻量级Django》值得一读。这也是经起检验的一本不错的书,其中也介绍了Django REST开发相关内容。想了解这本书的同学可以私信向我借阅电子版(本来想用公众号自动发的,怕有引流嫌疑(⊙﹏⊙)b)。

v2-efb9f2f84a732a00c61478efaf04cc81_b.jpg

DRF中的测试工具

本节中我们为API添加测试。 DRF为API测试提供了一些有用的类,使得测试更简单。本节使用如下几个测试相关的类:

  • APIRequestFactory:类似于Django的RequestFactory。支持对视图发送各种请求,对比各种响应。
  • APIClient:类似于Django的Client。支持对url发起GET和POST、测试响应。
  • APITestCase:类似于Django的TestCase。各种测试类的父类。

创建测试请求

Django的RequestFactory可以创建一个请求实例,对视图函数发起请求。DjangoRESTFramework的APIRequestFactory类拓展了Django的RequestFactory;它支持几乎所有的HTTP动词。语法如下:

factory = APIRequestFactory()
request = factory.post(uri, post_data)

我们在polls/tests.py中创建第一个测试:

# in polls/tests.py

# from django.test import TestCase

# Create your tests here.
from rest_framework.test import APITestCase
from rest_framework.test import APIRequestFactory

from polls import apiview, apiviewsets


class TestPoll(APITestCase):
    def setUp(self):
        # 设置初始化的值
        self.factory = APIRequestFactory()
        self.view = apiviewsets.PollViewSet.as_view({'get': 'list'})
        self.uri = '/polls/'

    def test_list(self):
        request = self.factory.get(self.uri)
        response = self.view(request)
        self.assertEqual(
            response.status_code, 
            200,
            '预期200状态的响应,但响应码为{}.'.format(response.status_code)
        )

运行测试:

$ python manage.py test

v2-3113333dee55248c72cceb9205de9392_b.jpg

可以看到我们的测试失败了。这是因我访问这个视图需要认证,而测试没有认证。 我们来编写一个带认证的测试:

# in polls/tests.py

# from django.test import TestCase

# Create your tests here.
from django.contrib.auth import get_user_model

from rest_framework.authtoken.models import Token
from rest_framework.test import APITestCase
from rest_framework.test import APIRequestFactory

from polls import apiview, apiviewsets


class TestPoll(APITestCase):
    def setUp(self):
        self.factory = APIRequestFactory()
        self.view = apiviewsets.PollViewSet.as_view({'get': 'list'})
        self.uri = '/polls/'
        self.user = self.setup_user()
        self.token = Token.objects.create(user=self.user)
        self.token.save()

    @staticmethod
    def setup_user():
        User = get_user_model()
        return User.objects.create_user(
            'test',
            email='testuser@test.com',
            password='823w74ytrh3948gh!'
        )

    def test_list(self):
        request = self.factory.get(
            self.uri,
            HTTP_AUTHORIZATION='Token {}'.format(self.token.key)
        )
        request.user = self.user
        response = self.view(request)
        self.assertEqual(
            response.status_code, 
            200,
            '预期200状态的响应,但响应码为{}.'.format(response.status_code)
        )

再来运行测试,结果如图:

v2-4941d886314f7cc1b805f3202507ce10_b.jpg

使用APIClient

使用APIClient不需要先创建request,可以直接使用GET和POST。

# in polls/test.py

# from django.test import TestCase

# Create your tests here.
from django.contrib.auth import get_user_model

from rest_framework.authtoken.models import Token
from rest_framework.test import APITestCase
from rest_framework.test import APIRequestFactory
from rest_framework.test import APIClient

from polls import apiview, apiviewsets


class TestPoll(APITestCase):
    def setUp(self):
        self.factory = APIRequestFactory()
        self.client = APIClient()
        self.view = apiviewsets.PollViewSet.as_view({'get': 'list'})
        self.uri = '/polls/'
        self.user = self.setup_user()
        self.token = Token.objects.create(user=self.user)
        self.token.save()

    @staticmethod
    def setup_user():
        User = get_user_model()
        return User.objects.create_user(
            'test',
            email='testuser@test.com',
            password='823w74ytrh3948gh!'
        )

    def test_list(self):
        request = self.factory.get(
            self.uri,
            HTTP_AUTHORIZATION='Token {}'.format(self.token.key)
        )
        request.user = self.user
        response = self.view(request)
        self.assertEqual(
            response.status_code, 
            200,
            '预期200状态的响应,但响应码为{}.'.format(response.status_code)
        )

    def test_list2(self):
        response = self.client.get('/api-polls/polls/')
        self.assertEqual(
            response.status_code,
            200,
            '预期200状态的响应,但响应码为{}.'.format(response.status_code)
        )

如果现在运行测试,会因为没有认证而不能通过。我们可以使用APIClient.login进行认证。

# in polls/tests.py

class TestPoll(APITestCase):
    ... ...

    def test_list2(self):
        # self.client.login(username='test', password='823w74ytrh3948gh!')
        self.client.force_authenticate(user=self.user, token=self.token)
        response = self.client.get('/api-polls/polls/')  # 注意这里使用的不是self.uri
        self.assertEqual(
            response.status_code,
            200,
            '预期200状态的响应,但响应码为{}.'.format(response.status_code)
        )

APIClient提供了

  • client.login(username='lauren', password='secret'),使用账号密码
  • client.credentials(HTTP_AUTHORIZATION='Token ' + token.key),使用token
  • client.force_authenticate(user=user),强制认证

三种方式进行认证,即使用账号密码,使用token,使用强制认证等。此处使用的是强制认证。 值得注意的是,APIClient模拟的是客户端,所有self.client.get(uri)中的uri,要使用不含主机名的完整链接。

官方文档见:Testing - Django REST framework 运行测试,查看结果:

v2-0d03cf404737568306e04a31e3cc2d8b_b.jpg

POST测试

之前都是进行GET测试,现在开始进行POST测试。

class TestPoll(APITestCase):
    ... ...

    def test_create(self):
        self.client.login(username='test', password='823w74ytrh3948gh!')  # 这一次,我们使用了用户名、密码验证
        params = {
            'question': '如何看待第一届杨超越杯编程大赛?',
            'created_by': self.user.id
        }
        response = self.client.post('/api-polls' + self.uri, params)
        self.assertEqual(
            response.status_code,
            201,
            '预期201状态的响应,但响应码为{}.'.format(response.status_code)
        )  # 注意post成功的状态码不是200,而是201

运行测试,不出意外的话测试成功。

v2-0d03cf404737568306e04a31e3cc2d8b_b.jpg

Step-last:后记

系列文章风格

系列文章会以低零基础、手把手、逐行解释、连续完整的风格进行写作。

  • 低零基础:降低文章阅读门槛,使接触Python Web开发时间较短的读者也能有所收获。本人本职是从事数据开发与数据挖掘,所以对低零基础深有体会。
  • 手把手:一些基础操作,也会说明。如本文中,包括安装库等操作也会进行说明。
  • 逐行解释:对代码进行解释,以白居易写诗风格为目标(传说白居易会把自己的诗解释给街头妇人,直到连不懂文化的妇人也能明白,完成创作)。
  • 连续完整:连续是指,文章是成系列的,上文下文之间是有着联系的,项目是连续的。代码托管也体现了这一点,不同的文章,对应不同的git标签或分支,也体现了不同的进度。完整是指,项目是完整的,文章也是完整的。文章可以当作博文来读,也可以当作教程来读。

文章列表

  • Vue+Django构建前后端分离项目(选读): https://zhuanlan.zhihu.com/p/54776124
  • Python构建RESTful网络服务[Django篇:基于函数视图的API]:https://zhuanlan.zhihu.com/p/55562891
  • Python构建RESTful网络服务[Django篇:使用PostgreSQL替代SQLite](选读):https://zhuanlan.zhihu.com/p/55903530
  • Python构建RESTful网络服务[Django篇:基于类视图的API]:https://zhuanlan.zhihu.com/p/57024322
  • Python构建RESTful网络服务[Django篇:基于视图集的API]:https://zhuanlan.zhihu.com/p/57791697
  • Python构建RESTful网络服务[Django篇:用户接入控制,认证与权限]:https://zhuanlan.zhihu.com/p/58426061

参考文献

Hillar G C. Building RESTful Python Web Services[J]. Birmingham, UK: Packt Publishing Ltd, 2016.

重构Djagno官方教程为RESTful网络应用.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值