单元测试-pytest框架实践

文章目录

  • 1. 单元测试用例目录
  • 2. 自动化测试用例编写步骤
  • 3. 命名规则
  • 4. 环境安装
  • 5. pytest语法
    • 5.1 unittest与pytest对比
    • 5.2 pytest运行插件
    • 5.3 fixture
    • 5.4 装饰器
  • 6. pytest.ini
  • 7. conftest.py
  • 8. 用例编写步骤
    • 8.1 按照以下方式检查用例
  • 9. 单元测试示例
  • 10. 运行
  • 11. 覆盖率
  • 12. 单个文件覆盖率详情
  • 14. 测试报告
  • 15.Mock
    • 15.1 什么是Mock
    • 15.2 Mock的定义
    • 15.3 Mock的使用
    • 15.4 Mock与MagicMock
    • 15.5 Mock示例
  • 16. MagicMock
  • 17.Patch
    • 17.1 patch能做什么
    • 17.2 patch的目标
    • 17.3 patch定义
    • 17.3 patch的使用方式
      • 17.3.1 装饰器方式
      • 17.3.2 上下文管理器
      • 17.3.3 手动方式

1. 单元测试用例目录

在这里插入图片描述

  • testPytest
    • business
      • xxx.py # 业务代码
    • .coveragerc # 覆盖率配置文件
    • pytest.ini # pytest测试框架配置文件,运行参数
    • run_unittest.py # 运行入口
    • tests
      • assets # README.md图片目录
      • common # 公共方法
      • data # 单元测试用例数据目录
      • env-pytest3.6 # 测试工具环境
      • example # 示例
      • logs # 日志
      • business # 测试用例模块目录
        • test_xxx.py # 测试文件
      • conftest.py # 自定义hook程序
      • install_env.sh # python单元测试环境安装
      • README.md # 单元测试文档
      • requirements.txt # 单元测试工具安装

2. 自动化测试用例编写步骤

1. 初始化	-	用例执行前的动作
2. 执行	-	具体用例逻辑
3. 断言	-	校验用例执行结果
4. 清理	- 	用例执行后的动作	-	一般是把用例修改的内容恢复到执行前的状态

3. 命名规则

pytest的命名规则:
1. 模块名(py文件)必须是以 test_ 开头或者 _test 结尾;
2. 测试类(class)必须是以 Test 开头,并且不能带 __init__ 方法;
3. 测试用例 - 类方法(method)必须是以 test_ 开头;
4. 测试用例 - 函数(function)必须是以 test_ 开头;

4. 环境安装

pip3 install env-pytest3.6
# 或者
pip3 install -r requirements.txt
  • env-pytest3.6:管软kos环境适配whl包
allure_pytest-2.9.45-py3-none-any.whl
allure_python_commons-2.9.45-py3-none-any.whl
atomicwrites-1.4.0-py2.py3-none-any.whl
attrs-21.4.0-py2.py3-none-any.whl
colorama-0.4.4-py2.py3-none-any.whl
importlib_metadata-4.8.3-py3-none-any.whl
iniconfig-1.1.1-py2.py3-none-any.whl
packaging-21.3-py3-none-any.whl
pluggy-1.0.0-py2.py3-none-any.whl
py-1.11.0-py2.py3-none-any.whl
pyparsing-3.0.7-py3-none-any.whl
pytest-7.0.1-py3-none-any.whl
pytest_assume-2.4.3-py3-none-any.whl
pytest_html-3.1.1-py3-none-any.whl
pytest_metadata-1.11.0-py2.py3-none-any.whl
pytest_repeat-0.9.1-py2.py3-none-any.whl
pytest_rerunfailures-10.2-py3-none-any.whl
setuptools-59.6.0-py3-none-any.whl
six-1.16.0-py2.py3-none-any.whl
tomli-1.2.3-py3-none-any.whl
typing_extensions-4.1.1-py3-none-any.whl
zipp-3.6.0-py3-none-any.whl
  • requirements.txt:python3.6版本单元测试包和版本
allure_pytest==2.9.45
allure_python_commons==2.9.45
atomicwrites==1.4.0
attrs==21.4.0
colorama==0.4.4
importlib_metadata==4.8.3
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.0.7
pytest==7.0.1
pytest_assume==2.4.3
pytest_html==3.1.1
pytest_metadata==1.11.0
pytest_repeat==0.9.1
pytest_rerunfailures==10.2
setuptools==59.6.0
six==1.16.0
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

5. pytest语法

5.1 unittest与pytest对比

在这里插入图片描述

5.2 pytest运行插件

在这里插入图片描述

5.3 fixture

在这里插入图片描述

