接口测试笔记

2020-07-08  吴水荣 
接口测试
应用程序编程接口(API:Application Programming Interface):以HTTP协议形式提供,定义了输入、输出、功能描述的服务
即是规矩的制定
流程:
1、接口的功能测试(先要保证接口是正确的)
2、测试接口的数据(传递一些特殊的数据,保证接口没有问题)----比如淘宝上不能搜索到一些国家禁止售卖的东西
3、自动化测试脚本的编辑
4、接口的性能、压力测试

HTTP协议:
协议:计算机通信网络中两台电脑 之间进行通信所必须共同遵守的规则或规定
HTTP协议:超文本传输协议,是一种规定了浏览器和服务器之间通信的规则

URL(统一资源定位符)
概念:互联网上源源的地址、位置、每一个资源都有一个唯一的URL
格式:协议://主机地址/路径

GET请求
提交的数据显示在地址栏,不安全;提交的数据量有限制,不重要的的数据使用 GET
比如在淘宝搜索商品时,地址栏会出现商品名字


POST请求
隐式提交数据,更安全;没有数据量大小的限制;重要数据使用POST

数据传递的方式:
键值对:  ?xx=11&xx=22          问号前面是网址,后面是传的值     &前面是属性名,后面是属性值
Json数据:
{
"属性名1":"属性值1",
"属性名2":"属性值2",
"属性名3":"属性值3"
}

HTTP协议之常见响应状态码
1xx:指示信息---表示请求已接收,继续处理
2xx:成功 ---表示请求已被成功接收,理解、接受
3xx:重定向---要完成请求必须进行更一步操作
4xx: 客户端错误---请求有语法错误或请求无法实现
5xx:服务器端错误---服务器未能实现合法的请求

restful风格:
按照一定的规则写出的易读、易懂的api文档;目的是让前端、后端、测试三方在工作的时候有据可循,提升开发的测试的效率(非常制要求)
增删改查四大功能的语法风格:
1、查
方法:get
响应码:200+查询的数据

2、增
方法:post
响应码:201+新增的数据

3、改
方法:put
响应码:200或201 +修改后的数据

4、删
方法:delete
响应码:204    (后面不加东西)

接口测试“聚合数据”地址
https://www.juhe.cn/ucenter/account
13480.....47   maggic108222   


接口自动化测试
2种方式:jmeter和使用python写代码的方式(使用request库)

一、request库
1、安装和查看效果:
pip install requests

pip show requests

2、发送请求
常见的HTTP请求方式:GET、POST、PUT、DELETE、HEAD、OPTIONS
使用requests发送网络请求只需要调用HTTP请求所对应的方法即可

2.1 GET请求
import requests;
response=requests.get()

请求方法的返回值response为Response对象,我们可以从这个对象中获取 所有我们想要的响应信息

实例:
"""
请求方法:GET
响应:
响应对象.url  #获取请求url
响应对象.status_code  #获取响应状态码
响应对象.text   #以文本形式显示响应内容
"""
import requests;
url='http://baidu.com'
#调用GET
response=requests.get(url);
#获取请求url
print('请求url是:',response.url);
#获取响应状态码
print("状态码:",response.status_code);
#获取响应信息 文本形式
print("文本响应内容",response.text);


结果:
请求url是: http://baidu.com/
状态码: 200
文本响应内容 <html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>

GET方法带参使用:
参数:params
方式1:params={"id":1001}
方式2:params={"id":"1001,1002"}
方式3:params={"id":1001,"kw":"北京"}


"""
请求方法:GET
案例:
1、http://www.baidu.com?id=1001
2、http://www.baidu.com?id=1001,1002
3、http://www.baidu.com?id=1001&kw=北京
参数:
params:字典或字符串
响应:
响应对象.url  #获取请求url
响应对象.status_code  #获取响应状态码
响应对象.text   #以文本形式显示响应内容
"""
import requests;
url='http://baidu.com'
#案例1  定义字典
# params={"id":1001}
#案例2  定义列表
# params={"id":"1001,1002"}
#运行的结果是:http://baidu.com/?id=1001%2C1002     %2c是逗号
#案例3  定义列表
params={"id":1001,"kw":"北京"}
#结果:请求url是: http://baidu.com/?id=1001&kw=%E5%8C%97%E4%BA%AC
#调用GET
#请求时带参  params
response=requests.get(url,params=params);
#获取请求url
print('请求url是:',response.url);
#获取响应状态码
print("状态码:",response.status_code);
#获取响应信息 文本形式
print("文本响应内容",response.text);


2.2 POST请求
作用:新增资源
应用:
1)导包
2)调用POST方法  requests.post()
参数:
1)url
2) json  新增请求报文
3) Headers: 请求信息头

