# 一、HTTP请求方式
在介绍API测试之前，我觉得有必要简单说一下HTTP的请求方式。  
在正规的接口文档最开始处，一般都会注明对于所有接口的一个约定，包括**使用协议、传输格式、请求方式**等等。  

比如以下chain33的接口文档约定：   
***
![test](https://gitlab.33.cn/flyaway53/test/raw/master/img-folder/4.png)   
***
使用的是POST的请求方式。而有些接口会使用GET的请求方式，两者之前还是存在一定的区别。 
当然HTTP请求方式还有HEAD、OPTIONS、PUT、DELETE、TRACE、CONNECT这些，但测试中一般不会遇到。  

### 那么POST和GET究竟有哪些区别？  
最直观的区别就是GET把参数包含在URL中，POST通过request body传递参数。   
这一点可以在使用Postman进行API测试时得到反馈，GET的请求方式不包含请求体（body），会将请求内容通过url传递。  
![test](https://gitlab.33.cn/flyaway53/test/raw/master/img-folder/5.png)   
如图，问号后的部分为实际的参数请求信息。将参数暴露在url上的方式相对POST来说安全性更低。  

而POST请求方式则是将请求信息放在请求体中，通过约定的格式将请求的信息传递出去。  
***
![test](https://gitlab.33.cn/flyaway53/test/raw/master/img-folder/6.png)  
***
如图是一个接口约定的POST请求格式，将该请求复制到body中，即可发送请求。  

对于**GET**来说，客户端与服务端的交互像是一个提问(如查询操作、搜索操作、读操作)。   

对于**POST**来说，有以下几种情况：  
1. 交互是一个命令或订单(order)，比提问包含更多信息   
2. 交互改变了服务器端的资源并被用户察觉，例如订阅某项服务   
3. 用户需要对交互产生的结果负责   

此外GET和POST还有一个比较大的区别，简单地说就是GET产生一个TCP数据包；POST产生两个TCP数据包。  
对于GET方式的请求，浏览器会把http header和data一并发送出去，服务器响应200（返回数据）；  
而对于POST，浏览器先发送header，服务器响应100 continue，浏览器再发送data，服务器响应200 ok（返回数据）。  

### HTTP状态码 
对接口完成调用后，服务器会返回一个包含HTTP状态码的信息头（server header）用以响应该请求。  
常见的状态码包括：  
* 200 - 请求成功
* 301 - 资源（网页等）被永久转移到其它URL
* 403 - 服务器理解请求客户端的请求，但是拒绝执行此请求
* 404 - 请求的资源（网页等）不存在
* 500 - 内部服务器错误

通过这个可以在API测试时加上对状态码的是否等于200的判断。  


# 二、JSON简介
简单介绍一下JSON，因为我们在调用接口及查看返回值的时候都会非常频繁地看到这个格式的信息。  
**JSON**：JavaScript 对象表示法（JavaScript Object Notation），是一种轻量级的文本数据交换格式。 
JSON具有一些特点及优势：  
一是独立于语言。虽然JSON 使用 JavaScript 语法来描述数据对象，但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。这为使用不同语言编程的前后端衔接提供便利；  
二是JSON 具有自我描述性，更易理解。这意味着值都是可列举的，是“人类可读”的 。  

在 JS 语言中，一切都是对象。  
而JSON 键值对是用来保存 JS 对象的一种方式。  
键/值对组合中的键名写在前面并用双引号 "" 包裹，使用冒号 : 分隔，然后紧接着值：  
`{"title":"string"}`  
其他的数组等格式无非是在这个基础上的扩展:
```
{
    "id":int32,
    "result":{
        "title":"string",
        "app":"string",
        "chain33":"string",
        "localDb":"string"
        },
    "error":null
}
```  
我们在调用接口时需要遵循约定好的JSON格式，来传递自己需要的测试参数。  
同样，在验证返回值时对比JSON格式的返回值，是否符合预期要求。无论从使用还是理解上都是非常容易上手的。  

# 三、API测试

## 接口是什么？
在计算机中，接口是计算机系统中两个独立的部件进行信息交换的共享边界。举个例子，我提供加法的计算接口，你给我两个数，我就给你返回一个和。  
## 什么是接口测试？  
狭义的接口测试指的是对接口进行测试，上个例子中测试的是不同输入参数时，我加法的返回是否正确。一般讲的接口测试是这种。

广义的接口测试包含接口提供方、接口调用方的测试。 比如，你调用我的接口执行加法，我返回错误的响应，或者我响应超时，这时你的处理是否正确。  

## 为什么要做接口测试？  
1. 一般的功能测试，覆盖不到多种情况。  
2. 从安全角度考虑，只依赖于前端的校验不能满足要求，需要服务端也要做校验，比如：APP上一个充值的界面，充值金额输入框不能输入负数，测试时，我们要确保服务器也做了这个校验。有一万种手段绕过前端验证。  
3. 从安全的角度考虑，用户密码、其他用户隐私信息传输时都需要进行加密。  
4. 当APP的代码不更新，而服务端代码更新时，直接通过接口自动化测试就能快速知道是否影响APP的功能。  
5. 很多系统是没有界面的，只提供接口功能，没法通过界面的方式进行测试。  
6. 接口测试相对容易实现自动化，也容易实现持续集成，且相对UI自动化也比较稳定，可以减少人工回归测试人力成本与时间，缩短测试周期，支持后端快速发版需求。接口持续集成是为什么能低成本高收益的根源。

## 怎么做接口测试？
“怎么做接口测试”这个问题可以分解为两个问题：  

1. 怎么设计接口测试用例？
2. 怎么执行接口测试？

### 怎么设计接口测试用例？  
![test](https://gitlab.33.cn/flyaway53/test/raw/master/img-folder/7.png)  

### 怎么执行接口测试？  
首先需要根据使用的协议来选择具体的工具。    
基于HTTP接口测试的工具:restclient、postman、jmeter、soapUI等等，基于JAVA代码的httpclinet、JAVA自带的URLConnection等。   
其中， 
* Fiddler、SOAPUI、PostMan等可以做半自动的接口自动化测试；  
* 使用Robot Framework、JMeter做全自动化的接口自动化测试；   
* 自己用代码做全自动的接口自动化测试，如Java+testNG；   

## API 测试的基本步骤  
通常来讲，无论采用什么 API 测试工具，API 测试的基本步骤主要包括以下三个步骤：  
* 准备测试数据；  
* 通过API测试工具，发起对被测API的request；  
* 验证返回结果的response。  

对我们现在的情况来说，使用postman解决一般的手工接口测试已经能够满足，而进行到自动化阶段更多会选择Jmeter；具体测试工具的使用就不详细介绍了。   
**需要注意的是接口测试与功能测试的区别。进行接口测试时，主要的关注点是接口，主要验证的也是功能的接口，而不是功能本身。**  
## 如何应对复杂场景的API测试？  
在实际工作中，面对单个API的调用并完成测试是比较少见的。更普遍的情况下，我们会遇到一些复杂的测试场景。  

### 测试场景一：被测业务操作是由多个API调用协作完成

很多情况下，一个单一的前端操作可能会触发后端一系列的API调用，由于前端测试的相对不稳定性，或者由于性能测试的要求，你必须直接从后端通过模拟API的顺序调用来模拟测试过程。   

这时，API的测试用例就不再是简单的单个API调用了，而是一系列API的调用，并且经常存在后一个API需要使用前一个API返回结果的情况，以及需要根据前一个API的返回结果决定后面应该调用哪个API的情况。  

好在，我们已经实现了API的调用和结果解析的代码化，这也就意味着我们可以很灵活地直接用代码来处理这些场景了。 比如，通过代码将上个API调用返回的response中的某个值传递给下一个API，再比如根据上一个API的返回结果决定下一个应该调用哪个API等。  

除此之外，我们还需要迫切解决的一个问题是：如何才能高效地获取单个前端操作所触发的API调用序列。解决这个问题的核心思路是，通过网络监控的手段，捕获单个前端操作所触发的API调用序列。比如，通过类似于Fiddler之类的网络抓包工具，获取这个调用序列；又比如，目前很多互联网公司还在考虑基于用户行为日志，通过大数据手段来获取这个序列。   
 
### 测试场景二：API测试过程中的第三方依赖  

API之间是存在依赖关系的，比如你的被测对象是API A，但是API A的内部调用了API B，此时如果由于某种原因，API B在被测环境中处于不可用状态，那么API A的测试就会受到影响。  

在单体架构下，通常只会在涉及到第三方API集成的场景中才会遇到这个问题，所以还不算严重。但是，在微服务架构下，API间相互耦合的依赖问题就会非常严重。  

解决这个问题的核心思路是，启用Mock Server来代替真实的API。  

### 测试场景三：异步API的测试  

异步API是指，调用后会立即返回，但是实际任务并没有真正完成，而是需要稍后去查询或者回调（Callback）的API。  

一直以来，异步API测试都是API测试中比较困难的部分。在我看来，对异步API的测试主要分为两个部分：一是，测试异步调用是否成功，二是，测试异步调用的业务逻辑处理是否正确。 
 
异步调用是否成功，这个还比较简单，主要检查返回值和后台工作线程是否被创建两个方面就可以了。 
 
但是，对异步调用业务逻辑的测试就比较复杂了，因为异步API通常发生在一些比较慢的操作上，比如数据库I/O、消息队列I/O等，此时测试往往需要去验证数据库中的值、消息队列中的值等，这就需要测试代码具有访问和操作数据库或者消息队列的能力。  

在实际工程项目中，这些能力一般会在测试框架级别提供，也就是说要求API测试框架中包含对应的工具类去访问和操作数据库或者消息队列等。  
# 四、API自动化测试框架  
接口自动化测试逐渐成为目前投入产出比最高的测试技术。简单地使用Postman进行API测试，会遇到一些显而易见的问题：  
1. 当需要频繁执行大量的测试用例时，基于界面的API测试就显得有些笨拙；  
2. 基于界面的操作的测试难以与CI/CD流水线集成。  

#### 那么理想中的接口自动化测试框架应该是怎样的呢？  
 一些常见的场景：  
* 测试或开发人员在定位问题的时候，想调用某个接口查看其是否响应正常；  
* 测试人员在手工测试某个功能点的时候，需要一个订单号，而这个订单号可以通过顺序调用多个接口实现下单流程；  
* 测试人员在开始版本功能测试之前，可以先检测下系统的所有接口是否工作正常，确保接口正常后再开始手工测试；  
* 开发人员在提交代码前需要检测下新代码是否对系统的已有接口产生影响；  
* 项目组需要每天定时检测下测试环境所有接口的工作情况，确保当天的提交代码没有对主干分支的代码造成破坏；  
* 项目组需要定时（30分钟）检测下生产环境所有接口的工作情况，以便及时发现生产环境服务不可用的情况；  
* 项目组需要不定期对核心业务场景进行性能测试，期望能减少人力投入，直接复用接口测试中的工作成果。  

面对这样的需求，比较适合我们的是选择一些成熟的测试框架，如基于 Java 的 OkHttP 和 Unirest、基于Python的http.client和Requests、基于NodeJS的Native和Request等，或者一些更有针对性的开源框架。