# 函数格式:
fixture(scope,autouse,params,ids,name):scope:   在什么层级下运行,它的值只有:session, package, module, class, function(默认值)autouse: 代表的是否自动执行,若此参数不加或者设置为False的话,代表不会自动运行,设置为True自动执行,无需调用。params:  进行的数据参数化,参数化的数据就是通过此参数传入到测试用例中。ids:     它是给生成的结果的fixture进行重命名,主要是为了好理解,前提是必须要有参数params。name:    对fixture的函数名起别名,或者叫重命名,没啥大用处。# 参数名:scopesession:如果等于此值,那么这个fixture在整个项目下只运行一次,这个特别适合于登录,登录在项目中只需要登录一次。package:如果设置为此值,那么这个fixture在这个包下只运行一次。module:如果设置为此值,那么这个fixture在这个文件中只运行一次,文件中可能既有函数又有类。class:如果设置为此值,那么这个fixture在这个类中只运行一次。function:它是这个函数的默认值,如果为此值,那么这个fixture在每个测试函数前运行一次。
# 参数名:autouseTrue: 如果等于此值,此fixture将被自动调用,而且都是在测试用例前执行。False:该值是此参数的默认值,如果等于此值,此fixture将不会自动调用,需要主动调用。
# 参数名:params它的值主要接受的是列表,而列表中的值存放的就是测试数据
# 参数名:ids它是给生成的结fixture进行重命名,因为它之前生成的模式是按照fixture_demo[index] 进行命名的,其中这个index就是每次循环的数字,第一次为0 。如果这个不太好理解,就可以使用ids进行重命名,但是前提必须要有参数化:params。
# 参数名:name它是给fixture函数重名名的,或者叫起别名,主要是为了调用时方便使用,一旦起别名,在测试用例中就必须的使用别名。

5.4 装饰器

在这里插入图片描述

6. pytest.ini

# pytest.ini是什么?
pytest.ini文件是pytest的主配置文件;
pytest.ini文件的位置一般放在项目的根目录下,不能随便放,也不能更改名字。
pytest.ini文件中都是存放的一些配置选项,这些选项都可以通过pytest -h查看到。# pytest.ini的编写格式
INI文件由节、键、值组成。
节   			[section]
选项/参数(键=值)	 name=value
注释				 注释使用分号表示(;)。在分号后面的文字,直到该行结尾都全部为注释。
# 例如:
[pytest]
;命令参数
addopts = -v 						; 输出更加详细的信息-s 						; 输出测试用例的打印信息,比如在用例中有print信息--reruns 3 				; 失败用例重跑,最多重试3次--reruns-delay 3 		; 重试前等待秒数--html=unittest_report.html 		; 生成HTML报告--self-contained-html 	; 合并CSS的HTML报告,分享报告样式不丢失--capture=sys			; 控制测试用例执行过程输出,sys表示捕获Python层级的sys.stdout和sys.stderr--cov=business --cov-branch --cov-config .coveragerc --cov-report=html:unittest_coverage_html --cov-report=xml:unittest_coverage.xml;指定测试用例跟目录,只有在pytest未指定文件目录或测试用例标识符时,该选项才有作用
;testpaths = ./tests;屏蔽不被检索单元测试用例的目录
;norecursedirs = .git;屏蔽告警
filterwarnings = ignore::DeprecationWarningasyncio_default_fixture_loop_scope = functionasyncio_mode = auto;解决测试用例参数话时ids中文乱码
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True;日志设置
log_cli = true
log_cli_level = INFO
log_cli_format = %(asctime)s - %(name)s - (%(levelname)s) - %(filename)s - %(lineno)d - %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S
log_format = %(asctime)s - %(name)s - (%(levelname)s) - %(filename)s - %(lineno)d - %(message)s
log_date_format = %Y-%m-%d %H:%M:%S;测试用例匹配规则
python_files = test_*.py
python_classes = Test*
python_functions = test_*;注册自定义标签
markers =nas: file storagedos: object storagesan: block storagemix: unstructured storagesmoke: smoke test

7. conftest.py