案例:
"""
案例:新增学院
参数:
1、json:传入json字符串
2、headers:传入请求信息头内容
响应:
1、响应对象.json()


接口文档里的内容:
2.1 学院--新增
1)请求方法:POST
2)请求地址:http://v.juhe.cn/weather/index     #其实地址不是正确的,因为我本来就没有这个项目,这个是随手拿的天气预报的接口地址
3)请求信息头:Content-Type:application/json
4)调用传入的json串如下(可新增多条,之间用,号隔开)
{
    "data":[
            {
                "dep_id":"T01",
                "dep_name":"Test学院",
                "master_name":"Test-Master",
                "slogan":"Here is Slogan"
            }
    ]
}
"""
#1、导包
import requests;
#调用post
#请求url
url='http://v.juhe.cn/weather/index'
#请求headers
headers={"Content-Type": "application/json"}
#请求json
data = {
        "data": [
            {
                "dep_id": "T01",
                "dep_name": "Test学院",
                "master_name": "Test-Master",
                "slogan": "Here is Slogan"
            }
        ]
}
response=requests.post(url,json=data,headers=headers)
#获取响应对象
print(response.json())
#获取响应状态码
print(response.status_code);


运行结果:
如果是真实存在一个学院管理系统,那结果应该是长这样子的:
{"already_exist":{'count':0, 'results':[]}, 'create_success':{'count':1, 'results':[{"dep_id": "T01", "dep_name": "Test学院", "master_name": "Test-Master", "slogan": "Here is Slogan"}]}}
201

但是因为我其实拿的是天气预报的接口地址,然后参数又是拿安全的参数,所以最终的结果是“错误的请求KEY",如下所示
{'error_code': 10001, 'resultcode': '101', 'reason': '错误的请求KEY', 'result': None}
200

扩展1:
data与json的区别
data:字典对象
json:json字符串
提示:
在python中字典对象和json字符串长得一样,但是后台格式不同,因此直接使用data会报错
转换:
导入json
使用json.dumps(字典对象)  方法来转换

实例:
"""
案例:新增学院
参数:
1、json:传入json字符串
2、headers:传入请求信息头内容
响应:
1、响应对象.json()


接口文档里的内容:
2.1 学院--新增
1)请求方法:POST
2)请求地址:http://127.0.0.1:8000/api/departments
3)请求信息头:Content-Type:application/json
4)调用传入的json串如下(可新增多条,之间用,号隔开)
{
    "data":[
            {
                "dep_id":"T01",
                "dep_name":"Test学院",
                "master_name":"Test-Master",
                "slogan":"Here is Slogan"
            }
    ]
}
"""
#1、导包
import json

import requests;
#调用post
#请求url
url='http://v.juhe.cn/weather/index'
#请求headers
headers={"Content-Type": "application/json"}
#请求json
data = {
        "data": [
            {
                "dep_id": "T01",
                "dep_name": "Test学院",
                "master_name": "Test-Master",
                "slogan": "Here is Slogan"
            }
        ]
}
# response=requests.post(url,json=data,headers=headers);
#如果使用data,则会报Json parse error。直接这样子写是不行的,需要将字典对象转换成json字符串
#response=requests.post(url,data=data,headers=headers);
#将字黄对象转换为json字符串
response=requests.post(url,data=json.dumps(data),headers=headers);
#获取响应对象
print(response.json())
#获取响应状态码
print(response.status_code);


扩展2:响应数据.text与响应数据.json的区别
长相基本一致,但r.json的类型是字典,可以通过键名获取值;r.text只是一个字符串
如果想要取值就要使用.json的方式

2.3 PUT方法   更新资源
应用:
1)导包 import requests
2) 调用put方法  requests.put()



实例
#1、导包
import json

import requests;
#调用post
#请求url
#url='http://v.juhe.cn/weather/index'
#案例的地址原来是这样子的
# url="http://127.0.0.1:8000/api/departments"
#然后因为是要PUT(更新一条学院信息),需要在url后加加上具体的dep_id
url="http://127.0.0.1:8000/api/departments/T01"
#请求headers
headers={"Content-Type": "application/json"}
#请求json
data = {
        "data": [
            {
                "dep_id": "T01",
                "dep_name": "Test学院2",    #假设需要改学院的名称,则在此作出改动
                "master_name": "Test-Master",
                "slogan": "Here is Slogan"
            }
        ]
}
response=requests.put(url,data=json.dumps(data),headers=headers);
#获取响应对象
print(response.json())
#获取响应状态码
print(response.status_code);

2.4  DELETE  删除
url="http://127.0.0.1:8000/api/departments/T01"
response=requests.delete(url)
#获取响应状态码
print(response.status_code);


响应内容:
response.status_code  状态码
response.url
response.encoding   查看响应头部字符编码
response.headers   头信息
response.cookies  
response.text
response.content   字节形式的响应内容
response.json()  JSON形式的 响应内容

