11.1 Requests 框架

11.1.1 requests 请求

1. reqeusts 库 安装

pip install requests

2. requests 库 GET 方法,参数通过 params 传入

import requests

# get 请求 无参数
get_response = requests.get("http://127.0.0.1:30060/login")
print(get_response.text) # get 请求带参数,方式一,添加在 url 中
get_response1 = requests.get("http://127.0.0.1:30060/loginAction?username=admin&password=123456") # get 请求带参数,方式二,放在 params 中
data_userLogin = {
"username": "admin",
"password": "23456"
}
get_response2 = requests.get("http://127.0.0.1:30060/loginAction", params=data_userLogin)

3. requests 库 POST 方法,参数通过 data 传入

import requests

data_addUser = {
"customer_name": "admin",
"customer_phone": "13312345678",
"customer_type": "C"
} get_response = requests.post("http://127.0.0.1:30060/addCustomer", data=data_addUser)
print(get_response.json())

4. requests 库的 request 方法

import requests

def requests_request():
# get 请求,必须大写
print(requests.request("GET", "http://127.0.0.1:30060/login").status_code)
# post 请求,必须大写
data_addUser = {
"customer_name": "admin",
"customer_phone": "13312345678",
"customer_type": "C"
}
print(requests.request("POST", "http://127.0.0.1:30060/addCustomer", data=data_addUser).json())

11.1.2 requests 响应

  • status
  • encoding
  • headers
  • raw:原始内容
  • text:字符串形式 -> html
  • content:二进制形式 -> pic/video
  • json:json 形式 -> json

11.1.4 Requests 处理 session

"""
该例子定义了三个请求,第一个请求与服务器建立连接 -> 第二个请求基于第一个请求,并在响应中提取 session 的 id 值提取出来
-> 作为第三个请求的参数
"""
import requests
import re
from requests import Session # 创建 session 会话对象,用该对象发送后续请求,表示所有请求在同一个会话中完成
session = Session()
session.get("http://localhost:1080/cgi-bin/welcome.pl?signOff=true") # 前后接口请求存在依赖,先发送服务器会产生session 值并在响应中取出来
get_first_response = session.get("http://localhost:1080/cgi-bin/nav.pl?in=home")
get_text = get_first_response.text # 提取 session 值,并使用字符串处理方式
get_index = get_text.find('userSession" value="') + len('userSession" value="')
get_session = get_text[get_index:get_text.find('"', get_index + 1)]
get_session1 = re.findall('userSession" value="(.+?)"', get_text) # 分析: userSession 的值是基于上一个请求服务器响应的,当前请求必须携带 session 值,否则服务器需要重新建立连接,会拒绝当前请求
data = {
'userSession': '%s' %get_session,
'username': 'admin',
'password': 'bean',
'login.x': '49',
'login.y': '15',
'JSFormSubmit': 'off'
}
get_response = session.post("http://localhost:1080/cgi-bin/login.pl", data=data)
print(get_response.text)

11.2 Mock 测试

1. Mock 目的:接口未开发完毕,影响接口测试进度。此时,可通过 mock 进行模拟数据;以及接口存在很多依赖的话,也可以解决依赖(实现解耦操作)。

2. Mock 技术主要分两类:

  • 1)Mock 服务:实现 Mock 功能的服务。由于项目中很多应用第三方服务,联调和测试比较麻烦,常见的解决方案是:搭建和部署一个临时服务,来模拟第三方服务进行联调和测试
  • 2)Mock 数据:mock 一个对象,通过它进行想要的测试,常见的有:EasyMock,Mockito,WireMock等,主要用于单元测试。

3. Mock 的基本原则是:

  • 被测试函数里面所对应的方法,如果没实现,就需要 mock
  • 无论对象有多少层,必须保证 mock 的对象是断言所调用的对象(同一对象)

