• 【接口测试】代码篇Python+Requests+UnitTest


    本文大纲截图:

    一、接口测试概念【看到最后有...】

    1、概念介绍

    接口测试:是对系统或组件之间的接口进行测试,主要是校验数据的交换、传递和控制管理过程,以及相互逻辑依赖关系。

    自动化测试:是把以人为驱动的测试行为转化为机器执行的一种过程。

    接口自动化测试:是让程序或工具代替人工自动的完成对接口进行测试的一种过程。

    2、接口测试与UI测试对比

    1、接口测试先执行还是UI功能先执行,为什么?

    • 接口测试先执行

    • 原因:

    • 1)UI功能测试必须依赖前台和后台开发完成且联调完成。

    • 2)接口测试后端开发一个就可以测试一个。

    2、接口测试与UI功能测试哪个更安全更高效?为什么?

    • 接口测试

    • 原因:

    • 1)接口测试是对请求及响应进行验证,如果出现问题,修改的也是后端,从而后端更安全。

    • 2)接口测试用例和执行要比UI要容易的多。

    3、测试用例的设计

    接口测试用例:依赖、正向、必测(状态码描述)、逆向

    UI功能测试用例:

    • 布局:

      • 1)布局颜色图标等是否与原型图一致

      • 2)图片及文字准确无误

    • 功能:

      • 正向:规则、测试点提取方法进行覆盖规则(等价类、边界值、判定表、场景法、......)

      • 逆向:等价类、边界值、判定表、场景法......

    • 非功能:易用性、兼容性、可靠性(稳定性测试、压力测试)效率(性能测试)、安全性

    3、接口测试实现方式

    使用接口工具来实现:JMeter、Postman等

    通过编写代码来实现:Python + Request + MySQL

    接口测试工具的不足:

    • 1)测试数据不好控制:CSV、参数化、变量方式,测试数据都是对单个数据进行解析,无法直接读取json格式,而接口与接口之间的数据传递基本都是通过json格式;无法直接存储或读取json格式。

    • 2)不方便测试加密接口:开发把加密程序写成jar包导入JMeter里面,然后再用jar包工具去进行转换加密操作,过程比较繁琐。

    • 3)扩展能力不足:如果接口与接口之间依赖关系比较繁琐,在JMeter中间用判断组件循环组件控制也比较繁琐;繁琐的业务依赖关系。

    二、代码自动化基础

    1、Requests库介绍

    介绍: Requests库是用Python编写的,基于urllib,采用Apache2 Licensed开源协议的HTTP库;相比urllib库,Requests库更加方便,可以节约我们大量的工作,完全满足HTTP测试需求。

    安装: pip3 install requests

    验证: pip3 show requests 或者直接进行导包操作

    1.1 发送请求:

    常见的HTTP请求方式:GETPOSTPUTDELETEHEADOPTIONS。使用requests发送网络请求非常简单,只需要调用HTTP请求类型所对应的方法即可。

    GET方法:

    • 作用:获取资源(查询)

    • 请求不含参数:

      • 导包 import requests

      • 调用get方法 requests.get(url)

    • 示例:

    1. import requests
    2. response = requests.get("http://www.baidu.com")
    3. # 请求方法的返回值responseResponse对象,可以从这个对象中获取需要的响应信息
    4. # 获取请求URL
    5. response.url
    6. # 获取响应状态码
    7. response.status_code
    8. # 以文本形式显示响应内容
    9. response.text
    • 请求含参数:

      • 导包 import requests

      • 调用get方法 requests.get(url,params)

      • 带参数的url:

    1. http://www.baidu.com?id=1001
    2. http://www.baidu.com?id=1001,1002
    3. http://www.baidu.com?id=1001&kw=北京
    • 参数:params

    1. 方式1:params={"id":1001}
    2. 方式2:params={"id":"1001,1002"}
    3. 方式3:params={"id":1001,"kw":"北京"}
    • 示例:

    1. # 导包
    2. import requests
    3. 请求url
    4. url = "http://www.baidu.com"
    5. 请求参数
    6. params={"id":1001,"kw":"北京"}
    7. 发送带参请求
    8. = requests.get(url, params = params)
    9. 打印请求url
    10. print("请求url:", r.url)
    • 响应

      • 获取URL:r.url

      • 获取响应状态码:r.status_code

      • 获取响应信息:r.text

    POST方法:

    • 作用:新增资源

      • 导包 import requests

      • 调用post方法:requests.post(url,json,headers)

    • 参数:

      • url:新增接口url地址

      • json:新增请求报文

      • headers:请求信息头部

    • 响应:

      • 响应状态 r.status_code

      • 响应信息 r.json(),以json文本形式响应内容

    • 示例:response = requests.post(url, data=None, json=None, **kwargs)

      • url:请求的URL

      • data:可选,要发送到请求体中的字典、元组、字节或文件对象

      • json:可选,要发送到请求体中的JSON数据

      • **kwargs:其他参数,如 headerscookies 等

      • 返回:requests.Response

    • 代码示例:

    1. # 导包
    2. import requests
    3. # 请求url
    4. url = "http://www.baidu.com"
    5. # 请求头
    6. headers = {"Content-Type":"application/json"}
    7. # 请求json
    8. data = {
    9.     "data": [{
    10.         "dep_id": "T01",
    11.         "dep_name": "Test01学院",
    12.         "master_name": "Test-Master",
    13.         "slogan": "Here is Slogan"
    14.     }]
    15. }
    16. # 调用post方法
    17. response = requests.post(url, json=data, headers=headers)
    18. # 打印响应状态
    19. print("响应状态为:", response.status_code)
    20. # 打印以json形式打印响应信息(以json文本形式响应内容)
    21. print("响应信息为:", response.json())

    扩展1: data与json的区别

    • data:字典对象

    • json:json字符串

    • 在python中字典对象和json字符串长得一样,但后台格式是有区别的

    • 将字典对象转为json字符串

      • 导入json

      • json.dumps(字典对象)

    • 代码示例:

    1. 1.导包
    2. import requests
    3. import json
    4. 2.调用post
    5. # 请求url
    6. url = "http://127.0.0.1:8000/api/departments/"
    7. # 请求headers
    8. headers = {"Content-Type""application/json"}
    9. # 请求json
    10. data = {
    11.     "data": [{
    12.         "dep_id""T01",
    13.         "dep_name""Test01学院",
    14.         "master_name""Test-Master",
    15.         "slogan""Here is Slogan"
    16.     }]
    17. }
    18. # 使用json方式来新增学院 -->成功
    19. # r = requests.post(url, json=data, headers=headers)
    20. # 使用data方式来新增学院 -->失败
    21. # 注意:对于python字典和json虽然长得一样,但是数据序列化格式还是有一定区别
    22. # r = requests.post(url, data=data, headers=headers)
    23. # 将字典对象转为json字符串
    24. = requests.post(url, data=json.dumps(data), headers=headers)
    25. 3.获取响应对象
    26. print(r.json())
    27. 4.获取响应状态码
    28. print(r.status_code)

    扩展2: 响应数据json()和text区别

    • json():返回类型为字典,可以通过键名来获取响应的值

    • text:返回类型为字符串,无法通过键名来获取响应的值

    • 提示:共同点长得都像字典

    PUT方法:

    • 作用:更新资源

      • 导包 import requests

      • 调用put方法 requests.put(url,json,headers)

    • 参数:参考post方法参数

    • 响应:

      • r.json()

      • r.status_code()

    DELETE方法:

    • 作用:删除资源

      • 导包 import requests

      • 调用方法 requests.delete(url)

    • 响应:响应状态码 r.status_code204

    1.2 传递参数

    如果需要在URL的查询字符串中传递数据,可以使用Requests提供了params参数来定义,其传递的参数可以是字符串或字典。

    • 示例1:

    1. parameter = {
    2.             "key1":"value1",
    3.             "key2":"value2"
    4.             }
    5. response2 = requests.get("http://httpbin.org/get",params = parameter)
    6. print(response2.url)
    7. # http://httpbin.org/get?key1=value1&key2=value2
    • 示例2:将一个列表作为值传入

    1. parameter = {
    2.             "key1":"value1",
    3.             "key2":["value21","value22"]
    4. }
    5. response3 = requests.get("http://httpbin.org/get",params = parameter)
    6. print(response3.url)
    7. # http://httpbin.org/get?key1=value1&key2=value21&key2=value22
    • 示例3:字典里值为None的键都不会被添加到URL的查询字符串里

    1. parameter = {
    2.             "key1":"value",
    3.             "key2":None
    4. }
    5. response4 = requests.get("http://httpbin.org/get",params = parameter)
    6. print(response4.url)
    7. #http://httpbin.org/get?key1=value

    1.3 响应内容

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

    常用的响应对象方法: r = response

    • r.status_code:获取响应状态码

      • 查询为 200

      • 新增 201

      • 更新 200 或 201

      • 删除为 204

    • r.url:获取请求url地址

    • r.encoding:查看响应头部字符编码,查看默认请求编码格式以及设置响应编码格式

      • 获取响应编码:r.encoding

      • 设置响应编码:r.encoding = "utf-8"

    • r.headers:获取服务器响应信息头,headers信息比较重要;项目工作中一般服务器返回的token/session相关信息都在headers中

    • r.cookies:获取cookie信息和cookie值;获取响应cookie信息的返回类型为字典,cookie信息由服务器产生。

      • r.cookies['键名']

      • 如:r.cookies['BDUSS']

      • cookie信息还可以作为请求用:在请求方法后面跟上cookies的值

    • r.content:以字节码形式解析响应内容,如:读取 图片、视频、多媒体的形式等文件;Python中打开并读取文件,用的'rb'格式以字节码的形式读取文件

      • 如:将图片写入当前目录baidu.png

    1. with open("./baidu.png""wb"as f:
    2.     f.write(r.content)
    • r.text:以文本形式解析响应内容,如GET方法请求的响应内容

    • r.json():以json字符串形式解析响应内容

    代码示例1: r.encodingr.textr.headers

    1. """
    2.     目标:响应对象常用方法
    3.         1、encoding
    4.             1)获取请求编码
    5.             2)设置响应编码
    6.         2、headers
    7.             1)获取响应信息头信息
    8.     案例:http://www.baidu.com
    9. """
    10. # 1.导包
    11. import requests
    12. # 2.调用get方法
    13. # 请求url
    14. url = "http://www.baidu.com"
    15. r = requests.get(url)
    16. # 3.查看默认请求编码 ISO-8859-1
    17. print(r.encoding)
    18. # 3-1 设置响应编码
    19. r.encoding = "utf-8"
    20. # 4.查看响应内容 text形式
    21. print(r.text)
    22. # 5.查看响应信息头
    23. # 注意:headers信息比较重要(项目工作中一般服务器返回的token\session相关信息都在headers中)
    24. print(r.headers)

    代码示例2: r.cookiesr.content

    1. """
    2.     目标:响应对象常用方法
    3.         1、cookies
    4.             1)获取响应cookies信息
    5.         2、content
    6.             1)以字节码形式获取形影信息(图片、视频...多媒体格式)
    7.     案例:
    8.         cookies:http://www.baidu.com
    9.         content:http://www.baidu.com/img/bd_logo1.png?where=super
    10. """
    11. # 导包
    12. import requests
    13. # 调用get方法
    14. # 请求url
    15. url = "http://www.baidu.com"
    16. r = requests.get(url)
    17. # 获取响应cookies 返回字典对象
    18. print("cookies信息为:", r.cookies)
    19. # 通过键名获取响应的cookies值
    20. print("cookies信息为:", r.cookies['BDORZ'])
    21. url_img = "http://www.baidu.com/img/bd_logo1.png?where=super"
    22. r = requests.get(url_img)
    23. # 以text文本形式解析图片 -->乱码
    24. print(r.text)
    25. # 以字节码形式解析图片 -->字节流
    26. print(r.content)
    27. # 将图片写入当前目录 baidu.png
    28. with open("baidu.png""wb"as f:
    29.     f.write(r.content)

    1.4 Session

    在requests里,session对象是一个非常常用的对象,这个对象代表一次用户会话:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开。 会话能让我们在跨请求时保持某些参数,如在同一个session实例发出的所有请求之间保持cookie。

    什么是session对象: 一次会话(从客户端和服务器创建请求连接开始,到客户端和服务器断开连接结束)

    为什么使用session对象: session可以自动保持服务器产生的cookies信息,并且自动在下一条请求时附加。

    创建session对象: session = requests.Session()。得到session对象后,就可以调用该对象中的方法来发送请求。

    应用:

    1. # 导包
    2. import requests
    3. # 获取session对象
    4. session = requests.session()
    5. # 通过 session对象.请求方法
    6. # 说明:通过session对象.方法,无论通过session对象调用哪个方法,返回结果都是response对象
    7. session.get()
    8. session.post()
    9. session.put()
    10. session.delete()

    示例:

    1. # 导包
    2. import requests
    3. # 获取session对象
    4. session = requests.session()
    5. # 请求验证码,让session对象记录cookies信息
    6. url_verify = "http://192.168.176.128/index.php?m=Home&c=User&a=verify"
    7. session.get(url_verify)
    8. # 请求登录
    9. url_login = "http://192.168.176.128/index.php?m=Home&c=User&a=do_login"
    10. data = {"username""13800001111",
    11.         "password""123456",
    12.         "verify_code"8888}
    13. = session.post(url=url_login, data=data)
    14. # 查看登录是否成功
    15. print(r.json())
    16. # 查询我的订单
    17. url_order = "http://192.168.176.128/index.php/Home/Order/order_list.html"
    18. = session.get(url_order)
    19. print(r.text)

    1.5 Cookie

    获取Cookie:

    1. import requests
    2. response = requests.get("http://www.baidu.com")
    3. print(response.cookies)
    4. for key,value in response.cookies.items():
    5.     print(key+"="+value)

    使用 cookies 参数发送的cookies到服务器:

    1. import requests
    2. url = 'http://httpbin.org/cookies'
    3. cookies = dict(cookies_are='working')
    4. response = requests.get(url, cookies=cookies)
    5. print(response.text)

    2、UnitTest+Requests

    示例代码:

    1. # 导包
    2. import unittest
    3. import requests
    4. # 新建测试类,继承unittest.TestCase
    5. class TestLogin(unittesst.TestCase):
    6.     # setUp:以test开头的方法执行之前,首先会被执行
    7.     def setUp(self):
    8.         # 获取session对象
    9.         self.session = requests.session()
    10.         # 登录url
    11.         self.url_login = "http://192.168.176.128/index.php?m=Home&c=User&a=do_login"
    12.         # 验证码url
    13.         self.url_verify = "http://192.168.176.128/index.php?m=Home&c=User&a=verify"
    14.     # tearDown:以test开头的方法执行之后,会被执行
    15.     def tearDown(self):
    16.         # 关闭session
    17.         self.session.close()
    18.     # 测试用例方法1:登录成功
    19.     def test_login_success(self):
    20.         # 请求验证码 -->获取cookies
    21.         self.session.get(self.url_verify)
    22.         # 请求登录
    23.         data = {"username""13800001111",
    24.                 "password""123456",
    25.                 "verify_code"8888}
    26.         r = self.session.post(self.url_login, data=data)
    27.         try:
    28.             # 断言
    29.             self.assertEqual("登录成功", r.json()['msg'])
    30.         except AssertionError as e:
    31.             print(e)
    32.     # 测试用例方法2:登录失败,账号不存在
    33.     def test_username_not_exist(self):
    34.         # 请求验证码 -->获取cookies
    35.         self.session.get(self.url_verify)
    36.         # 请求登录
    37.         data = {"username""138000011116",
    38.                 "password""123456",
    39.                 "verify_code"8888}
    40.         r = self.session.post(self.url_login, data=data)
    41.         try:
    42.             # 断言
    43.             self.assertEqual("账号不存在!", r.json()['msg'])
    44.         except AssertionError as e:
    45.             print(e)
    46.     # 测试用例方法3:登录失败,密码错误
    47.     def test_password_error(self):
    48.         # 请求验证码 -->获取cookies
    49.         self.session.get(self.url_verify)
    50.         # 请求登录
    51.         data = {"username""13800001111",
    52.                 "password""123456789",
    53.                 "verify_code"8888}
    54.         r = self.session.post(self.url_login, data=data)
    55.         try:
    56.             # 断言
    57.             self.assertEqual("密码错误", r.json()['msg'])
    58.         except AssertionError as e:
    59.             print(e)
    60. if __name__ == '__main__':
    61.     unittest.main()

    3、Python + MySQL

    3.1 数据库概念

    数据库(Database)就是一个存放数据的仓库,这个仓库是按照一定的数据结构来组织、存储和管理数据的。分为关系型数据库(MySQL、Oracle、SQL Server、SQLite等)和非关系型数据库(Redis、MongoDB等)。

    3.2 为什么要连接数据库?

    • 1)查询结构返回的数据,无法确定是否正确,需要通过sql语句和条件来查询返回结果,和接口返回结果进行断言

    • 2)新增资源接口成功后,无法获取到新增id,需要在此id作为下条接口请求使用参数

    3.3 Python操作MySQL的方式

    MySQLdb: 最流行的一个驱动,很多框架也是基于此库进行开发的。但只支持Python2.x,基于C开发的库,和Windows平台的兼容性不友好,不推荐使用。

    MySQLclient: MySQLdb的Fork版本,完全兼容MySQLdb。支持Python3.xDjango ORM的依赖工具。想使用原生SQL来操作数据库,则推荐此驱动。

    SQLAlchemy: 既支持SQL,又支持ORM的工具,非常接近Java中的Hibemate框架。

    PyMysql: 纯Python实现的驱动,速度比不上MySQLdb,安装比较简单,兼容MySQLdb

    3.4 使用PyMySQL操作MySQL

    安装PyMySQL

    • 下载:https://github.com/PyMySQL/PyMySQL

    • 安装最新版PyMySQL命令:pip install PyMySQL

    • 验证:pip show pymysql,出现相关版本信息

    操作数据库流程:流程:

    • 创建连接 connection

    • 获取游标对象 cursor

      • 执行select语句 cursor.execute()-关闭 cursor-关闭 connection

      • 执行i/u/d语句 cursor.execute()-出现异常-回滚事务 conn rollback()-关闭 cursor-关闭 connection;未出现异常-提交事务 conn commit()-关闭 cursor-关闭 connection

    步骤:

    • 导包 import pymysql

    • 创建数据库连接对象

    • 获取游标对象

    • 执行数据库语句方法

    • 关闭游标对象

    • 关闭连接对象

    • 连接数据库

    调用pymysql.connect()方法创建数据库连接:

    基本格式: conn = pymysql.connect(host=None, user=None, password="", database=None, port=0, charset="", autocommit=False)

    • host:数据库服务器地址

    • user:登录用户名

    • password:密码

    • database:要连接的数据库名称

    • port:数据库连接端口(默认值:3306)

    • charset:字符编码格式(中文编码:utf8)

    • autocommit:是否开启自动提交事务(默认值:False)

    • 注意:对数据库新增、更新、删除操作都需要开启自动提交事务,即:autocommit=True

    代码示例: conn = pymysql.connect(host="127.0.0.1", user="root", password="root", database="books", port=3306, charset="utf8", autocommit=False)

    连接数据库相关方法:

    • 获取数据库版本sql语句:select version()

    • 连接对象:conn = pymysql.connect(参数)

    • 获取游标:cursor = conn.sursor()

    • 执行sql语句方法:cursor.execute(sql)

    • 获取单条结果:cursor.fetcheone()

    • 关闭游标:cursor.close()

    • 关闭连接:conn.close()

    数据库常用查询方法:

    • fetchone():获取下一个查询结果集,结果集是一个对象

    • fetchmany():获取指定条数的返回结果行

    • fetchall():获取全部的返回结果行

    • rowcount:获取execute()方法执行后影响的行数

    • 注意:使用fetcheone()/fetchemany()/fetcheall()时,结果集都是从游标对象中获取,如果先从游标对象中取走一条数据,那么接下来在获取时就没有这条数据了。

    案例:

    • 1、连接到数据库(host:localhost, username:root, password:root,database:books

    • 2、获取数据库服务器版本信息

    • 示例代码:

    1. # 导包 pymysql
    2. import pymysql
    3. # 获取数据库连接对象
    4. conn = pymysql.connect(host="127.0.0.1",
    5.                        user="root",
    6.                        password="root",
    7.                        database="books",
    8.                        port=3306,
    9.                        charset="utf8",
    10.                        autocommit=False)
    11. # print(conn)
    12. # 获取游标对象
    13. cursor = conn.cursor()
    14. # print(cursor)
    15. # 调用执行方法/获取数据方法
    16. sql = "select version()"
    17. num = cursor.execute(sql)
    18. print("执行返回结果为:", num)
    19. # 获取执行结果
    20. result_one = cursor.fetchone()
    21. print("获取的执行结果为:", result_one)
    22. # 关闭游标对象
    23. cursor.close()
    24. # 关闭数据库连接对象
    25. conn.close()

    3.5 使用事务提交与回滚操作

    事务介绍:

    事务(Transaction):是并发控制的基本单位,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的个工作单位。例如:银行转账,从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。

    数据库事务:是指一个逻辑工作单元中执行的一系列操作(单个或多个操作),要么完全地执行,要么完全地不执行。注意MySQL中只有InnoDB引擎才支持事务。

    特征:

    • 原子性(Atomicity):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。

    • 一致性(Consistency):在数据库中看到的数据,要么是执行事务之前的状态,要么是执行事务之后的状态

    • 隔离性(Isolation):又称孤立性,事务的中间状态对其他事务是不可见的(通过数据库锁来解决)

    • 持久性(Durability):指一个事务一旦提交成功,它对数据库中数据的改变就应该是永久性的。

    提交机制:

    • 自动提交:

      • 通过连接参数形式:autocommit=True

      • 通过方法形式:conn.autocommit(True),True 开启自动提交

    • 手动提交:

      • 提交数据库事务:conn.commit()

      • 回滚事务:conn.rollback()

    事务提交与回滚操作代码示例:

    1. # 导包
    2. import pymysql
    3. # 创建连接对象
    4. conn = pymysql.connect(host="127.0.0.1",
    5.                 user="root",
    6.                 password="root",
    7.                 database="books",
    8.                 port=3306,
    9.                 charset="utf8")
    10. # # 自动提交事务(通过方法形式) True为开启自动提交
    11. # conn.autocommit(True)
    12. # 创建游标对象
    13. cursor = conn.cursor()
    14. try:
    15.     # 调用执行方法
    16.     # 新增图书
    17.     sql_book = "insert into t_book(title,pub_date) values('东游记','1986-1-1')"
    18.     cursor.execute(sql_book)
    19.     # 新增英雄人物
    20.     sql_hero = "insert into t_hero(name,gender,book_i) values('孙悟空',1,4)"
    21.     cursor.execute(sql_hero)
    22.     # 手动提交事务
    23.     conn.commit()
    24. except Exception as e:
    25.     # 打印异常信息
    26.     print(e)
    27.     # 回滚事务操作
    28.     conn.rollback()
    29. finally:
    30.     # 关闭游标对象
    31.     cursor.close()
    32.     # 关闭连接对象
    33.     conn.close()

    4、Mock测试简介

    Mock介绍: Mock是模拟的意思,模拟为实现或比较复杂的对象,直接返回想要的结果就可以。在软测领域,对于某些不容易构造或者不容易获取的对象,可以通过某些技术手段虚拟出一个测试对象,返回预先设计的结果。也就是说对于任意被测试的对象,可以根据具体测试场景的需要,返回特定的结果。

    Mock测试: 在测试过程中,对于某些不容易构造或者不容易获取的对象,可以用一个虚拟的对象来代替的测试方法。

    Mock作用:

    • 1)处理外部依赖,用来解除测试对象对外部服务的依赖(如:数据库,第三方接口等),使得测试用例可以独立运行。

    • 2)替换外部服务调用或一些速度较慢的操作,提升测试用例的运行速度。

    • 3)解决测试中模拟异常类的问题,模拟异常逻辑:一些异常的逻辑往往在正常测试中是很难触发的,通过Mock可以人为的控制触发异常逻辑。

    • 4)模拟未实现接口的测试,在测试中,经常碰到以下场景—A接口调用B接口执行,而B接口还未实现,处理这种场景的过程就要使用Mock测试。

    Mock安装和导入:

    • Python 3.3以前版本:pip install mock

    • Python 3.3 及以后版本:from unittest import mock

    接口Mock的实现方式:

    • 白盒:手动构造Mock对象,如:可以自己写某个接口方法的实现,根据需要编写返回值

    • 黑盒:Mock方案和程序使用的语言无关,如:搭建一个Mock服务器

    Python Mock:

    • Mock是Python中一个用于支持的测试的库,其主要功能是使用Mock对象代替掉指定的Python对象,以达到模拟对象的行为。

    • Mock的安装和导入:

      • Python 3.3之前的版本,需要另外安装mock模块:pip install mock

      • Python 3.3 及之后的版本,Mock模块已被合并到标准库中,命名为unittest.mock,可以直接import进来使用:from unittest import mock

    • Mock的基本用法:Mock对象是Mock模块中最重要的概念,就是通过unittest.mock.Mock类创建的实例,这个类的实例可以用来替换其他的Python对象,来达到模拟的效果。

    • Mock类的定义:class Mock(name=None, return_value=DEFAULT, side_effect=None, wraps=None, spec=None, spec_set=None, unsafe=False)

    • Mock对象的基本步骤:

      • 1)找到要替换的对象(可以是一个类、类的实例、函数)

      • 2)实例化Mock类得到一个Mock对象,并且设置Mock对象的行为(如被调用的时候返回什么值,被访问成员的时候返回什么值)

      • 3)使用这个Mock对象替换掉要替换的对象

      • 4)编写测试代码

    • 代码示例:

    1. # 导包(unittest、mock)
    2. import unittest
    3. from unittest import mock
    4. # 未实现的函数
    5. def add(x, y):
    6.     pass
    7. # 新建测试类 继承
    8. class TestAdd(unittest.TestCase):
    9.     # 新建测试方法
    10.     def test_add(self):
    11.         # 获取mock对象 并设置行为 替换未完成或未实现的对象
    12.         # return_value 设置行为(返回字符串、数值、对象)
    13.         # add = mock.Mock(return_value=18)  # FAILED (failures=1)
    14.         add = mock.Mock(return_value=30)  # OK
    15.         # 调用未实现的对象
    16.         result = add(1020)
    17.         # 断言
    18.         self.assertEqual(result, 30)
    19. if __name__ == '__main__':
    20.     unittest.main()

    三、代码自动化实践

    1、框架组成

    框架结构图:

    • 被测系统+数据库+API(Requests)+TestCase(UnitTest)+测试数据+测试报告

    • 构成说明:

      • 1)API用于封装被测系统的所有接口

      • 2)TestCase将一个或多个接口封装成测试用例,并使用UnitTest管理测试用例

      • 3)TestCase可以调用数据库进行数据的校验,用于断言

      • 4)为了方便维护测试数据,可以把测试脚本和测试数据分离开

      • 5)通过UnitTest断言接口返回的数据,并生成测试报告

    框架目录结构:

    • apiAutoTest(项目文件及文件夹名称):

      • api:定义封装被测系统的接口

      • case:定义测试用例

      • data:存放测试数据

      • report:存放生成的测试报告

      • tools:存放第三方的文件

      • app.py:定义项目的配置信息

      • run_suite.py:执行测试套件的入口

      • 说明:接口自动化测试框架命名为 apiAutoTest

    2、项目案例

    案例: 基于加密接口的测试用例设计

    环境准备:

    • 1)对响应加密的接口发起一个get请求,得到一个加密过后的响应信息(若有可以用的加密过的接口及了解加密接口解密方法的可跳过)

    • 2)准备一个demo.json文件,并对其进行加密。Linux命令行中:base64 demo.json > demo.txt

    • 3)使用python命令在加密文件所在目录启动一个服务:python http.server 9999

    • 4)访问该网站:http://127.0.0.1:9999/demo.txt

    解密原理:

    • 1)对响应的解密处理:如果知道使用的哪个通用加密算法,则可自行解决

    • 2)如果不了解对应的加密算法,则可让研发提供加解密的lib进行处理

    • 3)如果既不是通用加密算法,研发也无法提供加解密的lib的话,可让加密方提供远程解析服务(这样算法仍是保密的)

    调用python自带的base64解密:

    1. # 导包
    2. import requests
    3. import base64
    4. import json
    5. # 解密函数
    6. def test_encode():
    7.     url = "http://127.0.0.1:9999/demo.txt"
    8.     r = requests.get(url)
    9.     res = json.loads(base64.b64decode(r.content))
    10.     print(res)

    封装对不同算法的处理方法:

    1. # 导包
    2. import requests
    3. import base64
    4. import json
    5. # 解密方法封装
    6. class ApiRequest:
    7.     def send(selfdata: dict):
    8.         res = requests.request(data['method'], data['url'], headers=data['headers'])
    9.         if data['encoding'== 'base64':
    10.             return json.loads(base64.b64decode(res.content))
    11.         # 把加密过后的响应值发给第三方服务,让第三方解密后返回解密过的信息
    12.         elif data['encoding'== 'private':
    13.             return requests.post('url'data=res.content)

    测试解密方法的测试用例:

    1. import unittest
    2. from day05.api_request_01 import ApiRequest
    3. class TestApiRequest(unittest.TestCase):
    4.     req_data = {
    5.         "method""get",
    6.         "url""http://127.0.0.1:9999/demo.txt",
    7.         "headers": None,
    8.         "encoding""base64"
    9.     }
    10.     def test_send(selfdata: dict):
    11.         # 实例化解密对象
    12.         ar = ApiRequest()
    13.         print(ar.send(self.req_data))

    3、项目实例

    项目自动化测试框架apiAutoTest: api、case、data、report、tools、run_suite.py

    api:

    • 包含文件:api_login.py

    • 代码示例:

    1. # 创建登录接口类
    2. class ApiLogin(object):
    3.     # 初始化函数
    4.     def __init__(self):
    5.         # 验证码url
    6.         self.url_verify = "http://..."
    7.         # 登录url
    8.         self.url_login = "http://..."
    9.     # 请求验证码
    10.     def api_get_verify(self, session):
    11.         # 请求验证码
    12.         session.get(self.url_verify)
    13.     # 请求登录
    14.     def api_post_login(self, username, password, verify_code):
    15.         data = {"username": username,
    16.                     "password": password,
    17.                     "verify_code": verify_code}
    18.         # 执行登录接口 并 返回响应对象
    19.         return session.post(self.url_login, data = data)

    case:

    • 包含文件:test_login.py

    • 代码示例:

    1. # 导包
    2. import unittest
    3. import requests
    4. import ApiLogin
    5. # 新建测试类,并集成unittest.TestCase
    6. class TestLogin(unittest.TestCase):
    7.     # 实例化ApiLogin对象
    8.     login = ApiLogin()
    9.     # setUp
    10.     def setUp(self):
    11.     # 获取session对象
    12.         self.session = requests.session()
    13.     # tearDown
    14.     def tearDown(self) -> None:
    15.         # 关闭session对象
    16.         self.session.close()
    17.     # 测试方法 登录成功
    18.     def test_login_success(self):
    19.         # 调用 获取验证码(让session对象记录cookies信息)
    20.         self.login.api_get_verify(self.session)
    21.         # 调用 登录函数
    22.         r = self.login.api_post_login(self.session, "13800001111""123456"8888)
    23.         try:
    24.             # 断言 验证码
    25.             self.assertEqual(200, r.status_code)
    26.             # 断言 消息
    27.             self.assertEqual("登录成功", r.json()['msg'])
    28.         except AssertionError as e:
    29.             print(e)
    30. if __name__ == '__main__':
    31.     unittest.main()

    data: 存放数据驱动的数据文件,如.json格式的文件

    report: 生成测试报告存放路径

    tools: 插件HTMLTestRunner.py

    运行文件: run_suite.py

    • 代码示例:

    1. # 导包
    2. import time
    3. import unittest
    4. import apiAutoTest.tools.HTMLTestRunner import HTMLTestRunner
    5. # 组装测试套件
    6. suite = unittest.defaultTestLoader.discover("./case", pattern="test*.py")
    7. # 定义测试报告存放路径及文件名称
    8. report_path = "./report/{}.html".format(time.strftime("%Y_%m_%d %H_%M_%S"))
    9. # 运行测试套件并生成报告
    10. with open(report_path, "wb"as f:
    11.     # HTMLTestRunner(stream=f).run(suite)
    12.     # 非必填参数
    13.     HTMLTestRunner(stream=f,
    14.                    title="tpshop登录接口自动化用例",
    15.                    description="操作系统:win").run(suite)

     最后:【可能给予你助力自动化测试的教程】

    最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走

    面试资料

    我们学习软件测试必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

    上面是我整理的配套资源,这些资源对于软件测试的的朋友来说应该是最全面最完整的备战仓库,为了更好地整理每个模块,我也参考了很多网上的优质博文和项目,力求不漏掉每一个知识点,很多朋友靠着这些内容进行复习,拿到了BATJ等大厂的offer,这个仓库也已经帮助了很多的软件测试的学习者,希望也能帮助到你。

  • 相关阅读:
    SpringBoot 刷新上下文7--执行BeanFactoryPostProcessor
    SaaS 架构基础理论(一)
    【SimpleFunction系列二.2】SpringBoot注解整合Redisson分布式锁
    利用pymupdf编辑修改pdf
    查询操作及乐观锁
    A Framework to Evaluate Fusion Methods for Multimodal Emotion Recognition
    gdb调试
    JDBC —— 数据库连接
    Spring 条件注解没生效?咋回事
    Makefile
  • 原文地址:https://blog.csdn.net/IT_LanTian/article/details/127577301