实例1:
"""
目标:响应对象常用方法
1.encodeing
    1)获取请求编码
    2)设置响应编码
2.headers
    1)获取响应信息头信息

"""
#1、导包
import requests;
#请求url
url="http://www.baidu.com"
#调用get方法
response=requests.get(url)
#查看默认请求编码
print(response.encoding);

#查看响应内容 text
print(response.text);#汉字会变成乱码
#设置响应编码
response.encoding='utf-8';
print(response.encoding);
print(response.text)
#查看响应信息头   实际工作中比较重要,项目中一般服务器返回的token、session相头信息都在headers中
print(response.headers)


实例2:

"""
目标:响应对象常用方法
1.cookies
    1)获取响应cookies信息
2.content    字节形式的响应内容
    1)图片、视频。。。多媒体格式

"""
#1、导包
import requests;
#请求url
# url="http://www.baidu.com"
url_img='http://www.baidu.com/img/bd_logo1.png?where=super'
#调用get方法
response=requests.get(url_img)
#查看响应cookies    返回一个字典对象
# print("cookies:",response.cookies);
# #通过建名获取响应的cookies的值
# print("cookies信息值为:",response.cookies['BDORZ']);

#以text文本形式解析图片
# print(response.text);   #结果是一堆黑色乱码
#以content形式
print(response.content);#图片流
#b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02\x1c\x00
#将图片写入当前目录baidu.png
with open('./baidu.png','wb') as f:
    f.write(response.content);    #当前目录下会多出一张图片

三、案例
1、怎么获取相关数据(接口地址、数据)
1.1、获取验证码:
在验证码图片上右击鼠标选择复制图片地址:
http://localhost/index.php/Home/User/verify.html
1.2、登录接口获取:
url:勾选preserver log选项(才能抓取受保护数据-302重定向之前的数据)
form data : 以字典形式编写
登录接口:http://localhost/index.php?m=Home&c=User&a=do_login
登录数据:
data = {"username":"1348025***",
    "password: ":"ma****",
    "referurl":"http://localhost/index.php/home/Goods/goodsInfo/id/1",
    "verify_code":"e75i"

}

1.3 订单请求获取
登录成功,点击“我的订单”---复制
http://localhost/index.php/Home/Order/order_list.html


实例:
import requests
url_login = "http://localhost/index.php?m=Home&c=User&a=do_login";
data = {"username":"13480*****",
    "password: ":"******",
    "referurl":"http://localhost/index.php/home/Goods/goodsInfo/id/1",
    "verify_code":"e75i"
        }
r = requests.post(url=url_login,data=data);
print(r.json());

运行结果:
{'status': 0, 'msg': '验证码错误'}

怎么解决验证码错误:cookie

实例:登录测试窝并查看博客
import requests
#请求验证码
url_verify_code = "https://www.testwo.com/user/captcha"   #这个是右键复制图片地址
r = requests.get(url_verify_code);
#获取cookies
r_cookie = r.cookies;
print("获取的cookies:",r_cookie);

# #设置cookies变量
cookies = {"tw_ct":r_cookie['tw_ct']}
# #调用post + 添加cookies
url_login = "https://www.testwo.com/user/login"
data = {
    "referer":"none",
    "name":"******",           //真实用户名
    "password":"******",     //真实密码
    "captcha":"bkst"
}
r = requests.post(url=url_login,data=data,cookies=cookies);

print("返回的是:",r);
url_blog ="https://www.testwo.com/space/blog/2822"
r = requests.get(url=url_blog,data=data,cookies=cookies);
print("博客信息是:",r.text)

运行结果是:
获取的cookies: <RequestsCookieJar[<Cookie tw_ct=A3F5CA041EEFFECEAEB21DA4BFE93799 for www.testwo.com/>]>
返回的是: <Response [200]>
博客信息是:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Cache-Control" content="no-store" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>吴水荣的博客</title>
<script type="text/javascript" src="/scripts/jquery.min.js"></script>
.........
里面就是博客内容
</html>


2、使用session:可以自动保存服务器产生的cookies信息,并且自动在下一条请求时附加:
在requets里,session对象是一个非常常用的对象,它代表一次用户会话:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开
会话能让我们在跨请求时保持某些参数,比如在同一个session实例发出的所有请求之间保持cookie

2.1创建session对象
session = requests.Session();

