您的位置:首页 > 编程语言 > Python开发

《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

RPC

Remote 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在实际使用中还是比较多的,针对于这块的测试方法,除了工具,就是代码测试的办法了,建议大家还是多对此部分内容进行深入学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: