一篇文章了解_接口测试

1.为什么要做接口测试

2018年10月6日 星期六

15:05

本文讨论的接口均是服务级的接口,不是代码级

接口是什么

在讨论为什么要做接口测试之前,我们可以先稍微了解一下接口是什么?

接口可以很不准确的理解成是与资源打交道,这个资源可能是本系统的,也可能是其他系统的。

举个例子,假如我们在开发1个bug管理系统,该系统需要拿到公司的所有开发和测试人员的信息,这样开发和测试人员不用注册都可以登录进去了,这应该很好理解。

那么这些人员的信息储存在哪里呢?一般存储在hr系统里。现在的需求更加明确了,我们要到hr系统中去拿到人员信息,获取hr系统中的人员资源。

怎么拿呢?很多种方式,可以直接把hr系统的数据库拷贝一份放到bug管理系统里,不过这样不好,因为数据的同步会有点麻烦;还可以直接连hr系统的数据库去查,这样也不太好,这样我们就需要了解hr系统的数据存储结构和逻辑,一旦hr系统的数据字段发生改变,bug管理系统也要去该,以便同步。

比较好的做法是,hr系统暴露一些接口,通过这些接口去获取人员信息资源,这样bug系统就不需要关心hr系统的数据存储实现了。

这些接口可能是这样的:

  • 登录的接口,提供人员的用户名和密码,去hr系统中判断该人员是否存在,如果存在验证用户名和密码,如果验证通过就返回1个token,该token就是这个人员的通行证,通过token可以登录到bug管理系统中去;
  • 获取人员信息的接口,返回该人员的职位:测试还是开发,以及用户名,昵称等信息;

综上:接口可以理解成是不同系统或模块之间资源交流方式;

接口测试实际上是黑盒测试

作为黑盒测试,基本的测试思路是通过输入和输出判断被测系统或者对象的逻辑。

获取人员的信息,我需要把人员的用户名传给hr系统接口,这样hr系统的接口会返回给我用户的一些更加具体的信息。这里的输入是用户名,输出是用户的详细信息。

为什么要做接口测试

既然是接口获取和操作资源的方式,而大部分系统和产品中,资源一般都是产品的核心,比如微信核心资源就是通讯录关系链和聊天记录等,因此资源是必测的。

另外接口中大部分的内容是数据,通过数据的对比我们能推测到系统和产品的逻辑,测接口就是测逻辑。

最后接口中的返回相对单纯,不像web页面,html代码中有太多ui的东西,ui最不稳定,变化太快,接口相对稳定一点点,但是里面的干扰信息更少,断言相对容易很多。

接口测试用例怎么写

还是3a原则,这个我以前的回答里有。

  • A: arrange 初始化测试数据,就是造数据,这里的数据有我们输入的数据,也有目标接口所涉及的资源,比如hr系统中的用户信息,我们必须先有几条人员的详细信息才能去测获取人员信息的接口(当然只是正常的流程,我们有时候还需要清掉数据以便测试资源为空的情况);
  • A: act 调用接口,传入输入数据;
  • A: assert 断言, 对返回的资源信息进行断言,比如获取用户信息的接口返回了用户信息之后,我们要判断返回的用户是不是我们想要的那个用户,我们获取的是李雷的信息,接口如果返回韩梅梅,那么接口的逻辑就是不对的;

接口测试笔试题

测试教程网接口测试笔试题

有哪些常见的接口

  • 携程订飞机票,飞机票的信息一般都是通过各大航空公司的接口拿到的;
  • 淘宝的物流信息,一般淘宝的物流信息都是通过各个物流公司的接口拿到的;
  • 第三方微博客户端,个人用户的微博等信息都是通过微博的接口拿到的;

常见的接口测试工具

  • postman: 推荐。基本功能免费。最简单的基于http接口的调试和测试工具;
  • jmeter:后置处理器配合断言基本上可以满足接口测试需求,就是测试报告要做二次开发
  • 自己撸代码:推荐。配合类似xunit测试框架,基本可以满足一切需求;零基础实现python接口自动化视频教程,一起撸代码吧
  • soapui: 收费的;
  • insomnia:强力推荐。postman的弱化版,基本功能免费,重要的是工具代码开源,可以自己改;
  • paw: 强力推荐。mac上最强,淘宝买个授权好像就百把块钱;

 

 

2.最简单的接口长什么样

2018年10月7日 星期日

12:08

 

接口测试由浅入深的学习才可以尽量减少挫败感,避免从入门到放弃的悲剧。

从入门到放弃其实最可惜,毕竟花了那么多时间去学习,最后放弃掉,花费掉的青春就一去不复返了。

获取资源的接口

我们先来看一下获取资源的接口。

v2ex是一个讨论区,也就是大家常说的论坛,这是一个小众的讨论区,基本上只有程序员光顾。

v2ex里有很多的节点,节点可以理解成是讨论板块,比如这个板块就是讨论python技术的。

v2ex提供了一些接口,可以让我们去获取v2ex站点的一些资源,比如讨论区的信息,热门帖子等,这很符合互联网的分享精神。

v2ex的api描述页面在这里, 文档相对简洁,基本上是给看得懂的人看的。

我们仔细研究一下获取节点信息的接口。这个接口可以获取指定节点的名字,简介,URL 及头像图片的地址。

下面是这个接口的具体描述

获得指定节点的名字,简介,URL 及头像图片的地址。

https://www.v2ex.com/api/nodes/show.json

Method: GET
Authentication: None
接受参数:

name: 节点名(V2EX 的节点名全是半角英文或者数字)
例如:

https://www.v2ex.com/api/nodes/show.json?name=python

我们从这个接口文档里可以获得哪些信息呢?换一句话说,我们能不能通过接口文档去了解这个接口是做什么的呢?

接口分析

从上面的描述里,我们可以得到下面一些信息

  • 接口的协议:https协议,也就是更安全版本的http协议;
  • 请求的方法:http协议里定义了一些请求的方法或者叫动词,这些方法和动词可以进一步定义请求的目的,比如是获取资源还是创建资源等;上面的例子里,请求方法是GET;
  • 请求参数:http协议里规定了请求的时候可以传递一些参数给服务端,这些参数可以更加具体的描述资源,比如获取多少个资源,这个资源的名字是什么。上面的例子里,我们可以传递name参数指定资源的名称,比如获取名字叫python的资源,这里资源就是节点;
  • 鉴权:简单来说,就是要不要登录,很显然,上面的接口是不需要登录的

合起来,我们应该可以得到这样的信息:

当我们发送:https://www.v2ex.com/api/nodes/show.json?name=python,这个http的get请求给服务器之后,服务器应该返回相应的资源,那么这时候就需要探讨一下

如何查看接口的返回

我们现在已经知道了这个接口的情况了,如何查看接口的返回呢?

我们可以使用一些辅助工具帮助我们进行接口的调用,查看接口返回,最简单的跨平台调试工具推荐使用postman

 

 

3.入门级接口测试工具:postman的安装

2018年10月7日 星期日

12:08

 

关于postman

postman是跨平台的接口调试及测试工具,非常适合初学者使用。

安装postman

下载地址

请选择相应的版本下载,windows版下载完成后双击安装就好了。

postman的界面

发送第一个请求

我们发送上一节里提到的获取v2ex节点信息的api。

https://www.v2ex.com/api/nodes/show.json?name=python

具体步骤如下

postman是如何工作的

从原理图上可以看出,postman发送请求给服务器,然后从服务器接受响应,最后在postman中展示出来。

服务器响应

上图就是服务器的响应详情,这里包含了一些重要信息

  • 状态码: 200,表示响应是ok的
  • Body: 返回的主体
  • Headers: 可以简单的理解为一些键值对,对请求的主体起到了补充的作用
  • Time: 响应时间
  • Size: 响应的大小

json字符串

有基础的同学可以看出来,响应的主体是json格式的字符串,那么什么是json格式字符串呢?下一节我们将详细讲解。

 

 

4.Json简介

2018年10月7日 星期日

12:09

 

官方解释

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。

什么是JSON

首先json是字符串。

大家都知道,字符串是用来传递信息的。json字符串实际上就是一种规定了格式的字符串,

通过这种格式,我们可以在不同的编程语言之间互相传递信息,比如我们可以把javascript的对象转换成json传递给java,这样java可以反解析出java语言自身代表的对象;同理,我们可以把java对象转成json,通过解析json,python语言可以把json转成是自身的dict或者是list,json统一了交流的格式,使得信息可以在不同的语言间顺畅传递。

JSON解析的简单例子

比如,我们可以把json字符串转成python语言的dict

#coding: utf-8
import json

json_str = """
{
    "id" : 90,
    "name" : "python",
    "url" : "http://www.v2ex.com/go/python",
    "title" : "Python",
    "title_alternative" : "Python",
    "topics" : 7646,
    "stars" : 4862,

"header" : "这里讨论各种 Python 语言编程话题,也包括 Django,Tornado 等框架的讨论。这里是一个能够帮助你解决实际问题的地方。",

"footer" : null,

"created" : 1278683336,
    "avatar_mini" : "//v2ex.assets.uxengine.net/navatar/8613/985e/90_mini.png?m=1504080972",
    "avatar_normal" : "//v2ex.assets.uxengine.net/navatar/8613/985e/90_normal.png?m=1504080972",
    "avatar_large" : "//v2ex.assets.uxengine.net/navatar/8613/985e/90_large.png?m=1504080972"
}
"""
res = json.loads(json_str)

print(res['id']) # 90
print(res['name']) # python
print(res['url']) # http://www.v2ex.com/go/python

 

 

5.3A原则

2018年10月7日 星期日

12:09

 

3A原则原本是单元测试用例编写时应该遵循的基本原则,不过我们可以扩展到接口的自动化测试用例编写中来。

我们首先看一下3A原则在每一层自动化测试中的具体应用。

单元测试用例

  • Arrange: 初始化测试对象或者准备测试数据
  • Act : 调用被测方法
  • Assert: 断言