得到session对象后,就可以调用该对象中的方法来发送请求
session.get()\session.post()\session.delete()\session.put()
提示:无论通过session对象调用哪个方法,返回结果都是response对象
实例:
import requests
#获取session对象
session =requests.session();
#请求验证码  让session对象记录cookies
url_verify_code = "https://www.testwo.com/user/captcha"   #这个是右键复制图片地址
session.get(url_verify_code);
#请求登录
url_login = "https://www.testwo.com/user/login"
data = {
    "referer":"none",
    "name":"shui*****",
    "password":"******",
    "captcha":"bkst"
}
r = session.post(url=url_login,data=data);
#查看登录是否成功,返回200就成功
print("返回的是:",r);
#查看博客
url_blog ="https://www.testwo.com/space/blog/2822"
r = session.get(url=url_blog,data=data);
print("博客信息是:",r.text)

运行结果:
返回的是: <Response [200]>
博客信息是:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Cache-Control" content="no-store" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>吴水荣的博客</title>
......
</html>


3、集成UnitTest来测试接口自动化
3.1   需求:
某网站项目登录功能的接口测试
3.2  用例设计:
ID     模块    接口名称        请求url                    请求类型        请求参数类型    请求参数            预期结果         测试结果
001    登录    获取验证码    https://www.testwo.com/user/captcha        GET       form                    获取到验证码图片
    登录    登录        https://www.testwo.com/user/login        POST       form        {"referer":"none",        登录成功
                                                 "name":"shui****",
                                                "password":"ma***",
                                                "captcha":"bkst"}


002     登录    获取验证码                           GET      form                    获取到验证码图片
    登录    账号不存在    https://www.testwo.com/user/login                       POST      form        {"referer":"none",        提示账号不存在
                                                 "name":"不存在的账号",
                                                "password":"正确的密码",
                                                "captcha":"bkst"}        

003    登录    获取验证码                          GET     form                    获取到验证码图片
    登录    密码错误        https://www.testwo.com/user/login                          POST       form                    {"referer":"none",            提示密码错误    
                                                 "name":"shui****",
                                                "password":"错误的密码",
                                                "captcha":"bkst"}


3.3   搭建unittest结构
import unittest
import requests
#新建测试类
class TestLogin(unittest.TestCase):
    #setUp
    def setUp(self):
        pass
    #tearDonw
    def tearDown(self):
        pass
    #登录成功
    def test_login_success(self):
        pass
    #登录失败  账号不存在
    def test_username_not_exist(self):
        pass
    #登录失败  密码错误
    def test_password_error(self):
        pass

具体代码:
import unittest
import requests
import json
#新建测试类
class TestLogin(unittest.TestCase):
    #setUp
    def setUp(self):
        #获取session对象
        self.session = requests.session();
        #登录url
        self.url_login = 'https://www.testwo.com/user/login'
        #验证码url
        self.url_verify = "https://www.testwo.com/user/captcha"


    #tearDonw
    def tearDown(self):
        #关闭session
        self.session.close();

    #测试方法   登录成功
    def test_login_success(self):
        #调用session对象  获取coookies
        self.session.get(self.url_verify)
        #请求登录
        data = {
            "referer": "none",
            "name": "shui.....",
            "password": "m.......",
            "captcha": "bkst"
        }

        r = self.session.post(self.url_login,data=data);
        print(r.status_code)

        try:
            #断言
            self.assertEqual("200",str(r.status_code));
        except AssertionError as e:
            print(e);


    #登录失败  账号不存在
    def test_username_not_exist(self):
        data = {
            "referer": "none",
            "name": "sh....hahahahaha",
            "password": "m.....",
            "captcha": "bkst"
        }
        r = self.session.post(self.url_login, data=data);


        try:
            # 断言
            self.assertEqual("账号不存在", r.json()['msg']);
        except AssertionError as e:
            print(e);

    #登录失败  密码错误
    def test_password_error(self):
        data = {
            "referer": "none",
            "name": "shui....",
            "password": "m.....",
            "captcha": "bkst"
        }
        r = self.session.post(self.url_login, data=data);

        try:
            # 断言
            self.assertEqual("密码错误",r.json()['msg']);
        except AssertionError as e:
            print(e);

if __name__ == '__mian__':
    unittest.main();

#后面2种的断言那里,r.json()['msg'] 是会报错的,因为这个网站登录时本来就没有返回这种json格式的数据,但是我不知道要写什么,只有对着视频照抄了
#第一1用例那里是执行通过的



黑马头条的接口测试

一、接口测试流程:
1、需求分析
2、挑选需要做自动化测试的功能接口
3、设计测试用例
4、搭建自动化测试环境
5、设计自动化测试项目的框架
6、编写代码
7、执行测试用例
8、生成测试报告并分析结果