------ Cal.py (该函数方法未实现)------
class Cal(object):
def add(self, a, b):
pass def minus(self, a, b):
pass
------- Mobile.py (该功能已实现) -----
from CH11_Requests.Mock_Tech.SourceDir.Cal import Cal class Mobile(object):
def __init__(self):
self.cal = Cal() def get_add_int(self, a, b):
return int(self.cal.add(a, b)) def get_minus_int(self, a, b):
return int(self.cal.minus(a, b)) # 不同于 cal 对象,此处传入 add 对象
def get_add_int_1(self, add, a, b):
return int(add(a, b))

2.1 一层 mock

---------- CalTest.py ---------
# @Description : mock 一层对象
import unittest
from unittest import mock
from CH11_Requests.Mock_Tech.SourceDir.Cal import Cal class CalTest(unittest.TestCase):
def setUp(self) -> None:
self.cal = Cal() # 假设 cal.minus() 方法输入参数 4,6, 输出 0
def test_minus(self):
get_mock = mock.Mock(return_value=0)
# 此时 self.cal.minus 表示的是对象,如果是 self.cal.minus() 则表示的是 方法
self.cal.minus = get_mock
self.assertEqual(self.cal.minus(4, 6), 0) if __name__ == '__main__':
unittest.main()

2.2 mock 剖析及 二层 mock

------- MobileTest.py (需要测试 Mobile.py 的函数功能)----------
# @Description : 1. mock 实现; 2. 设计原则:源代码和测试代码分开,即可以分开成 SourceDir 和 TestDir
import unittest
from unittest import mock
from CH11_Requests.Mock_Tech.SourceDir.Mobile import Mobile
from CH11_Requests.Mock_Tech.SourceDir.Cal import Cal class MobileTest(unittest.TestCase):
def setUp(self) -> None:
self.mobile = Mobile()
self.cal = Cal() def test_get_add_int_case1(self):
# 创建 mock 对象;模拟 Cal 中 add() 返回值
mock_add = mock.Mock(return_value=6.0)
# 模拟 Mobile get_add_int 对象。(如果模拟 get_add_int 方法,是假设 get_add_int 方法还未实现)
self.mobile.get_add_int = mock_add
self.assertEqual(6, self.mobile.get_add_int(1.2, 5.2)) def test_get_add_int_case2(self):
# 创建 mock 对象;模拟 Cal 中 add() 返回值
mock_add = mock.Mock(return_value=6.0)
# 将 mock 对象赋值给 python 对象。模拟 add 对象,但是没有测试 mobile 的方法 get_add_nit
self.cal.add = mock_add
self.assertEqual(6.0, self.cal.add(2.0, 3.0)) def test_get_add_int_realWant(self):
# 创建 mock 对象;模拟 Cal 中 add() 返回值
mock_add = mock.Mock(return_value=6.0)
# => 实际上 mock 的应该是 mobile cal 的 add 对象。这里指的是 SourceDir 里的 cal.add 对象
self.mobile.cal.add = mock_add
# => 并且测试 mobile 的方法 get_add_int
self.assertEqual(6, self.mobile.get_add_int(2.0, 3.0)) def test_get_add_int_1(self):
# 针对 Mobile 的 get_add_int_1 的形式,使用如下的 mock
# 将 add 以对象形式传入到 get_add_int_1 的方法中。此时,有2种实现:1)mock cal.add 或 2)mock mobile.cal.add
mock_add = mock.Mock(return_value=6.0)
# 2.1) 这个 cal.add 是本测试类的 cal.add 对。该 mock 一层对象(cal.add)
self.cal.add = mock_add
self.assertEqual(6, self.mobile.get_add_int_1(self.cal.add, 2.0, 3.0)) # 2.2) 这个 cal.add 是 SourceDir 的 cal.add 对象。这个是 mock 对象(mobile)里面的对象(cal.add),即 二层对象
self.mobile.cal.add = mock_add
self.assertEqual(6, self.mobile.get_add_int_1(self.mobile.cal.add, 2.0, 3.0)) if __name__ == '__main__':
unittest.main()

2.3 接口 mock