# conftest.py是什么?
conftest.py是fixture函数的一个集合,将一些前置动作放在fixture中,然后供其它测试用例调用。不同于普通被调用的模块,conftest.py使用时不需要导入,Pytest会首先查找这个文件。# conftest.py有什么作用?
调用系统之前你需要先做登录操作,获取token
执行测试用例前先需要搜集测试用例数据。
执行测试用例前先需要进行筛选数据等。# conftest.py使用规则
它的文件名是固定的,不可更改。
它可以放在项目的跟目录下,也可以放在每个测试用例的文件夹下。
每次执行时都是首先先运行此文件下的fixture函数,然后才去执行测试用例文件。# 示例参考:tests/conftest.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-import os
import pytest
import datetime
from py.xml import html
from pytest_metadata.plugin import metadata_key""" 
自定义hook程序
"""# 定义一个全局变量,用于存储内容
global_data = {}def pytest_configure(config):# 设置临时目录为 Unix 风格路径temp_dir = '/tmp/pytest_temp'os.environ['PYTEST_ADDOPTS'] = f'--basetemp={temp_dir}'@pytest.fixture(autouse=True, scope="session")
def fix_session():"""会话级fixtureautouse:如果为 True,则对于可以看到它的所有测试,夹具函数将被激活。如果为 False(默认值),需要显式引用才能激活夹具。:return:"""print("fixture会话级:fix_session")@pytest.fixture(autouse=True, scope="module")
def fix_module():"""模块级fixture:return:"""print("fixture模块级:fix_module")@pytest.fixture(autouse=True, scope="class")
def fix_class():"""类级fixture:return:"""print("fixture类级:fix_class")@pytest.fixture(autouse=True, scope="function")
def fix_func():"""方法级fixture:return:"""print("fixture方法级:fix_func")@pytest.fixture
def set_global_data():"""设置全局变量,用于关联参数:return:"""def _set_global_data(key, value):global_data[key] = valuereturn _set_global_data@pytest.fixture
def get_global_data():"""从全局变量global_data中取值:return:"""def _get_global_data(key):return global_data.get(key)return _get_global_datadef pytest_html_report_title(report):"""报告标题"""report.title = "自动单元测试报告"# 运行测试前修改环境信息
def pytest_configure(config):config.stash[metadata_key]["项目名称"] = "Gaia-V5 单元测试"config.stash[metadata_key]["测试模块"] = "XXX场景-用户-创建"config.stash[metadata_key]["测试环境"] = "集群环境:192.168.120.20"config.stash[metadata_key]["测试版本"] = "V7.1.10.3"config.stash[metadata_key]["测试日期"] = f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"def pytest_html_results_summary(prefix, summary, postfix):"""报告摘要,测试人员信息"""prefix.extend([html.p("所属部门: xxx研发部-研发五处")])prefix.extend([html.p("测试人员: 张三、李四")])def pytest_html_results_table_header(cells):"""处理结果表的表头"""# 往表格中增加一列Time,并且给Time增加排序cells.insert(1, html.th("执行时间", class_="sortable time", col="time"))# 往表格增加一列Description,并且给Description增加排序cells.insert(2, html.th("用例描述", class_="sortable description", col="description"))# 移除表格最后一列:Linkscells.pop(-1)def pytest_html_results_table_row(report, cells):"""处理结果表的每一行"""# 往列Time插入每行的值cells.insert(1, html.td(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), class_="col-time"))# 往列Description插入每行的值cells.insert(2, html.th(report.description, class_="sortable description", col="description"))# 移除表格最后一列:Linkscells.pop(-1)@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):outcome = yieldreport = outcome.get_result()report.description = str(item.function.__doc__)def pytest_html_results_table_html(report, data):"""清除执行成功的用例logs"""if report.passed:del data[:]data.append("这条用例测试通过!")@pytest.hookimpl(optionalhook=True)
def pytest_metadata(metadata):"""取到标题栏"""metadata.pop("Platform", None)metadata.pop("Packages", None)metadata.pop("JAVA_HOME", None)metadata.pop("Plugins", None)

8. 用例编写步骤

testPytest
-- tests-- common	# 公共方法模块-- data		# 测试用例数据模块-- env-pytest3.6	# 单元测试运行环境.whl列表,通过 pip3 install env-pytest3.6 进行安装-- example	# 示例模块-- logs		# 单元测试日志模块,记录单元测试用例运行详细日志-- sysmgt	# 业务代码对应的单元测试用例模块-- conftest.py	# 定义hook函数集合-- README.md	# 单元测试模块说明书-- requirements.txt	# 单元测试运行环境,通过 pip3 install -r requirements.txt 进行安装-- .coveragerc
-- pytest.ini 	# 单元测试用例运行时配置文件
-- run_unittest.py	# 单元测试用例运行总入口

8.1 按照以下方式检查用例

# 1.导入业务函数是否正确
from business.user_info import create_user# 2.创建模拟数据,每个用例运行时都会有各自的测试数据,以类为例
class TestCreateUser(object):"""xxx"""@classmethoddef setup_class(cls):# 模拟数据passdef setup_method(self):# 模拟数据pass# 3.编写单元测试用例,根据业务函数分支、创建单元测试用例,使单元测试用例覆盖业务函数的所有分支def test_create_user_id_success(self):""":return:创建用户成功:rtype:"""# 1.模拟数据logger.info(f"模拟数据:{self.request_info}")# 2.调用方法result = create_user(self.request_info)logger.info(f"测试方法结果:{result}")# 3.断言assert json.loads(result) == RESULT_JSON# 4.断言,检查断言是否正确assert xxx

9. 单元测试示例

# -*- coding: utf-8 -*-
"""
Author: xxx
date:   2025/3/3 10:53
Description:"""# 导入python包
import pytest
import random
# 从单元测试公共模块导入包
from tests.common.logging_utils import CustomLogger
# 导入待测试包
from business.funx import add# 执行成功返回json格式
RESULT_JSON = {"version": 0, "operation_type": 'op', "error_code": 0}
# 定义日志模块
logger = CustomLogger("business").loggerdef setup_module(module):"""测试模块级别的fixture,模块开始时执行一次:param module::type module::return::rtype:"""logger.info("<<<========测试模块执行前调用:setup_module========>>>")def teardown_module(module):"""测试模块级别的fixture,模块结束时执行一次:param module::type module::return::rtype:"""logger.info("<<<========测试模块执行后调用:teardown_module========>>>")def setup_function(function):"""测试函数级别的fixture,函数开始时执行一次:param function::type function::return::rtype:"""logger.info("<<<========测试函数执行前调用:setup_function========>>>")def teardown_function(function):"""测试函数级别的fixture,函数结束后时执行一次:param function::type function::return::rtype:"""logger.info("<<<========测试函数执行后调用:teardown_function========>>>")class TestFunx(object):"""业务代码,测试类"""@classmethoddef setup_class(cls):"""测试类级别的fixture,需要用@classmethod装饰器修饰:return::rtype:"""cls.a = random.randint(1, 100)cls.b = random.randint(200, 500)logger.info("<<<========测试类执行前调用:setup_class========>>>")@classmethoddef teardown_class(cls):"""测试类级别的fixture,需要用@classmethod装饰器修饰:return::rtype:"""logger.info("<<<========测试类执行后调用:teardown_class========>>>")def setup_method(self):"""测试方法级别的fixture:return::rtype:"""logger.info("<<<========测试方法执行前调用:setup_method========>>>")def teardown_method(self):"""测试方法级别的fixture:return::rtype:"""logger.info("<<<========测试方法执行后调用:teardown_method========>>>")def test_add_success(self):"""求两个数的和:return::rtype:"""# 1.模拟数据logger.info(f"模拟数据:{self.a}, {self.b}")# 2.调用方法result = add(self.a, self.b)# 3.断言assert result == self.a + self.bdef test_add_zero(self):"""求两个数的和:return::rtype:"""# 1.模拟数据self.a = 0self.b = 0logger.info(f"模拟数据:{self.a}, {self.b}")# 2.调用方法result = add(self.a, self.b)# 3.断言assert result == self.a + self.bdef test_add_negative(self):"""求两个数的和,负数:return::rtype:"""# 1.模拟数据self.a = -1self.b = -2logger.info(f"模拟数据:{self.a}, {self.b}")# 2.调用方法result = add(self.a, self.b)# 3.断言assert result == self.a + self.bif __name__ == '__main__':pytest.main()

10. 运行

# 运行
python3 run_unittest.py

11. 覆盖率

# 直接打开
unittest_coverage_html/index.html
# 1. coverage
覆盖率:指的是代码被测试的程度。覆盖率越高,意味着代码被测试的部分越多,代码的质量通常也越高。# 2. statements
语句:指的是代码中的可执行语句。覆盖率报告会统计这些语句中有多少被执行了。# 3. missing
缺失:指的是代码中没有被测试到的语句。这些语句在测试过程中没有被执行到。# 4. excluded
排除:指的是在覆盖率报告中被排除的语句。这些语句通常不会被测试,例如文档字符串、某些装饰器等。可以通过配置文件(如 .coveragerc)来指定哪些语句需要排除。# 5. branches
分支:指的是代码中的条件分支,例如 if 语句、for 循环等。覆盖率报告不仅会统计语句的覆盖率,还会统计分支的覆盖率,确保条件分支都被测试到。# 6. partial
部分:指的是部分分支被测试到的情况。例如,一个 if-else 语句中只有 if 分支被执行了,而 else 分支没有被执行,这种情况就被认为是部分覆盖率。

在这里插入图片描述

12. 单个文件覆盖率详情

# 绿色:表示代码已经被测试覆盖。
# 红色:表示代码没有被测试覆盖。
# 灰色:表示这些行被排除在覆盖率统计之外。
# 黄色:表示部分分支被测试到,但不是所有分支都被测试到。

在这里插入图片描述

14. 测试报告

# 直接打开
unittest_report.html

在这里插入图片描述

15.Mock

参考官网进行学习并使用:26.6. unittest.mock 上手指南 — Python 3.6.15 文档

最新文档:unittest.mock — 新手入门 — Python 3.13.2 文档

15.1 什么是Mock

# Mock就是要模拟函数、方法、类的行为# 使用场景:
# 1.模拟函数调用
# 2.记录在对象上的方法调用
模块功能
mockmock.Mock
mockmock.patch
mockmock.patch.object
mockmock.multiple
mockmock.dict
# Mock对象是模拟的基石,提供了丰富多彩的功能。从测试的阶段来分类包括:
模块功能函数描述
Mock构造器namemock对象的名称,是mock对象的标识
Mock构造器return_valuemock对象被调用时的返回值
Mock构造器side_effectmock对象被调用时的返回,可以是函数,类等,覆盖return_value
Mock构造器spec将对象设置成mock的属性
Mock构造器spce_set严格限制mock对象的属性访问,访问不存在的属性报错
Mock构造器wraps模拟对象是装饰器的使用
Mock构造器unsafe默认情况下,如果任何以assert或assret开头的属性都将引发错误,当unsafe=True时可以访问
Mock断言方法assert_not_called模拟从未被调用过
Mock断言方法assert_called至少调用了一次模拟
Mock断言方法assert_called_once仅调用了一次模拟
Mock断言方法assert_called_with使用指定的参数调用模拟
Mock断言方法assert_called_once_with模拟被调用了一次,并且该调用使用了指定的参数
Mock断言方法assert_any_call已使用指定的参数调用了模拟
Mock管理方法attach_mock将一个mock对象添加到另一个mock对象中
Mock管理方法configure_mock更改mock对象的return_value值
Mock管理方法mock_add_spec给mock对象添加新的属性
Mock管理方法reset_mock将mock对象回复到测试之前的状态
Mock统计方法called标识是否调用过
Mock统计方法call_count返回调用的次数
Mock统计方法call_args获取调用时的参数
Mock统计方法call_args_list获取调用的所有参数,结果是一个列表
Mock统计方法method_calls测试一个mock对象都调用了哪些方法,返回一个列表
Mock统计方法mock_calls测试对象对mock对象所有的调用,结果是一个列表

15.2 Mock的定义

# Mock的定义
class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)spec: 参数可以把一个对象设置为 Mock 对象的属性。访问mock对象上不存在的属性或方法时,将会抛出属性错误。
side_effect :调用mock时的返回值,可以是函数,异常类,可迭代对象。使用side_effect可以将模拟对象的返回值变成函数,异常类,可迭代对象等。
当设置了该方法时,如果该方法返回值是DEFAULT,那么返回return_value的值,如果不是,则返回该方法的值。 return_value 和 side_effect 同时存在,side_effect会返回。
如果 side_effect 是异常类或实例时,调用模拟程序时将引发异常。
如果 side_effect 是可迭代对象,则每次调用 mock 都将返回可迭代对象的下一个值。
return_value :调用mock的返回值,模拟某一个方法的返回值。
wraps: 装饰器:模拟对象要装饰的项目。
如果wrapps不是None,那么调用Mock将把调用传递给wrapped对象(返回实际结果)。
对mock的属性访问将返回一个mock对象,该对象装饰了包装对象的相应属性。
name :mock 的名称。 这个是用来命名一个mock对象,只是起到标识作用,当你print一个mock对象的时候,可以看到它的name。
spec_set:更加严格的要求,spec_set=True时,如果访问mock不存在属性或方法会报错

15.3 Mock的使用

使用mock.Mock()可以创建一个mock对象,对象中的方法有两种设置途径:作为Mock类的参数传入。mock.Mock(return_value=20,side_effect=mock_fun, name='mock_sum')实例化mock对象之后设置属性。mock_sum = mock.Mock()mock_sum.return_value = 20mock_sum.side_effect = mock_fun

15.4 Mock与MagicMock

# unittest.mock模块提供了Mock和MagicMock:
Mock主要模拟指定的方法和属性
MagicMock是Mock的子类,同时模拟了很多Magic方法(len, __str__等等)
如果执行代码逻辑中,某一个函数不想被真的调用的话,可以使用Mock方法

15.5 Mock示例

from unittest.mock import Mock# 创建一个Mock对象
mock = Mock()# 模拟find_person 函数,在执行测试时执行到find_person这个函数,会直接返回{"id" :1, "name":"jack"},而不是真正的去执行函数
mock.find_person.return_value = {"id" :1,"name":"jack"
}# 调用find person函数
print(mock.find_person())

16. MagicMock

# MagicMock是Mock的一个子类,具有大多数魔法方法的默认实现。在mock.patch中new参数如果没写,默认创建的是MagicMock。魔法方法:Python 中的类有一些特殊的方法。在python的类中,以两个下画线__开头和结尾的方法如__new__,__init__ 。
这些方法统称“魔术方法”(Magic Method)。任意自定义类都会拥有魔法方法。
使用魔术方法可以实现运算符重载,如对象之间使用 == 做比较时,其实是对象中 __eq__实现的。
魔法方法类似于对象默认提供的各种方法。__new__	   创建类并返回这个类的实例
__init__   可理解为“构造函数”,在对象初始化的时候调用,使用传入的参数初始化该实例
__del__	   可理解为“析构函数”,当一个对象进行垃圾回收时调用
__class__
__delattr__
__dict__
__dir__
__doc__
__eq__
__format__
__ge__
__getattribute__
__gt__
__hash__
__init_subclass__
__le__
__lt__
__module__
__ne__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
__weakref__Magic Mock 的默认值:
Magic Mock 实例化之后就会有一些初始值,是一些属性的实现。具体的默认值如下:
__lt__: NotImplemented
__gt__: NotImplemented
__le__: NotImplemented
__ge__: NotImplemented
__int__: 1
__contains__: False
__len__: 0
__iter__: iter([])
__exit__: False
__aexit__: False
__complex__: 1j
__float__: 1.0
__bool__: True
__index__: 1
__hash__: default hash for the mock
__str__: default str for the mock
__sizeof__: default sizeof for the mock使用MagicMock和Mock的场景:
使用MagicMock:需要魔法方法的场景,如迭代
使用Mock:不需要魔法方法的场景可以用Mock

17.Patch

17.1 patch能做什么

# patch可以临时用Mock对象替换一个目标(函数,方法,类)
Patch()充当函数修饰器、类修饰器或上下文管理器。在函数体或with语句中,使用patch中的new替换目标函数或方法。当function/with语句退出时,模拟程序被撤消。
功能函数描述
mock.patchtarget模拟的目标对象
mock.patchnew默认对target的替换,模拟返回的结果
mock.patchnew_callable模拟返回的结果哦,是可调用对象,会覆盖new
mock.patchspec为mock对象添加属性
mock.patchspec_set属性限制,访问mock没有的属性会报错
mock.patchautospec标记mock对象属性全部被spce替换
mock.patchcreate允许访问mock对象不存在的属性
# target:模拟对象的路径,参数必须是一个str,格式为 ’package.module.ClassName’,注意这里的格式一定要写对。如果对象和mock函数在同一个文件中,路径要加文件名# new:模拟返回的结果,是一个具体的值,也可是函数。new属性替换target,返回模拟的结果。# new_callable:模拟返回的结果,是一个可调用的对象,可以是函数。# spec:与Mock对象的参数类似,用于设置mock对象属性。# spec_set:与Mock对象的参数类似,严格限制mock对象的属性或方法的访问。# autospec:替换mock对象中所有的属性。可以替换对象所有属性,但不包括动态创建的属性。
autospec是一个更严格的规范。如果你设置了autospec=True,将会使用spec替换对象的属性来创建一个mock对象。mock对象的所有属性都会被spec相应的属性替换。
被mock的方法和函数会检查他们的属性,如果调用它们没有属性会抛出 TypeError。它们返回的实例也会是相同属性的类。# create:允许访问不存在属性
默认情况下,patch()将无法替换不存在的属性。如果你通过create=True,当替换的属性不存在时,patch会创建一个属性,并且当函数退出时会删除掉属性。这对于针对生产代码在运行时创建的属性编写测试非常有用。它在默认情况下是关闭的,因为它可能是危险的,打开它后,您可以针对实际不存在的api编写通过测试的代码同时mock.patch也是mock的一个子类,所以可以用return_value 和 side_effect 直接使用 return_value

17.2 patch的目标

# patch可以替换的目标:
1.目标必须是可以 import2.是在使用目标的地方替换而不是替换定义

17.3 patch定义

unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)

17.3 patch的使用方式

# 1.手动方式
# 2.装饰器方式
# 3.上下文管理器方式
-手动指定装饰器上下文管理器
优点可以更精细控制mock的范文方便mock多个对象
不足之处需要手动start和stop装饰器顺序和珊瑚参数相反容易混乱一个with只能mock一个对象
# patch的最佳实践:
patch有三种使用方法,最佳的使用实践是装饰器形态。手动指定方法需要start和stop控制,过于繁琐,虽然存在一个stopall的方法,但是仍然要逐个start
with写法的缺点很明显,一次不可以mock多个目标,多个with层层缩进很明显不可能。最佳实践:装饰器方法可以方便的mock多个对象,只需要熟悉装饰的顺序和函数参数的对应关系。
patch中可以return_value和new都可以改变结果,推荐patch中使用new属性,Mock中使用return_value.

17.3.1 装饰器方式

import unittest
from unittest.mock import Mock,patch
from myprj.service import student_serviceclass TestStudentService(unittest.TestCase):@patch("myprj.service.student_service.save_student") @patch("myprj.service.student_service.find_student_by_id") # 把find_student_by_id函数替换成Mock对象def test_change_name_detector(self, find_student_mock, save_student_mock):'''参数find_student_mock与save_student_mock上述@patch是逆序的方式'''# setupstudent = Mock(id=1, name = 'Jack')find_student_mock.return_value = student# Actionstudent_service.change_name(1, 'Tom')# Assertself.assertEqual('Tom', student.name)

17.3.2 上下文管理器

	def test_change_name_contextmanager(self):# setupstudent = Mock(id=1, name = 'Jack')find_student_mock.return_value = studentwith patch("myprj.service.student_service.find_student_by_id") as find_student_mock, \patch("myprj.service.student_service.save_student"):# Actionfind_student_mock.return_value = studentstudent_service.change_name(1, 'Tom')# Assertself.assertEqual('Tom', student.name)

17.3.3 手动方式

	@patch("myprj.service.student_service.find_student_by_id")def test_change_name_manual(self,find_student_mock):# setupstudent = Mock(id=1, name = 'Jack')find_student_mock.return_value = studentpatcher = patch("myprj.service.student_service.save_student")patcher.start()# Actionstudent_service.change_name(1, 'Tom')patcher.stop()# Assertself.assertEqual('Tom', student.name)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/28474.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式 ARM Linux 系统构成(1):Bootloader层

目录 一、Bootloader 概述 1.1 核心作用 1.2 典型启动流程 二、ARM Bootloader 架构详解 2.1 多阶段启动设计 2.2 关键代码流程 2.3. Bootloader的加载过程 2.4. Bootloader的加载方式 2.5. Bootloader 的移植 三、常见的Bootloader介绍 3.1. U-Boot 3.2. vivi …

Ubuntu20.04双系统安装及软件安装(九):谷歌浏览器

Ubuntu20.04双系统安装及软件安装&#xff08;九&#xff09;&#xff1a;谷歌浏览器 打开终端&#xff0c;下载谷歌浏览器软件包&#xff1a; wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb下载完成后直接在原终端执行&#xff1a; sudo…

【五.LangChain技术与应用】【10.LangChain ChatPromptTemplate(下):复杂场景下的应用】

凌晨两点的西二旗,你盯着监控大屏上跳动的错误日志,智能客服系统在流量洪峰中像纸船一样摇晃。用户骂声塞满弹窗:“等了十分钟就这?”“刚才说的怎么不认了?”“我要人工!!”——这时候你需要的不只是ChatPromptTemplate,而是给对话系统装上航天级操控台。 一、模板组…

Django项目实战

1、安装django 查看包安装的位置 pip镜像源 镜像源名称镜像地址​清华源​https://pypi.tuna.tsinghua.edu.cn/simple​阿里云​https://mirrors.aliyun.com/pypi/simple​腾讯云​https://mirrors.cloud.tencent.com/pypi/simple​华为云​https://repo.huaweicloud.co…

【YOLO V5】目标检测 WSL2 AutoDL VScode SSH

【YOLO V5】目标检测 WSL2 AutoDL VScode SSH 前言整体思路理解向YOLO 目标检测完整流程 环境配置Anaconda 获取 YOLO 代码与预训练模型下载 YOLOv5 代码和预训练模型配置 YOLOV5 工程环境解压 YOLOv5 源代码 并 添加预训练模型调整依赖版本选择对应的 Python 解释器 数据集准备…

PMP项目管理—沟通管理篇—1.规划沟通管理

文章目录 基本信息步骤4W1HITTO输入工具与技术输出 沟通需求分析沟通技术沟通模型沟通噪声障碍的类型 沟通漏斗模型 沟通方法相关方参与度评估矩阵传递方式影响沟通效果沟通管理计划 基本信息 步骤 收集信息&#xff0c;加工信息确定项目沟通需求确定项目沟通的方法编制项目沟…

SCI期刊推荐 | 免版面费 | 计算机领域:信息系统、软件工程、自动化和控制

在学术研究领域&#xff0c;选择合适的SCI期刊对科研成果的传播与认可至关重要。了解SCI期刊的研究领域和方向是基础&#xff0c;确保投稿内容与期刊主题相符。同时&#xff0c;要关注期刊的影响因子和评估标准&#xff0c;选择具有较高影响力和学术认可度的期刊。阅读期刊的投…

EasyDSS视频推拉流系统:清理缓存文件时如何确保缓存读写不受影响?

视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户咨询&#xff0c;视频推…

探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(二)

文章目录 2.3.3 极化编码巴氏参数与信道可靠性比特混合生成矩阵编码举例 2.3.4 极化译码最小单元译码串行抵消译码&#xff08;SC译码&#xff09;算法SCL译码算法 2.3.5 总结**Polar 码的优势****Polar 码的主要问题****Polar 码的应用前景** 2.3.6 **参考文档** 本博客为系列…

GCC RISCV 后端 -- cc1 入口

GCC编译工具链中的 gcc 可执行程序&#xff0c;实际上是个驱动程序&#xff08;Driver&#xff09;&#xff0c;其根据输入的参数&#xff0c;然后调用其它不同的程序&#xff0c;对输入文件进行处理&#xff0c;包括编译、链接等。可以通过以下命令查看&#xff1a; gcc -v h…

用DeepSeek-R1-Distill-data-110k蒸馏中文数据集 微调Qwen2.5-7B-Instruct!

下载模型与数据 模型下载&#xff1a; huggingface&#xff1a; Qwen/Qwen2.5-7B-Instruct HF MirrorWe’re on a journey to advance and democratize artificial intelligence through open source and open science.https://hf-mirror.com/Qwen/Qwen2.5-7B-Instruct 魔搭&a…

基于编译器特性浅析C++程序性能优化

最近在恶补计算机基础知识&#xff0c;学到CSAPP第五章的内容&#xff0c;在这里总结并且展开一下C程序性能优化相关的内容。 衡量程序性能的方式 一般而言&#xff0c;程序的性能可以用CPE&#xff08;Cycles Per Element&#xff09;来衡量&#xff0c;其指的是处理每个元素…

K8s控制器Deployment详解

回顾 ReplicaSet 控制器,该控制器是用来维护集群中运行的 Pod 数量的&#xff0c;但是往往在实际操作的时候&#xff0c;我们反而不会去直接使用 RS&#xff0c;而是会使用更上层的控制器&#xff0c;比如说 Deployment。 Deployment 一个非常重要的功能就是实现了 Pod 的滚动…

大模型巅峰对决:DeepSeek vs GPT-4/Claude/PaLM-2 全面对比与核心差异揭秘

文章目录 一、架构设计深度解剖1.1 核心架构对比图谱1.2 动态MoE架构实现架构差异分析表 二、训练策略全面对比2.1 训练数据工程对比2.2 分布式训练代码对比DeepSeek混合并行实现GPT-4 Megatron实现对比 2.3 关键训练参数对比 三、性能表现多维评测3.1 基准测试全景对比3.2 推理…

【MySQL_02】安装(8.4.4LTS : Windows + Linux)

文章目录 一、版本说明二、官网下载三、Windows安装3.1 安装和配置3.2 四、Linux安装 历史文章点击&#x1f449;&#xff1a;SQL &#x1f408;‍⬛github&#xff1a;https://github.com/mysql &#x1f4bb;官网&#xff1a; https://www.mysql.com &#x1f30f;维基百科…

今天来介绍和讨论 AGI(通用人工智能)

首先介绍&#xff0c;AGI&#xff08;通用人工智能&#xff09;是什么&#xff1f; AGI&#xff08;Artificial General Intelligence&#xff0c;通用人工智能&#xff09;指的是能够像人类一样理解、学习、推理和解决广泛任务的人工智能系统。与目前的AI不同&#xff0c;AGI可…

自动化学习-使用git进行版本管理

目录 一、为什么要学习git 二、git是什么 三、git如何使用 1、git的下载安装和配置 2、git常用的命令 3、gitee远程仓库的使用 &#xff08;1&#xff09;注册 &#xff08;2&#xff09;创建仓库 &#xff08;3&#xff09;配置公钥&#xff08;建立电脑和git…

探秘基带算法:从原理到5G时代的通信变革【十】基带算法应用与对比

文章目录 三、算法在现代通信系统中的应用3.1 5G 通信中的应用3.1.1 信道编码与调制解调3.1.2 大规模 MIMO 技术3.1.3 案例分析&#xff1a;5G 基站与终端实现 3.2 卫星通信中的应用3.2.1 抗干扰与纠错编码3.2.2 信号处理与调制解调3.2.3 案例分析&#xff1a;卫星通信系统实例…

从vue源码解析Vue.set()和this.$set()

前言 最近死磕了一段时间vue源码&#xff0c;想想觉得还是要输出点东西&#xff0c;我们先来从Vue提供的Vue.set()和this.$set()这两个api看看它内部是怎么实现的。 Vue.set()和this.$set()应用的场景 平时做项目的时候难免不会对 数组或者对象 进行这样的骚操作操作&#xff…

IO学习day3

一、思维导图 二、练习 1、使用文件IO读取图片 文件大小&#xff0c;文件偏移量&#xff0c;宽度&#xff0c;高度 2.向一个程序中输入文件名&#xff0c;判断指定目录下是否有这个文件&#xff0c;如果有这个文件, 将这个文件的属性信息输出。如果不存在输出不存在即可。 .…