二、接口清单整理
1、登录接口
    1.1 请求登录接口
        请求:
        1)请求url:http://ttapi.research.itcast.cn/app/v1_0/autoorizations
        2) 请求方法:POST
        3) 请求参数:Headers = {"Content-Type":"application/json"}
        4)请求报文:
            {"mobile":"13480909876","code":"888888"}   #假设验证码是8888888

        响应:
        1)状态码:201
        2)响应数据
            {"message":"xxx"}
    1.2 获取短信验证码:
        1)请求url:http://ttapi.research.itcast.cn/app/v1_0/sms/codes/:mobile
            (mobile要换成手机号)
        2)  状态码:200
        3)请求方法:GET
    提示:验证码发送成功后,在手机中查找,然后填写到1.1的4)的code里(一个手机要隔一分钟才能重新发验证码)
2、获取用户频道列表
    2.1 请求:
        1)url:http://ttapi.research.cn/app/v1_0/user/channels
        2) 方法:GET
        3) 请求参数:Headers = {"Content-Type":"application/json",
                         "Authorization":"Bearer token信息"}    #那一长串的token信息
        提示:默认token有效期为2小时
    2.2  响应
        1)响应状态码:200
        2)响应数据:{"message":"xxx"}
3、收藏文章
    3.1 请求:
        1)url: http://ttapi.research.cn/app/v1_0/article/collections
        2) 方法:GET
        3) 请求参数:Headers = {"Content-Type":"application/json",
                         "Authorization":"Bearer token信息"}    #那一长串的token信息
        4) 请求报文:{"target":文章id}
    3.2  响应
        1)响应状态码:201
        2)响应数据:{"message":"xxx"}
4、取消收藏文章
    4.1 请求:
        1)url: http://ttapi.research.cn/app/v1_0/article/collections/:target
        2) 方法:DELTETE
        3) 请求参数:Headers = {"Content-Type":"application/x-www-form-urlencoded",
                         "Authorization":"Bearer token信息"}    #那一长串的token信息
        4) 请求报文:{"target":文章id}
    4.2  响应
        1)响应状态码:204
        


四个接口的文档(开发给出)
1、用户认证(登录注册)
基本信息:
Path:  /app/v1_0/authorizations                      #这个是服务器的地址名,真正使用时一定要加上服务器地址名名ip号之类的
Method:POST

接口描述:
1)线上地址
http://ttapi.research.itcast.cn/app/v1_0/autoorizations
2)返回HTPP状态码
1. 201  ok
2. 400  请求参数错误
            包括:参数缺失、手机号格式不正确、验证码失效等
3. 507 服务器数据库异常

3)token说明
1. token用于访问需要身份认证的普通接口,有效期2小时
2. refresh_token 用于在token过期后,获取 新的用户token,有效期14天

请求参数
Headers
参数名称        参数值         是否必须        示例    备注
Content-Type    application/json    是

Body
名称    类型    是否必须    默认值    备注    其他信息
mobile    string    是        手机号
code    string    是        短信验证码(这是在另外一个接口)

返回数据
名称       类型    是否必须    默认值    备注    其他信息
message    string    是        提示信息
data      object     否        数据
  --token    string     是        用户token令牌
 --refresh_token string 是        用于刷新token的令牌


1.5 获取短信验证码
基本信息:
Path:  /app/v1_0/sms/codes/:mobile
Method : GET

接口描述
1)线上接口路径
http://ttapi.research.itcast.cn/app/v1_0/sms/codes/:mobile            #这里最后一个参数应该是一个手机号,验证码会发送到手机里,抄出来就行
2)接口访问次数受限
每手机号每分钟1次
3)返回HTTP状态码
1. 200   ok
2. 404  手机号不正确
3. 429   接口访问次数受限,body数据返回
  {
“message”:"Too many requests."
}
4. 507   服务器数据库异常

请求参数:
请求参数
Headers
参数名称        参数值         是否必须        示例    备注
Content-Type    application/json    是

路径参数
参数名称    示例        备注    
mobile    13480909876    手机号

返回数据
名称       类型    是否必须    默认值    备注    其他信息
data       object     否        数据
  --mobile    string     是        手机号
message      string 是    ok    提示信息ok


2、获取用户频道列表:
基本信息
Path:/app/v1_0/user/chanels
Method: GET

接口描述:
1)线上地址:
http://ttapi.research.cn/app/v1_0/user/channels
2)返回状态码
507   数据库错误
200  ok

3)不强制用户登录,匿名用户返回后台设置的默认频道列表

请求参数
Headers
参数名称        参数值        是否必须        示例        备注
Content-type     application/json    是
Authoization            是        Bearer
                        eyJ0eXA...........    用户Token,未登录用户为空

返回数据
名称        类型    是否必须    默认值     备注
message        string    是        提示信息
data        object    否
 --channels    object[]    是        频道列表
--id        interger    是        频道ID
--name        string    是        频道名称

3、收藏文章
基本信息:
Path: /app/v1_0/article/collections
Method: POST
接口描述:
1)线上地址:
http://ttapi.research.cn/app/v1_0/article/collections