------ Login_Interface.py (该接口服务目前无法启动)--------
import requests def login_interface(data):
get_response = requests.get("http://127.0.0.1:30060/loginAction", params=data)
return get_response.json()
-------- LoginTest.py (需要测试接口功能)---------
# @Description : mock 接口对象
import unittest
import requests
from unittest import mock
from CH11_Requests.Mock_Tech.SourceDir.Login_InterFace import login_interface class LoginTest(unittest.TestCase):
def setUp(self) -> None:
pass def test_login_case1(self):
# mock 接口对象,但是 login_interface 没有调用 Login_Interface 的 login_interface 方法
data = {"username": "admin",
"password": "123456"
}
expect = {"errorcode": "0",
"message": "登录成功"
}
get_mock = mock.Mock(return_value=expect)
# login_interface 是一个对象,所以该处可以 mock 成功
login_interface = get_mock
print(login_interface(data))
# {'errorcode': '0', 'message': '登录成功'} def test_login_case2(self):
# 该例子中, self.get_response 是一个变量,实际运行中没有 mock 成功
data = {"username": "admin",
"password": "123456"
}
self.get_response = requests.get("http://127.0.0.1:30060/loginAction", params=data).json()
get_mock = mock.Mock(return_value=data)
# ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。此时 mock 对象是赋值给一个变量值,不是对象
self.get_response = get_mock
print(self.get_response) def test_login_case3(self):
# mock 传个 对象
data = {"username": "admin",
"password": "123456"
}
# 该例子中, get_response 实际上没有使用到
get_response = lambda: requests.get("http://127.0.0.1:30060/loginAction", params=data).json()
get_mock = mock.Mock(return_value=data)
get_response = get_mock
print(get_response())
# {'username': 'admin', 'password': '123456'} def test_login_real(self):
# mock 传个 对象。区别于 case3, 这里的 get_response 加上了 "self." 作为当期对象
data = {"username": "admin",
"password": "123456"
}
# lambda 生成对象,所以该列子中可以 mock 成功。区别于 case3,加上 self. 之后,self.get_response 对象有实际被使用
self.get_response = lambda: requests.get("http://127.0.0.1:30060/loginAction", params=data).json()
get_mock = mock.Mock(return_value=data)
self.get_response = get_mock
print(self.get_response())
# {'username': 'admin', 'password': '123456'} if __name__ == '__main__':
unittest.main()

11.2.4 重构封装 Mock 服务

1. side_effect 参数:当实际代码已经实现时,需要修改测试代码,让产生的返回值将 return_value 进行覆盖。

------- Cal.py --------
class Cal(object):
def add(self, a, b):
pass # 未实现的方法
def minus(self, a, b):
pass # 已经实现了的方法
def minus1(self, a, b):
return a - b + 2
-------- CalTest.py ---------
import unittest
from unittest import mock
from CH11_Requests.Mock_Tech.SourceDir.Cal import Cal class CalTest(unittest.TestCase):
def setUp(self) -> None:
self.cal = Cal() # 假设 cal.minus() 方法输入参数 4,6, 输出 0
def test_minus(self):
get_mock = mock.Mock(return_value=0)
# 此时 self.cal.minus 表示的是对象,如果是 self.cal.minus() 则表示的是 方法
self.cal.minus = get_mock
self.assertEqual(self.cal.minus(4, 6), 0) # 测试已经实现了的 minus1 方法
# side_effect 参数,将 实际产生的值覆盖 return_value
def test_minus1(self):
get_mock = mock.Mock(return_value=0, side_effect=self.cal.minus1)
self.cal.minus1 = get_mock
self.assertEqual(self.cal.minus1(4, 13), 0)
# 此时输入的参数,得到的结果就断言失败
self.assertEqual(self.cal.minus1(4, 13), -7)
# 该结果正确 if __name__ == '__main__':
unittest.main()

2. 一个用例涉及多个对象

----------- Mobile.py ------------
from CH11_Requests.Mock_Tech.SourceDir.Cal import Cal class Mobile(object):
def __init__(self):
self.cal = Cal() def get_add_int(self, a, b):
return int(self.cal.add(a, b)) def get_minus_int(self, a, b):
return int(self.cal.minus(a, b)) # 不同于 cal 对象,此处传入 add 对象
def get_add_int_1(self, add, a, b):
return int(add(a, b)) def get_add_minus_int(self, a, b):
return int(self.cal.add(a, b) - self.cal.minus(a, b))
--------- MobileTest2.py ---------
import unittest
from unittest import mock
from CH11_Requests.Mock_Tech.SourceDir.Mobile import Mobile
from CH11_Requests.Mock_Tech.SourceDir.Cal import Cal class MobileTest(unittest.TestCase):
def setUp(self) -> None:
self.mobile = Mobile()
self.cal = Cal() def test_get_add_minus_int(self):
# 实现一个测试用例涉及多个对象
mock_add = mock.Mock(return_value=10)
mock_minus = mock.Mock(return_value=0)
self.mobile.cal.add = mock_add # 绝对不能 self.cal.add = mock_add
self.mobile.cal.minus = mock_minus
self.assertEqual(self.mobile.get_add_minus_int(4, 6), 10) if __name__ == '__main__':
unittest.main()

3. mock + parameterized

----- MobileTest3.py --------
# -*- coding: utf-8 -*-
# @Time : 2023/1/8 15:02
# @Author : Bruce He
# @File : MobileTest3.py
# @Project Name: Lesson_FullStack_TD
# @Description : 测试 mock + parameterized
import unittest
from unittest import mock
from parameterized import parameterized
from CH11_Requests.Mock_Tech.SourceDir.Mobile import Mobile
from CH11_Requests.Mock_Tech.SourceDir.Cal import Cal class MobileTest(unittest.TestCase):
def setUp(self) -> None:
self.mobile = Mobile()
self.cal = Cal() @parameterized.expand([(16, 4, 9, 7, 12), (10, 0, 4, 6, 10)])
def test_parameterized_get_add_minus_int(self, mock1, mock2, input1, input2, expect):
# 实现一个测试用例涉及多个对象
mock_add = mock.Mock(return_value=mock1)
mock_minus = mock.Mock(return_value=mock2)
self.mobile.cal.add = mock_add # 此处的 self.mobile.cal.add 是 mock 二层对象
self.mobile.cal.minus = mock_minus
self.assertEqual(self.mobile.get_add_minus_int(input1, input2), expect) if __name__ == '__main__':
unittest.main()

4. mock 对象的主要参数

  • return_value:mock 的返回值
  • side_effect:当实际程序代码已实现,不需要调用 mock 对象时,则会调用实际的程序结果覆盖 return_value 的值

5. mock 对象的常用属性:called, call_count, call_args, call_args_list

6. mock 对象的方法(用于断言): assert_called_once(), assert_called_wirh()

