软件测试-1-软件测试知识架构和API接口常识

参考软件测试知识架构

1 软件测试架构

1 软件测试基础
Fiddler 、Linux

2 数据库
MySQL 、Redis

3 性能测试
性能测试、Android app性能 、Jmeter

4 自动化测试
测试框架Pytest 、 
测试报告Allure 、
Web自动化之Selenium(Python) 、
App 自动化之Appium(待整理):

5 编程基础
python 基础 、Flask基础 、 Django基础 、
HTML 基础 、 CSS基础 

Jenkins 、Git 、 Docker

6 中间件
Nginx 、Tomcat .....

2 API接口常识

程序员不得不知道的API接口常识
软件测试-1-软件测试知识架构和API接口常识_第1张图片

说实话,我非常希望两年前刚准备找实习的自己能看到本篇文章,那个时候懵懵懂懂,跟着网上的免费教程做了一个购物商城就屁颠屁颠往简历上写。
至今我仍清晰地记得,那个电商教程是怎么定义接口的:管它是增加、修改、删除、带参查询,全是 POST请求,比如下面这样:

修改用户的收货地址
POST /xxx-mall/cart/update_address

现在看来,全部用POST请求估计是为了传参方便吧。

那个时候自己也没有一个API接口需要设计的意识,跟学过类似教程的朋友应该懂的,老师敲一行代码学生跟着敲一行。如果没人提这个事情,正式工作进入团队后,是很容易出丑的(作者亲身经历,捂脸)。

文章会分为五小块:

初识API接口
关于API限流
关于API版本管理
关于API权限与安全
关于团队间的API互通

这是一篇会隐式罗列很多知识点的文章,您可以按需深度搜索进行更进一步的学习。当年渴望看到这样的文章的原因是:学习一个知识点其实只需要时间,对学生而言,时间不是问题,问题在于不知道该往哪些方向学。本文希望通过串讲,梳理一下个人当前了解到的API知识体系,整理的同时也希望能对大家有一点点帮助。

2.1 初识API接口

一、记得在我初学web开发的时候,后端框架相关的教程基本都会教学生写渲染模版(不分语言),也就是说后端返回的是整个网页的数据,浏览器只负责渲染。

一般这类模版在后端都会对应一个路由,比如前端想登入一个看用户信息的页面,在url中输入的访问地址大概长这样:

https://ajun24.com/user

那个时候,我以为这样的路由地址就是API概念的全部了。

二、值得一提的是:绝大部分后端教程都会简单教一下前端,在前端的补充教程中有一个必学的知识点,叫:AJAX。

老师大概率会演示一下AJAX这个技术怎么使用,写个小Demo,告诉大家可以这样在页面上发送异步请求。

这个技术请求的后端接口一般不会跳转或返回一个 html页面,大概率会返回一份json数据。我一直对这样的接口和返回页面数据的接口有着迷之困惑。直到我实习后明白了什么叫前后端分离开发

三、但是为了教学方便,完整项目大概率还是会用渲染模版的方式讲解,毕竟只在一套系统里写代码演示会方便很多。

当年就是这样学完了第一个项目,虽然对如何做一个软件系统有了整体的认识,但是对API设计的认识是非常弱的。

四、其实我在学AJAX这个知识点的时候就在想:有没有可能全部数据都通过类似AJAX这种方式获取?这样感觉会更方便一些。

后来实习的时候,前端同学告诉我:开发前需要先定义API哦。当然,他还告诉我:删除一个东西不能用POST请求哦。

五、后来导师提醒我:你需要去了解一下如何设计 REST风格的API。

自从那次出丑后,我明白了一个事情,一定要敢于把自己的不足"暴露"给愿意指点你的人看。就好比我们读大学的时候最好要努力去找一份实习,每一次被拒以及每一份offer都会告诉我们,这个社会需要什么样的人才,什么样的技能可以帮助我们谋得一份工作。

六、在正式的面试场合下,或许我们更应该条理清晰地和面试官介绍什么是表现层状态转换,但是在这篇文章中,我想把REST风格的API称为更容易让人看懂的API。

大家会发现符合REST风格的API能非常容易地让别人知道调用这个API能干什么,比如:

# 查询用户信息
GET     /users                
# 根据id更新某个用户的信息,只部分更新客户端提交的数据
PATCH   /users/{user_id}      

按约定写API就好比在IT领域说行话,大家只要看见你的API,就知道你能提供什么样的服务。

七、有同学可能会好奇为什么要遵守规范?
假如,我们负责的系统仅联系到我们身边同事的系统,那约定API的时候只需要打个招呼,或在聊天工具上简单说明一下就可以了,甚至可以没有文档。

但在很多情况下,我们的系统是要被很多其他系统调用的,大家想象一下我们去调用云厂商API的场景:别人的工程师大概率不是我们的微信好友,大多数时候是没有人站在我们身边手把手告诉我们API怎么调用的。这个时候想调用对方提供的API,就得看对方提供的API文档。如果对方的API不按照规范定义,那API文档绝对像天书一样难读。

看天书的痛苦,保证大家体会一次足以终生难忘。

良好的API文档一般会像工具手册,没有太多学习成本,否则别人下一次很有可能就不使用我们的服务了。

所以先系统地学习API定义约规,再编写API文档,然后根据设计进行开发是一个比较好的研发流程

八、接下来的问题是,在了解了API的规范后,如何写出良好的API文档呢?

众所周知,写文档对程序员来说是一件非常痛苦的事情,一想到学习写专业的API文档还需要学习成本,实在是劝退。这个时候我们可以通过一些自动化工具辅助我们完成一篇优秀的API文档,比如我们可以使用swagger,它可以通过我们的代码自动生成API文档。

最近还看到不少基于API的研发测试一体化产品和平台,感觉一站式的、流水线式的研发管理是未来的趋势呀!

2.2 关于API限流

API写出来后会被调用,但由于计算机&网络系统的局限性,我们的API接口是不可以被无限制调用的

大家可以随便到网上挑一个比较专业的API文档看,比如大家可以去看云厂商对外提供的API,基本都会看到一个接口频率调用限制,比如:单用户调用频率为30次/秒。

所以当我们在设计API的时候,限流是一个不得不考虑的事情(内部自己弄着玩的不算哈,泛指面向用户的系统)。

一、在设计限流之前,我们首先要知道自己系统的瓶颈。

假设我们的API纯粹调用自家的技术组件,比如数据库,消息队列等中间件,这个时候我们可以通过压测得知一个接口的最大承受能力。

假设我们的系统是一个中间系统,需要依赖其他系统的接口完成业务,那么这个时候基于木桶原理,我们接口的可访问频率就会受限于其他业务系统。

二、了解完自身项目的访问瓶颈后,需要考虑自身系统的架构,假设我们的系统是单体部署
软件测试-1-软件测试知识架构和API接口常识_第2张图片
那这个时候我们只需要简单的令牌桶算法即可以完成限流,下面是一个极简的令牌桶算法实现Demo:

实现一个固定容量的桶,按一定的频率往桶内放令牌直至桶满,每当执行一个限频操作需要从桶中获取一个令牌才能继续操作,若桶中没有令牌,则进行等待
往令牌桶中放令牌的操作不便按照原概念实现,所以放令牌这步放到取令牌的时候进行。我们根据当前取令牌的时间减去上一次取令牌的时间差,就能得知这段时间内增加了多少个令牌。

class TokenBucket(object):

    # rate 是令牌桶生产令牌的速率,
    # capacity 是令牌桶生产令牌的容量
    def __init__(self, rate, capacity):
        self._rate = rate
        self._capacity = capacity
        self._current_amount = 0
        self._last_consume_time = int(time.time())

    # token_amount 是执行一次操作需要的令牌数量
    def consume(self, token_amount):
        # 通过时间差乘速率,得到令牌的增量
        increment = (int(time.time()) - self._last_consume_time) * self._rate
        时间差乘速率,得到令牌的增量  
        self._current_amount = min(
            increment + self._current_amount, self._capacity)
        # 令牌数量不够则不允许操作
        if token_amount > self._current_amount:
            return False
        # 更新最后一次操作时间
        self._last_consume_time = int(time.time())
        # 结算当前的令牌数量
        self._current_amount -= token_amount
        return True

三、但实际工作中,我们部署单体架构的机会不多,现在的大公司都构建有自己的云生态,业务部门上云后可快速进行扩缩容,所以我们的系统很有可能会进行集群部署,用户的请求通过代理层负载均衡至各个后端节点:
软件测试-1-软件测试知识架构和API接口常识_第3张图片

这个时候上面的15行代码显然就不符合我们的分布式系统架构,我们得考虑更复杂的限流算法实现了(这里不是指令牌桶算法不合适,是指令牌桶算法的实现方式需要改进),当然这个实现大概率会放在代理层了,而不是实现在我们的业务层。

四、大家可以上网看一下主流云厂商提供的云服务,很多都会提供API网关,对应着我们上面提到的代理层。

假如一个公司有统一的API网关服务,或有类似的代理服务,业务部门是可以在API限流这件事情上省下很大功夫的。我有时候想,当越来越多的中小企业基于巨无霸云厂商搭建业务,大家要考虑的技术性问题就会越来越少,越来越专注于业务,这到底是一件好事还是坏事呢?

2.3 关于API版本管理

介绍完API及限流的基本知识后,谈一下和业务比较相关的API的版本管理

在没真正接触业务前,我以为只有软件需要做版本管理,为啥API也要做版本管理咧?其实原理是一样的,软件会根据需求不断迭代版本,API同样也会迭代版本,但秉承开闭原则,为了不影响之前的业务,我们最好不要改动原有的API。

因此,我们设计API的时候可以指定版本号,比如上述的例子:

GET     /users  # 查询用户信息
我们可以统一定义成:

GET     /api/v1/users  # 查询用户信息
假设这个接口有了第二个版本,
我们就可以通过版本号进行区分了:

GET     /api/v2/users  # 查询用户详细信息

换作两年前的我可能会对API版本管理无感,但大家尝试把自己代入以下场景就能明白了:

比如我们的产品让我们出一套新的查询用户API,假设我们没有定义版本号,由于/users这条路由已经在用了,逼不得已,我们就会定义一个新的:

GET   /get_user_info  # 查询用户信息

新接口和老接口的意思差不多,如果我们一直负责这个系统,那还好说(心里有不同版本的区分)。

但假如这个系统换了另一个接班人,当他面对大量意义接近的接口时,肯定会怀疑人生的(屎山就是这样来的)。

软件测试-1-软件测试知识架构和API接口常识_第4张图片

2.4 关于API权限与安全

接着我们思考一下API的权限与安全问题。

还是回到初学的时候,那个时候我对API接口权限完全没有任何概念。老师为了快速教会我们开发系统,很多接口的设计是完全裸奔的。如果不了解一点点相关的知识,工作中会容易给别人一种考虑事情不周到的感觉。

在实际生产中,接口是不可以不做权限校验的,如果我们的系统暴露在公网,还没有权限校验的话,系统估计很快就挂了;内部涉及机密的系统,权限校验则更为严格。

关于权限校验,个人暂时分为三个维度,三个维度或许可以对应三种业务类型:

第一种是直接针对IP设置白名单,这种方式比较适用于客户端有限且固定的内部系统;
第二种则是设置权限校验流程,比如采用Token鉴权,较多用于ToB业务。大家在云厂商注册账号后基本都会得到一对密钥,后续的API调用一般都需要先根据密钥进行权限认证;
第三种是通过用户登陆判断权限,较多用于ToC业务,比如我们登陆京东,登陆淘宝需要账号,没有登陆就访问不了购物车等页面。

值得一提的是,权限设计是另一个维度的知识,除了第一个维度,后两者其实都可以单独成立一个系统的。比如公司的用户管理系统,中心化权限认证系统等等。

权限校验关乎着公司财产安全,所以不可忽视,很多时候我们甚至需要在API设计层面考虑安全问题。再次引用商城的例子,比如登陆后获取用户购物车的订单,API大概率会设计成这样子:

GET /users/287435/orders

但直接暴露用户id或许不是一个明智的选择,有可能被不法分子利用,我们可以换种方式,比如用以下的方式替代:

GET /users/me/orders

总而言之,API的设计除了参考规范外,还需要根据自身业务情况进行更进一步的安全考虑。

2.5 关于团队间的API互通

最后是一个延展性话题,相信大家都感受到了我们正身处于一个数据时代,我们的个人信息,包括各类行为喜好,都存放在各家互联网公司的数据仓库里,企业们可能比我们更了解我们自身,网上也有很多与数据资产有关的话题。

既然已经把数据比作资产了,而资产流动性又是一个经久不衰的话题,所以各类数据的开放性问题也倍受关注。而数据对外开放,必然就会涉及到API接口。

当然作为一只小码农,我的视野极其有限,很难从一个较高的层次去谈论企业的数据问题。但在工作中,当其他业务团队提出要调用自己负责的项目的API接口时,也是需要进行多方位考虑的。

3 Apifox接口调试工具

先写 API 文档还是先写代码?你需要这款神器!
融资三千万,腾讯阿里都在用的国产软件!
Apifox官方网站
Apifox使用文档
作为软件开发从业者,API调试是必不可少的一项技能,在这方面 Postman做的非常出色。但是在整个软件开发过程中,API调试只是其中的一部分,还有很多事情Postman无法完成,或者无法高效完成,比如:API文档定义、API Mock、API自动化测试等等。Apifox就是为了解决这个问题而生的。
软件测试-1-软件测试知识架构和API接口常识_第5张图片