2)返回状态码
400   请求参数错误
401   用户未认证
507   数据库错误
201  ok

3)在请求头Trace 中传递collect 埋点参数
请求参数
Headers
参数名称        参数值        是否必须        示例        备注
Content-type     application/json    是
Authoization            是        Bearer
                        eyJ0eXA...........    用户Token,未登录用户为空
Trace                是

Body
名称    类型    是否必须        默认值    备注    其他信息
target    integer    是            收藏的目标文章id

返回数据
名称        类型    是否必须    默认值     备注
message        string    是        提示信息
data        object    否
 --target        integer    是        频道列表
--id        interger    是        收藏的文章id
--name        string    是        频道名称


4、取消收藏文章
基本信息:
Path: /app/v1_0/article/collections/:target
Method: DELETE
接口描述:
1)线上地址:
http://ttapi.research.cn/app/v1_0/article/collections/:target             #冒号后面的是文章id

2)返回状态码
204   取消成功,注意这是本接口成功调用的返回状态码,body没有数据(没有默认的message)
401   用户未认证
404   访问路径错误
507   数据库错误
201  ok


请求参数
Headers
参数名称        参数值        是否必须        示例        备注
Content-type     application/x-
        www-from-urlencoded是
Authoization            是        Bearer
                        eyJ0eXA...........    用户Token,未登录用户为空


路径参数
参数名称    示例    备注    
target    

返回数据
名称        类型    是否必须    默认值     备注
message        string    否        提示信息


三、用例设计
3.1 单接口
模板:id、模块、接口名称、请求url、用例名称、请求方法、请求参数类型、请求参数、预期结果、备注
注意:单接口颗粒度放得比较小(以测试数据为颗粒度)

3.2 多接口
多接口颗粒度为每个接口为基础,每个接口内所设计的数据可以通过参数化来解决


实例:
ID    模块    用例名称    接口名称        请求url            请求方法    请求参数类型    请求参数                    预期结果        测试结果    备注
hmtt001    登录    登录成功    获取短信验证码    http://ttapi.research.itcast.cn    GET    url        手机号:                    1、响应状态码:200
                    /app/v1_0/sms/codes/:mobile                        13480251222    2、手机接收到验证码
                                 POST    1、Headers    1、Headers = {"Content-Type":"application/json"}1、响应状态码:201
            请求登录接口    http://ttapi.research.itcast.cn/    2、body        2、body={"mobile":"13480909876","code":"888888"}  2、登录成功,提示token信息
                    app/v1_0/authorizations
-------------------------------------------------------------------------------------------------------------------------------------------------    ------------------------------------------------------------------------------------------------
hmtt002    用户    获取用户    获取用户频道    http://ttapi.research.itcast.cn    GET                                1、响应状态码:200
        频道列表    列表        /app/v1_0/user/channels        Headers    Headers = {"Content-Type":"application/json",        2、响应数据:{"message":"xxx"}
                                    "Authorization":"Bearer token信息"}
            
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
hmtt003    文章    成功收藏文章 收藏文章    http://ttapi.research.itcast.cn                    1、响应状态码:200
                    /app/v1_0/article/collections    POST    1、Headers    1、Headers = {"Content-Type":"application/json",
                                                "Authorization":"Bearer token信息"}    1、响应状态码:201
                                    2、body        2、body={"target":"文章id"}              2、响应数据:{"message":"xxx"}
                    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
hmtt004    文章    成功取消收藏文章 取消收藏文章    http://ttapi.research.itcast.cn    DELETE    1、url                            1、响应状态码:204
                    /app/v1_0/article/collections/:target                            
                                     2、Headers    1、Headers = {"Content-Type":"application/x-www-form-urlencoded",
                                                    "Autorization":"Bearer token信息"}
                    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


四、项目目录结构搭建:
1、接口对象层 api
2、用例执行业务层 case
3、数据驱动 data
4、测试报告 report
5、工具层  tools 第三方工具包
6、运行入口层  运行测试用例,并生成报告   run_suite.py

4.1
login.py
"""
目标:实现登录接口对象封装
"""
#导包  requests
import requests
#新建类 登录接口对象
class ApiLogin(object):
#新建方法  登录方法
    def api_post_login(self,url,mobile,code):
        #headers 定义
        headers = {"Content-Type":"application/json"}
        #data  定义
        data = {"mobile":mobile,"code":code}
        #调用post并返回响应对象
        return requests.post(url,headers=headers,json=data);