读后笔记 -- Python 全栈测试开发 Chapter11:Python + Requests 实现接口测试的更多相关文章

  1. 大数据全栈式开发语言 – Python

    前段时间,ThoughtWorks在深圳举办一次社区活动上,有一个演讲主题叫做“Fullstack JavaScript”,是关于用JavaScript进行前端.服务器端,甚至数据库(MongoDB) ...

  2. 【Python全栈-后端开发】嵩天老师-Django

    嵩天老师-Python云端系统开发入门教程(Django) 视频地址:https://www.bilibili.com/video/av19801429 课前知识储备: 一.课程介绍: 分久必合.合久 ...

  3. 【Python全栈-后端开发】Django入门基础

    Django基础知识 一. 什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的 ...

  4. 【Python全栈-后端开发】Django进阶2-Form表单

    Django进阶2-Form表单 Django的Form主要具有一下几大功能: 生成HTML标签(可以保留上次输入内容) 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页 ...

  5. 【Python全栈-后端开发】数据库进阶

    数据库进阶 python关于mysql的API---pymysql模块 pymsql是Python中操作MySQL的模块,其使用方法和py2的MySQLdb几乎相同. 模块安装 pip install ...

  6. 【Python全栈-后端开发】Django进阶之Model操作复习

    Django进阶之Model操作复习 一.字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - ...

  7. 【Python全栈-后端开发】Django进阶1-分页

    Django[进阶篇-1 ]分页 分页 一.Django内置分页 from django.core.paginator import Paginator, EmptyPage, PageNotAnIn ...

  8. 【Python全栈-后端开发】Django入门基础-2

    Django入门基础知识-2 一 .模版 一.模版的组成 HTML代码+逻辑控制代码 二.逻辑控制代码的组成 1  变量(使用双大括号来引用变量) {{var_name}} 2  标签(tag)的使用 ...

  9. 【Python全栈-后端开发】MySQL数据库-练习题

    MySQL数据库-练习题 一.表关系 请创建如下表,并创建相关约束 二.操作表 1.自行创建测试数据 2.查询“生物”课程比“物理”课程成绩高的所有学生的学号: 3.查询平均成绩大于60分的同学的学号 ...

  10. Python全栈之路----目录

    Module1 Python基本语法 Python全栈之路----编程基本情况介绍 Python全栈之路----常用数据类型--集合 Module2 数据类型.字符编码.文件操作 Python全栈之路 ...

随机推荐

  1. JZOJ 1495. 宝石

    题目大意 用边长为 \(k\) 的正方形在平面内覆盖,求它能覆盖的最大点权和 思路 \(60\) 分:其实很容易想到按它们的横坐标先后排序,然后单调队列维护.复杂度 \(O(n k \log k)\) ...

  2. KMP字符串 AcWing 831

    题目:https://www.acwing.com/problem/content/833/ 题意:求子串在母串中每次出现时的下标位置. 题解:哈哈哈,敲题时想到之前看到一个人叫 kmp 算法为 看毛 ...

  3. group by 、concat_ws()、 group_caoncat()的使用

    group系列 之前觉得这里简单不需要再进行总结了.后来发现还是需要总结巩固一下,还是有一些方法之类的之前未使用过.这里来重新整理,记录一下. group by 将表中的数据根据某个条件进行分组. 比 ...

  4. 代码随想录算法训练营day18 | leetcode 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树

    LeetCode 513.找树左下角的值 分析1.0 二叉树的 最底层 最左边 节点的值,层序遍历获取最后一层首个节点值,记录每一层的首个节点,当没有下一层时,返回这个节点 class Solutio ...

  5. refactorObjProps:裁剪、添加对象字段或更新字段内容

    介绍 根据模板,自动对一个 JS 对象的字段进行裁剪.添加或更新字段类型. 比如,做一个设置功能,其设置的数据(对象)存储在 localStorage 中.如果对象的字段名称更新了.或增加了一个新的字 ...

  6. .NET AsyncLocal 避坑指南

    目录 AsyncLocal 用法简介 AsyncLocal 实现原理 AsyncLocal 的坑 AsyncLocal 的避坑指南 HttpContextAccessor 的实现原理 AsyncLoc ...

  7. openfoam 智能指针探索

    前言 今天看到一个程序,用到了智能指针, virtual tmp<volScalarField> rho() const; 借此机会把有关智能指针的知识体系重新梳理一遍 智能指针autoP ...

  8. LeetCode-798 得分最高的最小论调 及差分和前缀和的学习

    来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/smallest-rotation-with-highest-score 题目描述 给你一个数组  ...

  9. GoLang中signal.Notify函数用法

    官方描述: Notify函数让signal包将输入信号转发到c.如果没有列出要传递的信号,会将所有输入信号传递到c:否则只传递列出的输入信号. signal包不会为了向c发送信息而阻塞(就是说如果发送 ...

  10. REST风格开发

    使用测试工具 测试工具中 总结 @RequestBody:接收请求体参数Json @RequestParam:接收路径参数,包括表单 @PathVariable: 接收路径变量的. Rest风格优化 ...