给一个例子(c#)

[TestMethod] 
public void Withdraw_ValidAmount_ChangesBalance() 

    // arrange 
    double currentBalance = 10.0; 
    double withdrawal = 1.0; 
    double expected = 9.0; 
    var account = new CheckingAccount("JohnDoe", currentBalance); 
    // act 
    account.Withdraw(withdrawal); 
    double actual = account.Balance; 
    // assert 
    Assert.AreEqual(expected, actual); 

服务间的接口测试用例

服务间的接口测试实际上是黑盒测试,3A原则也适用于这种测试用例的编写

  • A: arrange 初始化测试数据,就是造数据,这里的数据有我们输入的数据,也有目标接口所涉及的资源,比如hr系统中的用户信息,我们必须先有几条人员的详细信息才能去测获取人员信息的接口(当然只是正常的流程,我们有时候还需要清掉数据以便测试资源为空的情况);
  • A: act 调用接口,传入输入数据;
  • A: assert 断言, 对返回的资源信息进行断言,比如获取用户信息的接口返回了用户信息之后,我们要判断返回的用户是不是我们想要的那个用户,我们获取的是李雷的信息,接口如果返回韩梅梅,那么接口的逻辑就是不对的;

举个例子(python)

def test_get_task_by_id(self):
# arrange
        create_task_res = self.create_task('test', 'desc')
        new_id = create_task_res['id']

# act
        url_for_get_by_id = self.ip + '/api/tasks/' + str(new_id)
        res = requests.request("GET", url_for_get_by_id).json()

# assert
        self.assertEqual(res['id'], new_id)

手工测试用例

手工的功能测试用例也可以用3A原则来编写。

  • Arrange: 准备被测功能相关的测试数据,比如往系统里录入一批工单以便测试工单的分页功能
  • Act : 调用被测的功能,实际上这就是我们一直讲的测试步骤
  • Assert: 断言

举个例子

# arrange and act
打开chrome浏览器并跳转至http://localhost/wordpress/wp-login.php
在用户名文本框中输入admin
在密码文本框中输入admin
点击登陆按钮
# assert
浏览器跳转到http://localhost/wordpress/wp-admin/
右上角出现“你好,admin”字样

总结

总之对于接口的自动化测试用例说来,遵循3A原则就意味着

  • Arrange: 测试用例执行之前需要准备测试数据,包括需要输入的数据及存量数据
  • Act: 通过不同的参数来调用接口,并拿到返回
  • Assert: 必须做断言,否则用例就没有任何意义了

 

 

6.unittest框架

2018年10月7日 星期日

12:10

在我们真正的编写测试用例之前,我们需要了解一下测试框架。

unittest是python自带的单元测试框架,尽管其主要是为单元测试服务的,但我们也可以用它来做接口的自动化测试。

unittest框架为我们编写用例提供了如下的能力

  • 定义用例的能力。unittest框架有一套固有套路,可以让我们定义测试用例时更加简单和统一
  • 断言的能力。unittest框架提供了一系列的断言
  • 各种执行策略。通过test suit或者扩展的方式,我们可以自定义用例执行的策略

简单的例子

import unittest

class StringTestCase(unittest.TestCase):
    def setUp(self):
        # Arrange
        self.test_string = "This is a string"

def testUpper(self):
        # Act and Assert
        self.assertEqual("THIS IS A STRING", self.test_string.upper())

if __name__ == '__main__':
    unittest.main()

剖析

import unittest

导入unittest库,不导入就没办法使用,好比手机如果要使用某个app就必须先安装该app一样,是套路,记住就好。

 

class StringTestCase(unittest.TestCase):

定义测试类,初学者看到这一行就害怕,其实大可不必。这还是套路,测试类的名字你可以随意取,当然了首字母最好大写,这样更符合规范一些。所有的测试类都必须直接或间接的继承自unittest.TestCase类。总之,这还是套路,记住就好。

 

def setUp(self):
    # Arrange
    self.test_string = "This is a string"

继续套路。setUp(self)方法是一个钩子方法,在每个测试用例执行之前都会执行一次,是做数据初始化的好地方。

在上面的例子里,我们为每一个测试方法都定义了被测对象,self.test_string

 

def testUpper(self):
    # Act and Assert
    self.assertEqual("THIS IS A STRING", self.test_string.upper())

套路继续。这里定义了一个名为testUpper的测试方法,这个方法就是一个测试用例。

注意,只有方法名以test开头的方法才是测试用例

self.assertEqual是一个断言方法,作用是如果第一个参数跟第二个参数相等,那么用例通过,否则用例失败,并在测试报告中打印出错误原因。上面的例子里,我们判断self.test_string.upper()方法会将"This is a string"字符串转换成"THIS IS A STRING"

 

if __name__ == '__main__':
    unittest.main()

最后依然是套路,上面的代码表示,如果直接执行该python文件的话,就运行所有的测试类里的测试用例,也就是运行所有的以test开头的方法。

总结

使用unittest的话需要记住下面的几点

  • 导入unittest
  • 定义继承自unittest.TestCase的测试类
  • 定义以test开头的测试方法,这个方法就是测试用例,你可以在一个类里定义n个测试用例
  • 断言
  • unittest.main()是执行测试用例最简单的方式

 

 

 

7.requests库

2018年10月7日 星期日

12:10

 

requests库可以极大的简化我们发送http请求及获取响应的代码,简洁而优雅。

简单示例

>>> import requests
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>>> r.status_code
200
>>> r.headers['content-type']
'application/json; charset=utf8'
>>> r.encoding
'utf-8'
>>> r.text
u'{"type":"User"...'
>>> r.json()
{u'private_gists': 419, u'total_private_repos': 77, ...}

上面的例子相信大家很容易看明白,在我们做接口自动化测试的时候,我们往往使用requests的提供的接口发送请求和获取响应,并根据响应类型将响应转换成python自建的数据结构。比如上面的例子里,我们将响应的json字符串转换成了python的dict。

安装

官方文档 中文文档

快速开始

官方文档 中文文档

划重点

英文不好的同学可以参考中文文档的相关章节。

 

 

8.第一个用例

2018年10月7日 星期日

12:10

 

前面铺垫了很多的基础知识,掌握基础知识是做基于http接口自动化测试的前提,不建议直接跳过。

前提条件

学习本节需要有一些前提条件

  • 安装了postman
  • 安装了python
  • 安装了requests

用例描述

认识测试对象这一节里有过描述。


获得指定节点的名字,简介,URL 及头像图片的地址。

https://www.v2ex.com/api/nodes/show.json

Method: GET
Authentication: None
接受参数:

name: 节点名(V2EX 的节点名全是半角英文或者数字)
例如:

https://www.v2ex.com/api/nodes/show.json?name=python

# 响应

{
    "id" : 90,
    "name" : "python",
    "url" : "http://www.v2ex.com/go/python",
    "title" : "Python",
    "title_alternative" : "Python",
    "topics" : 7669,
    "stars" : 4870,

"header" : "这里讨论各种 Python 语言编程话题,也包括 Django,Tornado 等框架的讨论。这里是一个能够帮助你解决实际问题的地方。",

"footer" : null,

"created" : 1278683336,
    "avatar_mini" : "//v2ex.assets.uxengine.net/navatar/8613/985e/90_mini.png?m=1504279401",
    "avatar_normal" : "//v2ex.assets.uxengine.net/navatar/8613/985e/90_normal.png?m=1504279401",
    "avatar_large" : "//v2ex.assets.uxengine.net/navatar/8613/985e/90_large.png?m=1504279401"
}

使用postman调试接口

在写用例之前,我们先在postman里把接口调通,大家可以参考之前这篇

然后选择右上角的Code菜单,如下图所示

选择导出为python requests的代码,拷贝到系统剪切板,如下图所示

导出的代码应该是这个样子的

import requests

url = "https://www.v2ex.com/api/nodes/show.json"

querystring = {"name":"python"}

headers = {
    'cache-control': "no-cache",
    'postman-token': "a596dcc5-ab8b-8456-79c7-94a6ac11378e"
    }

response = requests.request("GET", url, headers=headers, params=querystring)

print(response.text)

使用unittest重构代码

导出的代码只是3A里的Arrange和Act,我们使用unittest来重构代码

新建文件v2ex_api_case.py

import requests
import unittest

class V2exAPITestCase(unittest.TestCase):

def test_node_api(self):
        url = "https://www.v2ex.com/api/nodes/show.json"
        querystring = {"name":"python"}
        response = requests.request("GET", url, params=querystring).json()
        self.assertEqual(response['name'], 'python')
        self.assertEqual(response['id'], 90)

if __name__ == '__main__':
    unittest.main()

运行用例

使用下面的命令可以运行用例

python v2ex_api_case.py

运行结果

.
---------------------------------------
Ran 1 test in 0.437s

OK

总结

  • postman可以帮助我们完成50%左右的工作,比如调试接口,导出部分代码等
  • 使用unittest重构用例可以帮助我们添加断言,提供在命令行执行的能力,很容易跟ci工具进行集成

 

 

 

9.什么是mock server

2018年10月7日 星期日

12:11

 

使用场景

前端客户端团队和后端服务端团队往往节奏是不一致的。前端很多情况下需要等待后台的api开发完成后才能进行开发联调和测试,这种前后端不对称就造成了前后端团队节奏不一致,从而造成整个项目/产品交付/发布延期。

有一种解决方案的思路是前后端先约定好后端提供的api接口的细节,前端人员自行先模拟出这些后端的实现,当然这些实现是假的,不过前端可以去调用这些假的实现,而且能拿到返回,这样一来前端就不需要等待后端开发完成才开始工作了。

但是这样还是会有问题,前端实现的假的api没办法迅速反映出后端的变化。简单来说就是后端可能在约定好的api接口上进行了些许修改,而没有知会前端人员,这样前端的假的api实现并没有相应更新,在正式联调时就会出现问题。

像这种假的api实现,不管是前端实现的还是后端去实现的,我们可以称之为mock server。

  • mock表示这个api返回的数据是假的,仅作为测试用的
  • server表示需要启动服务,说到底这是一个服务程序

契约测试

由于前后端往往有一些信息不对称,导致约定的api可能在前后端都会发生变化,所以保证前后端的一致性就成了一个挑战。

这时候有人提出了契约测试,大致思想是前后端共用一份契约,约定了api的细节,前后端的任何变化都需要先修改契约,然后通过契约去通知前后端团队,统一更新实现。这也是契约精神的表现。

如果为契约测试设置一种测试工具的话,我会规划下面一些特性

  • 契约的描述工具:也就是契约长什么样子,用什么工具去定义才能让前后端团队秒懂
  • 通过契约自动生成mock server实现,这样前端团队就可以拿来即用了,如果契约修改了,那么前端团队也很容易感知到
  • 通过契约自动生成接口测试用例,这样通过持续运行这些接口测试用例,后端团队就可以第一时间发现契约的修改

 

 

 

10.使用flask实现mock server

2018年10月7日 星期日

12:11

 

flask

flask是python实现的简单的web框架,与django互补。

flask教程

如何理解flask

  • 路由 -> /request/uri
  • handler -> 路由进来之后处理request并返回response的逻辑

最简单的例子

from flask import Flask
app = Flask(__name__)

@app.route("/") # 路由
def hello(): # handler
    return "Hello World!"

实现mocked smile task api

获取所有的任务

GET /api/tasks # get all tasks

查看一个任务的详情

GET /api/tastks/:task_id # get a task with task_id

完成一个任务

PUT /api/tastks/:task_id # complete a task

代码


from flask import Flask, jsonify, g
import copy
app = Flask(__name__)

@app.before_request
def set_up_data():
    g.data = [
        {'id': 1, 'title': 'task 1', 'desc': 'this is task 1'},
        {'id': 2, 'title': 'task 2', 'desc': 'this is task 2'},
        {'id': 3, 'title': 'task 3', 'desc': 'this is task 3'},
        {'id': 4, 'title': 'task 4', 'desc': 'this is task 4'},
        {'id': 5, 'title': 'task 5', 'desc': 'this is task 5'}
    ]

g.task_does_not_exist = {"msg": "task does not exist"}

@app.route('/api/tasks')
def get_all_tasks():
    return jsonify(g.data)

@app.route('/api/tasks/<int:task_id>')
def get_task(task_id):
    if task_id > 0 and task_id <= len(g.data):
        return jsonify(g.data[task_id])
    else:
        return jsonify(g.task_does_not_exist)

@app.route('/api/tasks/<int:task_id>', methods=['PUT'])
def complete_task(task_id):
    if task_id > 0 and task_id <= len(g.data):
        tmp = copy.deepcopy(g.data[task_id])
        tmp['done'] = True
        return jsonify(tmp)
    else:
        return jsonify(g.task_does_not_exist)

运行

set FLASK_APP=smile_task_mock_server.py

flask run
* Serving Flask app "smile_task_mock_server"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

浏览器打开localhost:5000就好了

posted @ 2018-10-07 13:08  Leq123  阅读(861)  评论(0编辑  收藏  举报