4.2
case/test_login.py
"""
目标:完成登录业务层实现
"""
#导包
import unittest
from api.api_login import ApiLogin;
#新建测试类
class TestLogin(unittest.TestCase):
#新建测试方法
    def test_login(self):
         #暂时存放数据  url  mobile  code
        url="http://ttapi.research.itcast.cn/app/v1_0/authorizations";
        mobile = '13480。。。';
        code = "189598"    //这里需要先在浏览器输入http://ttapi.research.itcast.cn/app/v1_0/sms/codes/13480。。。来获取验证码,然后才开始执行
         #调用登录方法
        s = ApiLogin().api_post_login(url,mobile,code);
        print("响应结果:",s.json())
         #断言 响应信息  状态码
        self.assertEqual("OK",s.json()['message']);
        self.assertEqual(201,s.status_code);
if __name__ == '__main__':
    unittest.main();

4.3
解决数据存储问题:
1、步骤:
    编写数据存储文件 login.json
    编写读取json工具
    使用参数化动态获取参数数据

login.json
{
  "url": "http://ttapi.research.itcast.cn/app/v1_0/authorizations",
  "mobile": "13480。。。",
  "code": "",                                         ------这个先放空着,等手机收到验证码再填上去
  "expect_result": "OK",
  "status_code": 201
}

read_json.py
#导包
import json
#打开json文件并获取文件流

    # with open("../data/login.json",'r',encoding="utf-8") as f:
    #     #调用load方法加载文件流
    #     data = json.load(f);
    #     print("获取的数据为:",data);

"""
问题:未经过封闭无法在别的模块内使用
解决:进行封装
"""
# def read_json():
#     with open("../data/login.json", 'r', encoding="utf-8") as f:
    #     #调用load方法加载文件流
    #     return json.load(f);

"""
问题2:数据存储文件有好几个
解决:使用参数替换静态文件名
"""
class ReadJson(object):
    def __init__(self,filename):
        self.filepath = "../data/"+filename;
    def read_json(self):
        with open(self.filepath, 'r', encoding="utf-8") as f:
            #调用load方法加载文件流
            return json.load(f);

"""
问题3:预期格式为列表嵌套元组,目前返回字典
解决:读取字典内容,并添加到新的列表中
"""
if __name__ == "__main__":
    # print(ReadJson("login.json").read_json());
    data = ReadJson('login.json').read_json();
    #新建空列表,添加读取json数据
    arrs = [];
    arrs.append((data.get("url"),
                 data.get("mobile"),
                 data.get('code'),
                 data.get('expect_result'),
                 data.get('status_code')))
    print(arrs);


test_login.py
"""
目标:完成登录业务层实现
"""
#导包
import unittest
from api.api_login import ApiLogin;
from parameterized import parameterized;
from tools.read_json import ReadJson
#读取数据函数
def get_data():
    data = ReadJson('login.json').read_json();
    #新建空列表,添加读取json数据
    arrs = [];
    arrs.append((data.get("url"),
                 data.get("mobile"),
                 data.get('code'),
                 data.get('expect_result'),
                 data.get('status_code')))
    return arrs;
#新建测试类
class TestLogin(unittest.TestCase):
#新建测试方法
    @parameterized.expand(get_data())
    def test_login(self,url,mobile,code,expect_result,status_code):
         #暂时存放数据  url  mobile  code
        # url="http://ttapi.research.itcast.cn/app/v1_0/authorizations";
        # mobile = '13480。。。';
        # code = "189598"
         #调用登录方法
        s = ApiLogin().api_post_login(url,mobile,code);
        print("响应结果:",s.json())
         #断言 响应信息  状态码
        self.assertEqual(expect_result,s.json()['message']);
        self.assertEqual(status_code,s.status_code);
if __name__ == '__main__':
    unittest.main();

多个用例:
login_more.json
{
  "login_001": {
    "url": "http://ttapi.research.itcast.cn/app/v1_0/authorizations",
    "mobile": "13480。。。。",
    "code": "579120",
    "expect_result": "OK",
    "status_code": 201},
   "login_002": {
     "url": "http://ttapi.research.itcast.cn/app/v1_0/authorizations",
     "mobile": "1344000",
     "code": "081897",
     "expect_result": "err",
     "status_code": 201}
}

test_login_more.py
"""
目标:完成登录业务层实现
"""
#导包
import unittest
from api.api_login import ApiLogin;
from parameterized import parameterized;
from tools.read_json import ReadJson
#读取数据函数
def get_data():
    datas = ReadJson('login_more.json').read_json();
    #新建空列表,添加读取json数据
    arrs = [];
    #使用遍历获取所有的value
    for data in datas.values():
        arrs.append((data.get("url"),
                     data.get("mobile"),
                     data.get('code'),
                     data.get('expect_result'),
                     data.get('status_code')))
    return arrs;
#新建测试类
class TestLogin(unittest.TestCase):
#新建测试方法
    @parameterized.expand(get_data())
    def test_login(self,url,mobile,code,expect_result,status_code):
         #调用登录方法
        s = ApiLogin().api_post_login(url,mobile,code);
        print("响应结果:",s.json())
         #断言 响应信息  状态码
        self.assertEqual(expect_result,s.json()['message']);
        self.assertEqual(status_code,s.status_code);
