《Web接口开发与自动化测试基于Python语言》--第13章

来源:互联网 发布:鉴知往来的意思 编辑:程序博客网 时间:2024/06/04 18:24

第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 djangorestframeworkpip install markdownpip install django-filter

安装好之后,创建一个新的项目django_rest,在项目下创建“api”应用:

django-admin startproject django_restcd django_restpython manage.py startapp api

进入django_rest,修改settings.py文件:

# Application definitionINSTALLED_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, Groupfrom rest_framework import serializersclass 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, Groupfrom rest_framework import viewsetsfrom api.serializers import UserSerializer, GroupSerializerfrom 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 = UserSerializerclass 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, includefrom django.contrib import adminfrom rest_framework import routersfrom 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 unittestimport requestsclass 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 = requests.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()

注意:

  1. 请求接口的资源不是通过接口参数(?user=1)访问,而是通过URI路径(/1/)访问;
  2. 接口的访问需要用户签名,在发送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_literalsfrom 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, Groupfrom rest_framework import serializersfrom api.models import Event, Guestclass 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, Groupfrom rest_framework import viewsetsfrom api.serializers import UserSerializer, GroupSerializer,EventSerializer, GuestSerializerfrom django.shortcuts import renderfrom 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 = UserSerializerclass GroupViewSet(viewsets.ModelViewSet):    """    API endpoint that allows groups to be viewed or edited    """    queryset = Group.objects.all()    serializer_class = GroupSerializerclass EventViewSet(viewsets.ModelViewSet):    """    API endpoint that allows events to be viewed or edited    """    queryset = Event.objects.all()    serializer_class = EventSerializerclass 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 ConfigurationThe `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, includefrom django.contrib import adminfrom rest_framework import routersfrom 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在实际使用中还是比较多的,针对于这块的测试方法,除了工具,就是代码测试的办法了,建议大家还是多对此部分内容进行深入学习。

阅读全文
0 0
原创粉丝点击