3.1 代码未动文档先行

其实大家都知道API文档先行的重要性,但是在实践过程中往往会遇到很多困难。

程序员最讨厌的两件事:(1)写文档,(2)别人不写文档

大多数开发人员不愿意写API文档的原因是:写文档短期收益远低于付出的成本,然而并不是所有人都能够坚持做有长期收益的事情的。

作为一个前后端分离模式开发的团队,我们经常会看到这样的场景:前端开发和后端开发在一起热烈的讨论“你这接口参数怎么又变了?”,“接口怎么又不通了?”,“稍等,我调试下”,“你再试试…"。

那能不能写好API文档,大家都按文档来开发?很难,因为写文档、维护文档比较麻烦,而且费时,还会经常出现API更新了,但文档还是旧的,各种同步不一致的情况,从而耽搁彼此的时间。

之前我们团队也遇到了同样的问题,那么作为研发团队的负责人,我是如何带领团队解决这个问题的呢?方法其实很简单,如果能做到让写文档/维护文档这件事情的短期收益就能远高于付出的成本,那么所有问题都能迎刃而解,开发人员就会非常乐意去写接口文档。
一、团队原工作模式

API设计人员使用Swagger写API文档。
前端开发使用mock.js模拟mock假的API数据。
后端开发使用Postman调试API。
测试人员使用JMeter测试API。

二、我们遇到的问题
(1)我们团队是前后端同步进入开发的,不能等后端开发完了才出接口文档,前端再进入开发,所以使用后端代码注释自动生成 Swagger不适合我们。
(2)写Swagger文档效率很低,并且有学习门槛,让团队所有人都熟练手写Swagger文档是不现实的,更何况团队不停有新人进来。
(3)开发人员在Swagger定义好文档后,接口调试的时候还需要去Postman再定义一遍。
(4)前端开发Mock数据的时候又要去mock工具里定义一遍,手动设置好Mock规则。
(5)测试人员需要去JMeter定义一遍。
(6)前端根据mock工具出来的数据开发完,后端根据Swagger定义的接口文档开发完,各自测试测试通过了,本以为可以马上上线,结果一对接发现各种问题:原来开发过程中接口变更,只修改了Swagger,但是没有及时同步修改mock。
(7)同样,测试在JMeter写好的测试用例,真正运行的时候也会发现各种不一致。
(8)开发过程,经常会有发现开始定义的接口文档有不合理的地方,需要临时调整,经常出现接口改了,但是文档没有更新。
(9)时间久了,各种不一致会越来越严重。

三、最佳实践解决方案
(1)前端(或后端)在Apifox上定好接口文档初稿。

(2)前后端一起评审、完善接口文档,定好接口用例

(3)前端使用Apifox自动生成的Mock数据进入开发,而无需手写mock规则,直接开工。

(4)后端使用接口用例调试开发中接口,系统根据接口文档的定义自动校验返回的数据是否正确,只要所有接口用例调试通过,接口就开发完成了。

(5)后端开发完成后,测试人员(也可以是后端)使用集合测试功能进行多接口集成测试,完整测试整个接口调用流程。

(6)前后端都开发完,前端从Mock数据切换到正式数据,联调通常都会非常顺利,因为前后端双方都完全遵守了接口定义的规范。

3.2 接口管理现状

一、常用解决方案

(1)使用Swagger管理API文档。
(2)使用Postman调试API。
(3)使用MockJs等工具Mock API数据。
(4)使用JMeter做API自动化测试。

二、存在的问题
维护不同工具之间数据一致性非常困难、低效。并且这里不仅仅是工作量的问题,更大的问题是多个系统之间数据不一致,导致协作低效、频繁出问题,开发测试人员痛苦不堪。
(1)开发人员在Swagger定义好文档后,接口调试的时候还需要去Postman再定义一遍。
(2)前端开发Mock数据的时候又要去MockJs定义一遍,还需要手动设置Mock规则。
(3)测试人员需要去JMeter再定义一遍。
(4)前端根据MockJs Mock出来的数据开发完,后端根据Swagger定义的接口文档开发完,各自都试测试通过了,本以为可以马上上线,结果一对接发现各种问题:

开发过程中接口变更了,只修改了Swagger,但是没有及时同步修改 MockJs。后端开发的接口数据类型和文档不一致,肉眼难以发现问题。同样,测试在JMeter写好的测试用例,真正运行的时候也会发现各种不一致。时间久了,各种不一致会越来越严重。

3.3 Apifox解决方案

软件测试-1-软件测试知识架构和API接口常识_第6张图片

Apifox=Postman+Swagger+Mock+JMeter

Apifox是API 文档、API 调试、API Mock、API自动化测试一体化协作平台。通过一套系统、一份数据,解决多个系统之间的数据同步问题。只要定义好接口文档,接口调试、数据 Mock、接口测试就可以直接使用,无需再次定义;接口文档和接口开发调试使用同一个工具,接口调试完成后即可保证和接口文档定义完全一致。高效、及时、准确!

(1)接口设计:Apifox接口文档遵循OpenApi 3.0(原Swagger)、JSON Schema规范的同时,提供了非常好用的可视化文档管理功能,零学习成本,非常高效。并且支持在线分享接口文档。

(2)数据模型:可复用的数据结构,定义接口返回数据结构及请求参数数据结构(仅JSON和XML模式)时可直接引用。支持模型直接嵌套引用,直接JSON/XML智能导入,支持oneOf、allOf等高级组合模式。

(3)接口调试:Postman有的功能,比如环境变量、前置/后置脚本、Cookie/Session全局共享等功能,Apifox都有,并且比Postman更高效好用。接口运行完之后点击保存为用例按钮,即可生成接口用例,后续可直接运行接口用例,无需再输入参数,非常方便。自定义脚本100%兼容Postman语法,并且支持运行javascript、java、python、php、js、BeanShell、go、shell、ruby、lua等各种语言代码。

(4)接口用例:通常一个接口会有多种情况用例,比如参数正确用例、参数错误用例、数据为空用例、不同数据状态用例等等。运行接口用例时会自动校验数据正确性,用接口用例来调试接口非常高效。

(5)接口数据Mock:内置Mock.js规则引擎,非常方便mock出各种数据,并且可以在定义数据结构的同时写好mock规则。支持添加“期望”,根据请求参数返回不同mock数据。最重要的是Apifox零配置即可Mock出非常人性化的数据,具体在本文后面介绍。

(6)数据库操作:支持读取数据库数据,作为接口请求参数使用。支持读取数据库数据,用来校验(断言)接口请求是否成功。

(7)接口自动化测试:提供接口集合测试,可以通过选择接口(或接口用例)快速创建测试集。目前接口自动化测试更多功能还在开发中,敬请期待!目标是:JMeter有的功能基本都会有,并且要更好用。

(8)快捷调试:类似Postman的接口调试方式,主要用途为临时调试一些无需文档化的接口,无需提前定义接口即可快速调试。

(9)代码生成:根据接口及数据数据模型定义,系统自动生成接口请求代码、前端业务代码及后端业务代码。

(10)团队协作:Apifox天生就是为团队协作而生的,接口云端实时同步更新,成熟的团队/项目/成员权限管理,满足各类企业的需求。

3.4 Apifox应用

一、接口调用
示例项目->快捷请求。
软件测试-1-软件测试知识架构和API接口常识_第7张图片

http://10.23.243.1:8003/iplat/service/BIDM100/queryTimeSeriesHistoryData

body如下:
{
      "params": {
            "deviceCode": "TMA100001",
            "pointId": "00",
            "startTime": 1654845646000000000,
            "endTime": 1654949646000000000,
            "kpiId": "G电压",
            "aggregateFunc": 1
      }
}

二、使用python3

# encoding:utf8
# Post Data to webpage
import datetime
import json
import requests

req_url = 'http://10.23.243.1:8003/iplat/service/BIDM100/queryTimeSeriesHistoryData'

data_dict = {
    "params":
    {
        "deviceCode": "TMA100001",
        "pointId": "00",
        "startTime": 1654845646000000000,
        "endTime": 1654949646000000000,
        "kpiId": "G电压",
        "aggregateFunc": 1
    }
}

data_json = json.dumps(data_dict)

'''
一、如果使用参数json传递数据,可以直接传入字典;
会自动将字典类型的对象转换为json格式;
并将Content-Type置为application/json。
'''
response = requests.post(url=req_url, json=data_dict)



'''
二、如果使用参数data传递数据,例如【json.dumps()】,
一定要在header中指定Content-Type
'''
req_headers = {'Content-Type': 'application/json'}
response = requests.post(url=req_url, headers= req_headers,data= data_json)


print(response.content)

你可能感兴趣的:(软件测试,软件测试)