if __name__ == '__main__':
    unittest.main();



其中第一个用例是参数是全对的,第二个用例的参数,手机号错误
所以运行结果:
响应结果: {'data': {'refresh_token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTQ3MTIwMTUsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOnRydWV9.gHBTtlRLAVd-cqg46yepaOmsJDcSubskn3N6Vh8Wc-U', 'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTM1MDk2MTUsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOmZhbHNlfQ.uWM4pDRT0WcuR-WcFQgmgwOrXyqDjt9f2D-zz3TN4rA'}, 'message': 'OK'}
响应结果: {'message': {'mobile': '1348025124000 is not a valid mobile'}}

Ran 2 tests in 17.841s

FAILED (failures=1)

{'mobile': '1348025124000 is not a valid mobile'} != err

Expected :err
Actual   :{'mobile': '1348025124000 is not a valid mobile'}



获取用户频道列表api封装
api_channel.py
"""
目标:实现获取用户频道列表接口对象封装
"""
#导包  requests
import requests
#新建类 对象类
class ApiChannels(object):
#新建方法  登录方法
    def api_get_channels(self,url,headers):
        #调用get请求
        return requests.get(url,headers=headers);


test_channel.py
#导包
import unittest
from api.api_channel import ApiChannels;
#新建测试类
class TestChannels(unittest.TestCase):
    #新建测试方法
    def test_channels(self):
        #临时数据
        url = "http://ttapi.research.itcast.cn/app/v1_0/user/channels"
        #提示:token之前有个空格和Bearer
        headers = {"Content-Type":"pplication/json","Authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTM1MDk2MTUsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOmZhbHNlfQ.uWM4pDRT0WcuR-WcFQgmgwOrXyqDjt9f2D-zz3TN4rA"}
        #调用获取用户频道列表方法
        r = ApiChannels().api_get_channels(url,headers)
        #调试
        print(r.json());
        #断言 状态码
        self.assertEqual(200,r.status_code);
        #断言 响应信息
        self.assertEqual("OK",r.json()['message'])

if __name__ == "__main__":
    unittest.main();


运行结果:
OK
{'message': 'OK', 'data': {'channels': [{'name': '推荐', 'id': 0}]}}


获取用户频道业务数据参数化
data/channel.json
{
  "url": "http://ttapi.research.itcast.cn/app/v1_0/user/channels",
  "headers": {"Content-Type":"application/json","Authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTM1NzI5NTgsInVzZXJfaWQiOjEyNzc3Njg4MjUxNjQ1Mjk2NjQsInJlZnJlc2giOmZhbHNlfQ.RpYg9aSuvp1SFzpb9d8AH-vVp-0JB3U5PMCed2a3Mjc"},
  "expect_result": "OK",
  "status_code": 200
}


test_channel.py
#导包
import unittest
from api.api_channel import ApiChannels;
from parameterized import parameterized;
from tools.read_json import ReadJson
def get_data():
    data = ReadJson('channel.json').read_json();
    # 新建空列表,添加读取json数据
    arrs = [];
    arrs.append((data.get("url"),
                 data.get("headers"),
                 data.get('expect_result'),
                 data.get('status_code')))
    return arrs;
#新建测试类
class TestChannels(unittest.TestCase):
    #新建测试方法
    @parameterized.expand(get_data())
    def test_channels(self,url,headers,expect_result,status_code):
        #调用获取用户频道列表方法
        r = ApiChannels().api_get_channels(url,headers)
        #调试
        print(r.json());
        #断言 状态码
        self.assertEqual(status_code, r.status_code);
        #断言 响应信息
        self.assertEqual(expect_result, r.json()['message'])
if __name__ == "__main__":
    unittest.main();

运行结果:
OK
{'data': {'channels': [{'id': 0, 'name': '推荐'}]}, 'message': 'OK'}



运行主入口实现
先在tools那里添加HTMLTestRunner.py文件,然后在run_suite.py里导入
"""
目标:
1、搜索组装测试套件
2、运行测试套件并生成测试报告
"""
#导包
import unittest
import time
from tools.HTMLTestRunner import HTMLTestRunner
#组装测试套件
suite = unittest.defaultTestLoader.discover("./case/",pattern="test*.py");
#指定报告存放路径及文件名称
file_path = "./report/{}.html".format(time.strftime("%Y_%m_%d %H_%M_%S"));
#运行测试套件并生成测试报告
with open(file_path,'wb') as f:
    HTMLTestRunner(stream=f).run(suite);




103°|1034 人阅读|0 条评论
登录 后发表评论