《Web接口开发与自动化测试基于Python语言》--第13章
2017-10-22 23:01
736 查看
第13章 REST
REST同样属于Web Services技术范围。REST定义了一组体系框架原则,根据这些原则设计以系统资源为中心的Web 服务,包括使用不同语言编写的客户端如果通过HTTP处理和传输资源状态。REST可以说是近年来最主要的Web服务设计模型,基本取代了SOAP和WSDL。13.1 RPD与REST
RPCRemote Procedure Call,远程过程调用。RPC风格的开发关注于服务器/客户端之间的方法调用,而并不关注基于哪个网络层的哪种协议。
RPC风格的代表是:XML-RPC、大Web 服务
XML-RPC
XML-RPC是一种使用XML格式封装的方法的调用,并使用HTTP协议作为传送机制的RPC风格的实现。
XML-RPC的请求方法是:HTTP协议的POST方法,请求和响应的数据格式均为XML。
测试用例管理系统TestLink的对外接口就是PHP开发的XML-RPC。
大Web服务
即Big Web Services,是基于SOAP+WSDL+UDDI等技术实现RPC风格的大型Web服务的统称。
REST
Representational State Transfer,即表现层状态转化。
REST具有跨语言、跨平台的特点,它是一种遵循REST风格的Web Services。
如果一个架构符合REST原则,就称它为RESTful架构。
资源(Resources)
表现层其实指的是“资源”,所谓“资源”就是网络的一个实体,或者说是一个具体信息。可以用一个URI(统一资源定位符)来指向它,要想获取这个资源,只需要访问它的URI即可。
表现层(Representation)
“资源”是一种信息实体,它可以有多种外在表现形式,这种把“资源”具体呈现出来的形式,叫做它的“表现层”。
eg:文本既可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式、二进制格式来表现。
URI只代表资源的实体,不代表它的形式。
状态转化(State Transfer)
HTTP协议是一个无状态协议,所有的状态都保存在服务器端,客户端如果想要操作服务器,就必须通过某种手段,让服务器端发生“状态转化”,而这种转化是建立在表现层之上的,所以是“表现层状态转化”。
客户端用到的手段,只能是HTTP协议,具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT和DELETE。
GET:用来获取资源;
POST:用来新建资源(也可用于更新资源)
PUT:用来更新资源
DELETE:用来删除资源
综合以上内容,RESTful架构可以总结为:
每一个URI代表一种资源
客户端和服务器之间,传递这种资源的某种表现层
客户端通过四个HTTP动词,对服务器端资源进行操作,实现“表现层状态转化”
以上这些概念的一个包含关系:
SOA--Web Services--RPC--XML-RPC | |----Big Web Services--SOAP | |------WSDL | |------UDDI |-----------REST
13.2 Django REST Framework
顾名思义,是一套基于Django的REST风格的架构。它具有以下特点:
功能强大、灵活,可以帮助你快速开发Web API
支持认证策略,包括OAuth 1a和OAuth 2
支持ORM和非ORM数据源的序列化
丰富的文档以及良好的社区支持
官方网址:http://www.django-rest-framework.org/
13.2.1 创建简单的API
安装Django REST Framework:pip install djangorestframework pip install markdown pip install django-filter
安装好之后,创建一个新的项目django_rest,在项目下创建“api”应用:
django-admin startproject django_rest cd django_rest python manage.py startapp api
进入django_rest,修改settings.py文件:
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'api', ] # 在文件末尾添加如下信息 REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) }
rest_framework:为Django REST Framework应用;
api:为我们自己创建的应用;
DEFAULT_PERMISSION_CLASSES:默认的权限策略可以设置在全局范围内;
执行数据库迁移:
python manage.py migrate
创建超级管理员帐户:
python manage.py createsuperuser
Ps:这里我自己创建的是,admin、admin@mail.com、那啥+0
创建数据序列化,在api应用下创建serializers.py:
from django.contrib.auth.models import User, Group from rest_framework import serializers class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ('url', 'username', 'email', 'groups') class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ('url', 'name')
Serializers用于定义API的表现形式,如返回哪些字段、返回怎样的格式等,这里序列化Django自带的User和Group。
继续编辑视图文件:api/views.py
#! /usr/bin/python # -*- coding:utf-8 -*- from django.contrib.auth.models import User, Group from rest_framework import viewsets from api.serializers import UserSerializer, GroupSerializer from django.shortcuts import render # Create your views here. # ViewSets定义视图的展现形式 class UserViewSet(viewsets.ModelViewSet): """ API endpoint that allows users to be viewed or edited """ queryset = User.objects.all().order_by('-date_joined') serializer_class = UserSerializer class GroupViewSet(viewsets.ModelViewSet): """ API endpoint that allows groups to be viewed or edited """ queryset = Group.objects.all() serializer_class = GroupSerializer
其中,ViewSets用于定义视图的展现形式,eg:返回哪些内容、需要做哪些权限处理。
在URL中会定义相应的规则到ViewSet,ViewSets则通过serializer_class找到对应的Serializers。这里将User和Group的所有对象赋予queryset,并返回这些值。在UserSerializer和GroupSerializer中定义要返回的字段。
继续编辑url文件:django_rest/urls.py
from django.conf.urls import url, include from django.contrib import admin from rest_framework import routers from api import views # Routers provide an easy way of automatically determining the URL conf. router = routers.DefaultRouter() router.register(r'users', views.UserViewSet) router.register(r'groups', views.GroupViewSet) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]
因为使用的是ViewSets,所以可以使用routers类自动生成URL conf。
启动该服务:
python manage.py runserver,启动成功后,访问:http://127.0.0.1:8000的效果如下:
13.2.2 添加接口数据
通过前面创建的超级管理员账号登录,然后:http://127.0.0.1:8000/groups/,通过该链接,添加用户组:test组、developer组
http://127.0.0.1:8000/users/,通过该链接,添加用户:tom、jack
13.2.3 测试接口
针对我们上面添加的用户、用户组的接口查询,使用Requests库编写测试用例:#! /usr/bin/python # -*- coding:utf-8 -*- import unittest import requests class UserTest(unittest.TestCase): '''用户查询测试''' def setUp(self): self.base_url = 'http://127.0.0.1:8000/users' self.auth = ('admin', 'admin123456') def test_user1(self): '''test user admin''' r = requests.get(self.base_url+'/1/', auth=self.auth) result = r.json() self.assertEqual(result['username'], 'admin') self.assertEqual(result['email'], 'admin@mail.com') def test_user2(self): '''test user tom''' r = request e95f s.get(self.base_url+'/2/', auth=self.auth) result = r.json() self.assertEqual(result['username'], 'tom') self.assertEqual(result['email'], 'tom@mail.com') def test_user3(self): '''test user jack''' r = requests.get(self.base_url+'/3/', auth=self.auth) result = r.json() self.assertEqual(result['username'], 'jack') self.assertEqual(result['email'], 'jack@mail.com') class GroupsTest(unittest.TestCase): '''用户组查询测试''' def setUp(self): self.base_url = 'http://127.0.0.1:8000/groups' self.auth = ('admin', 'admin123456') def test_groups1(self): '''test groups test''' r = requests.get(self.base_url+'/1/', auth=self.auth) result = r.json() self.assertEqual(result['name'], 'test') def test_groups2(self): '''test groups developer''' r = requests.get(self.base_url+'/2/', auth=self.auth) result = r.json() self.assertEqual(result['username'], 'developer') if __name__ == '__main__': unittest.main()
注意:
请求接口的资源不是通过接口参数(?user=1)访问,而是通过URI路径(/1/)访问;
接口的访问需要用户签名,在发送get()请求时需要指定auth参数。
13.3 集成发布会系统API
通过Django REST Framework来实现发布会签到系统接口更简单。13.3.1 添加发布会API
在我们创建的django_rest项目的基础上增加发布会和嘉宾的相关接口。首先,创建模型,修改文件:api/models.py
#! /usr/bin/python # -*- coding:utf-8 -*- from __future__ import unicode_literals from django.db import models # Create your models here. # 发布会 class Event(models.Model): name = models.CharField(max_length=100) limit = models.IntegerField() status = models.BooleanField() address = models.CharField(max_length=200) start_time = models.DateTimeField('events time') create_time = models.DateTimeField(auto_now=True) def __str__(self): return self.name # 嘉宾 class Guest(models.Model): event = models.ForeignKey(Event) realname = models.CharField(max_length=64) email = models.EmailField() sign = models.BooleanField() create_time = models.DateTimeField(auto_now=True) class Meta: unique_together = ('phone', 'event') def __str__(self): return self.realname
执行数据库迁移:
python manage.py makemigrations api
python manage.py migrate
继续添加发布会数据序列化,编辑文件:api/serializers.py
#!/usr/bin/python # -*- coding:utf-8 -*- from django.contrib.auth.models import User, Group from rest_framework import serializers from api.models import Event, Guest class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ('url', 'username', 'email', 'groups') class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ('url', 'name') class EventSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Event fields = ('url', 'name', 'address', 'start_time', 'limit', 'status') class GuestSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Guest fields = ('url', 'realname', 'phone', 'email', 'sign', 'event')
继续定义发布会和嘉宾视图,编辑文件:api/views.py
#! /usr/bin/python # -*- coding:utf-8 -*- from django.contrib.auth.models import User, Group from rest_framework import viewsets from api.serializers import UserSerializer, GroupSerializer,EventSerializer, GuestSerializer from django.shortcuts import render from api.models import Event, Guest # Create your views here. # ViewSets定义视图的展现形式 class UserViewSet(viewsets.ModelViewSet): """ API endpoint that allows users to be viewed or edited """ queryset = User.objects.all().order_by('-date_joined') serializer_class = UserSerializer class GroupViewSet(viewsets.ModelViewSet): """ API endpoint that allows groups to be viewed or edited """ queryset = Group.objects.all() serializer_class = GroupSerializer class EventViewSet(viewsets.ModelViewSet): """ API endpoint that allows events to be viewed or edited """ queryset = Event.objects.all() serializer_class = EventSerializer class GuestViewSet(viewsets.ModelViewSet): """ API endpoint that allows guests to be viewed or edited """ queryset = Guest.objects.all() serializer_class = GuestSerializer
继续添加URL配置,编辑文件:django_rest/urls.py
"""django_rest URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.10/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url, include from django.contrib import admin from rest_framework import routers from api import views # Routers provide an easy way of automatically determining the URL conf. router = routers.DefaultRouter() router.register(r'users', views.UserViewSet) router.register(r'groups', views.GroupViewSet) router.register(r'events', views.EventViewSet) router.register(r'guests', views.GuestViewSet) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]
重启项目,访问后的效果如下:
13.3.2 测试接口
使用Django REST Framework开发的接口,除了可以使用GET方法查询接口数据外,还可以调用接口添加数据,并不需要关心接口插入数据的细节,只需要将接口请求改为POST即可。使用Postman添加一条发布会接口,额,最近无法访问谷歌,这个工具也用不了,就使用了另外一个类似工具做测试:
13.4 soapUI测试工具
soapUI是一款针对REST和SOAP的功能和性能测试工具:https://www.soapui.org/13.4.1 创建SOAP测试项目
Projects-New SOAP Project:Project Name:MobileCodeWS
Initial WSDL:http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl
依次展开:MobileCodeWS–MobileCodeWSSoap–getMobileCodeInfo
双击:Request1,填写接口查询的手机号,点击运行按钮,即可得到结果。
注意: 新版本中要求填写userID,免费用户的userID是空白,默认为“?”问号,不修改的话会报错。
Ps:我已经把手机号去掉显示了。
13.4.2 创建REST测试项目
继续创建一个REST项目。认证选项里各选项的含义:
Username:用于填写基本认证的用户名;
Password:用于填写基本认证的密码;
Domain:域名是基本认证的可选项,设置为空;
Pre-emptive auth:设置定义认证的行为;
Use global preference:用于定义HTTP设置为全局首选项;
Authenticate pre-emptively:仅适用于此请求,不需要等待身份验证时才发送凭据。
soapUI的官方文档:https://www.soapui.org/siapui-projects/soapui-projects.html
总结
Djanog REST Framework在实际使用中还是比较多的,针对于这块的测试方法,除了工具,就是代码测试的办法了,建议大家还是多对此部分内容进行深入学习。相关文章推荐
- 关于《Web接口开发与自动化测试--基于Python语言》
- 《Web接口开发与自动化测试基于Python语言》--第15章
- 《Web接口开发与自动化测试基于Python语言》--第1章
- 《Web接口开发与自动化测试基于Python语言》--第9章
- 《Web接口开发与自动化测试基于Python语言》--第7章
- 《Web接口开发与自动化测试 -- 基于Python语言》---现已出版。
- 《Web接口开发与自动化测试基于Python语言》--第14章
- 《Web接口开发与自动化测试基于Python语言》--第10章
- 《Web接口开发与自动化测试基于Python语言》--第12章
- 《Web接口开发与自动化测试基于Python语言》--第11章
- 《Web接口开发与自动化测试基于Python语言》–第3章
- 《Web接口开发与自动化测试 -- 基于Python语言》 ---前言
- 7.Selenium2 自动化测试实战-基于Python语言-WebElement接口1
- 8.Selenium2 自动化测试实战-基于Python语言-WebElement接口2
- Python Web接口开发学习笔记(基于Django框架)(一)
- 5.Selenium2 自动化测试实战-基于Python语言-控制浏览器后退、前进
- 我的第一个python web开发框架(18)——前台页面与接口整合
- 开发基于接口的 .net web 服务
- web接口开发-之python必知
- 简单实现接口自动化测试(基于python+unittest)