当前位置: 首页 > news >正文

python单元测试

文章目录

  • 单元测试
    • 定义
    • 断言函数
    • Test Fixtures
    • Mock
    • patch
      • 装饰器模拟(首选)
      • 上下文管理器模拟
      • 手动模拟
    • 测试实例
  • 测试覆盖率
  • pytest框架
    • 起步
    • 安装使用
    • 常用参数
    • 跳过测试
    • @pytest.fixture
    • conftest.py
    • 参数化测试
  • 数据库查询的mock
    • 覆盖率

单元测试

定义

单元测试是指一个自动化的测试:

  • 用来验证一小段代码的正确性
  • 可以快速执行
  • 在独立的环境中执行

断言函数

assertEqual
assertNotEqual
assertTrue
assertFalse
assertIs
assertIsNot
assertIsNone
assertIsNotNone
assertIn
assertNotIn
assertIsInstance
assertNotIsInstance
assertRaises

示例一:assertEqual

class Calculator:def add(self, *args):ret = 0for item in args:ret += itemreturn ret
from unittest import TestCasefrom server.app import Calculatorclass TestCalculator(TestCase):def test_add(self):calculator = Calculator()expect_result = 10actual_result = calculator.add(1, 2, 3, 4)self.assertEqual(expect_result, actual_result)

示例二:assertRaises

class Service:def download_img(self, url: str):if url:return Trueraise ValueError("url error")
from unittest import TestCase
from server.app import Serviceclass TestService(TestCase):def test_download_img_success(self):service = Service()ret = service.download_img("http://www.baidu.com/1.png")self.assertTrue(ret)def test_download_img_with_exception(self):service = Service()with self.assertRaises(ValueError):service.download_img("")

Test Fixtures

在测试方法执行之前或者之后执行的函数或者方法被称为Test Fixtures

  • module级别的Fixtures:setUpModule,tearDownModule
  • class级别的Fixtures:setUpClass,tearDownClass
  • method级别的Fixtures:setUp,tearDown
class Service:def download_img(self, url: str):if url:return Trueraise ValueError("url error")
from unittest import TestCase
from server.app import Servicedef setUpModule():print("执行module前...")def tearDownModule():print("执行module后...")class TestService(TestCase):@classmethoddef setUpClass(cls):print("执行class前...")@classmethoddef tearDownClass(cls):print("执行class后...")def setUp(self):self.service = Service()print("执行任意测试方法前...")def tearDown(self):print("执行任意测试方法后...")def test_download_img_success(self):ret = self.service.download_img("http://www.baidu.com/1.png")self.assertTrue(ret)def test_download_img_with_exception(self):with self.assertRaises(ValueError):self.service.download_img("")
执行module前...
执行class...
执行任意测试方法前...
执行任意测试方法后...
执行任意测试方法前...
执行任意测试方法后...
执行class...
执行module后...

Mock

模拟函数,方法,类的行为。

  • Mock:主要模拟指定的方法和属性

  • MagicMock:Mock的子类,同时模拟了很多Magic方法(__len____str__方法等)

示例一:

from unittest.mock import Mockdef test_hello():hello = Mock()hello.find_user.return_value = {'name': '旺财','age': 1}print(hello.find_user())if __name__ == '__main__':test_hello()
{'name': '旺财', 'age': 1}

示例二:

class Student:def __init__(self, id: int, name: str):self.id = idself.name = namedef find_name_by_id(id):passdef save_student(student):passdef chang_name(id: int, new_name: str):student = find_name_by_id(id)if student:student.name = new_namesave_student(student)
from unittest.mock import Mock
from unittest import TestCase
from server.app import chang_name
from server import appclass TestService(TestCase):def test_change_name_with_record(self):service.find_name_by_id = Mock()student = Mock(id=1, name='旧名字')service.find_name_by_id.return_value = studentservice.save_student = Mock()chang_name(1, '新名字')self.assertEqual('新名字', student.name)service.find_name_by_id.assert_called()service.save_student.assert_called()def test_change_name_without_record(self):service.find_name_by_id = Mock()service.find_name_by_id.return_value = Noneservice.save_student = Mock()chang_name(1, '新名字')# 断言没有被调用service.save_student.assert_not_called()

patch

path可以临时用Mock对象替换一个目标(函数,方法,类)。本质还是上一节的Mock操作。

path可以替换的目标:

  • 目标必须是可import的
  • 是在使用的目标的地方替换而不是替换定义

path的使用方式:

  • 装饰器的方式
  • 上下文管理器的方式
  • 手动方式

装饰器模拟(首选)

class Student:def __init__(self, id: int, name: str):self.id = idself.name = namedef find_name_by_id(id):passdef save_student(student):passdef chang_name(id: int, new_name: str):student = find_name_by_id(id)if student:student.name = new_namesave_student(student)
from unittest.mock import Mock, patch
from unittest import TestCase
from server.app import chang_nameclass TestService(TestCase):@patch("server.server.save_student")@patch("server.server.find_name_by_id")def test_change_name_decorator(self, find_name_by_id_mock, save_student_mock):student = Mock(id=1, name='旧名字')find_name_by_id_mock.return_value = studentchang_name(1, '新名字')self.assertEqual('新名字', student.name)find_name_by_id_mock.assert_called()save_student_mock.assert_called()

上下文管理器模拟

from unittest.mock import Mock, patch
from unittest import TestCase
from server.app import chang_nameclass TestService(TestCase):def test_change_name_context(self):student = Mock(id=1, name='旧名字')with patch("server.server.find_name_by_id") as find_name_by_id_mock, patch("server.server.save_student"):find_name_by_id_mock.return_value = studentchang_name(1, '新名字')self.assertEqual('新名字', student.name)

手动模拟

from unittest.mock import Mock, patch
from unittest import TestCase
from server.app import chang_nameclass TestService(TestCase):@patch("server.server.find_name_by_id")def test_change_name_manual(self, find_name_by_id_mock):student = Mock(id=1, name='旧名字')find_name_by_id_mock.return_value = studentpather = patch("server.server.save_student")pather.start()chang_name(1, '新名字')pather.start()self.assertEqual('新名字', student.name)

测试实例

path里面的模拟对象已经对所有魔术方法都进行了mock,如果不关心返回值可以不用后续return_value了

import os.path
from urllib.request import urlopen, Requestdef download_img(url: str):site_url = Request(url, headers={"User-Agent": "Mozilla/5.0"})with urlopen(site_url) as web_file:img_data = web_file.read()if not img_data:raise Exception(f"Error: cannot load the image from {url}")file_name = os.path.basename(url)with open(file_name, 'wb') as file:file.write(img_data)return f"Download image successfully, {file_name}"
from unittest.mock import patch, MagicMock
from unittest import TestCase
from server.app import download_img# https://www.bilibili.com/video/BV1EK411B7LX/?spm_id_from=333.788&vd_source=35b478ef20f153fb3c729ee792cdf651
class TestService(TestCase):# urlopen在方法参数中被mock为urlopen_mock# urlopen_mock的返回值是一个urlopen_result_mock# urlopen_result_mock的__enter__方法返回值是一个web_file_mock# web_file_mock的read方法返回值需要定义@patch("server.server.urlopen")# 因为在service.service文件中引入了,所以可以直接使用service.service引入Request@patch("server.server.Request.__new__")def test_download_img_with_exception(self, request_init_mock, urlopen_mock):# Setupurl = 'https://www.google.com/a.png'urlopen_result_mock = MagicMock()web_file_mock = MagicMock()urlopen_mock.return_value = urlopen_result_mockurlopen_result_mock.__enter__.return_value = web_file_mockweb_file_mock.read.return_value = Nonewith self.assertRaises(Exception):download_img(url)@patch("builtins.open")@patch("os.path.basename")@patch("server.server.urlopen")@patch("server.server.Request.__new__")def test_download_img_with_success(self, request_init_mock, urlopen_mock, basename_mock, open_mock):# Setupurl = 'https://www.google.com/a.png'urlopen_result_mock = MagicMock()web_file_mock = MagicMock()urlopen_mock.return_value = urlopen_result_mockurlopen_result_mock.__enter__.return_value = web_file_mockweb_file_mock.read.return_value = 'not none'basename_mock.return_value = 'fff'ret = download_img(url)self.assertEqual("Download image successfully, fff", ret)

测试覆盖率

#统计测试覆盖率
python -m coverage run -m unittest#查看覆盖率报告
python -m coverage report#生成html格式的覆盖率报告
python -m coverage html

pytest框架

起步

pytest是一个基于python语言的第三方测试框架。

有以下优点:

  • 语法简单
  • 自动检测测试代码
  • 跳过指定测试
  • 开源

安装使用

#安装
pip install pytest#运行(自动查找test_*.py,*_test.py测试文件。自动查找测试文件中test_开头的函数和Test开头的类中的test_开头的方法)
pytest
pytest -v#测试指定测试类
pytest test_xxx.py

常用参数

-v 输出详细的执行信息,比如文件和用例名称
-s 输出调试信息,比如print的打印信息
-x 遇到错误用例立即停止

跳过测试

@pytest.mark.skip
@pytest.mark.skipif
import sysfrom server.app import Student
import pytestdef skip():return sys.platform.casefold() == 'win32'.casefold()# @pytest.mark.skip(reason="暂时跳过")
@pytest.mark.skipif(condition=skip(), reason="window平台跳过")
class TestStudent:def test_student_create(self):student = Student(1, 'bob')assert student.id == 1assert student.name == 'bob'def test_student_create():student = Student(2, 'alice')assert student.id == 2assert student.name == 'alice'

@pytest.fixture

class Student():def __init__(self, id: int, name: str):self.id = idself.name = namedef valid_name(self):if self.name:return 3 < len(self.name) < 10return False
from server.app import Student
import pytest@pytest.fixture
def valid_student():student = Student(1, 'Kite')yield student@pytest.fixture
def not_valid_student1():student = Student(2, 'abcdefjijklmnopq')yield student@pytest.fixture
def not_valid_student2(not_valid_student1):# 这里不能对valid_student的name进行赋值修改哟student = Student(3, 'Bob')student.name = not_valid_student1.nameyield studentdef test_student(valid_student, not_valid_student1, not_valid_student2):ret = valid_student.valid_name()assert retret = not_valid_student1.valid_name()assert not retret = not_valid_student2.valid_name()assert not ret

conftest.py

作用:使得fixture可以被多个文件中的测试用例复用。

在tests目录下建立conftest.py文件,这里引入其他文件中的fixture,那么其他用例中就可以使用这些fixture,你也可以定义fixture在这个文件中(但是不推荐哈)

参数化测试

# 判断是否是奇数
def is_odd(x: int):return x % 2 != 0
import pytestfrom server.app import is_odd@pytest.mark.parametrize("num,expect_ret", [(1, True), (2, False)])
def test_is_odd(num, expect_ret):actual_ret = is_odd(num)assert expect_ret == actual_ret

数据库查询的mock

import pytest
from unittest.mock import patch, MagicMock
from server.controller.message_controller import create_user@pytest.fixture
def mock_session_scope():with patch("server.db.session.session_scope") as mock_session_scope:mock_session_scope_return_value = MagicMock()mock_session_scope.return_value = mock_session_scope_return_valuesession_mock = MagicMock()mock_session_scope_return_value.__enter__.return_value = session_mockyield session_mockdef test_create_user(mock_session_scope):ret = create_user("alice")assert 'ok' == retdef test_create_user_exception(mock_session_scope):with pytest.raises(ValueError):create_user("")

覆盖率

pip install pytest
pip install pytest-cov
pytest --cov --cov-report=html

相关文章:

python单元测试

文章目录 单元测试定义断言函数Test FixturesMockpatch装饰器模拟&#xff08;首选&#xff09;上下文管理器模拟手动模拟 测试实例 测试覆盖率pytest框架起步安装使用常用参数跳过测试pytest.fixtureconftest.py参数化测试 数据库查询的mock覆盖率 单元测试 定义 单元测试是…...

华为---静态路由-浮动静态路由及负载均衡(二)

7.2 浮动静态路由及负载均衡 7.2.1 原理概述 浮动静态路由(Floating Static Route)是一种特殊的静态路由&#xff0c;通过配置去往相同的目的网段&#xff0c;但优先级不同的静态路由&#xff0c;以保证在网络中优先级较高的路由&#xff0c;即主路由失效的情况下&#xff0c…...

Maven deploy上传远程私服失败

Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy (default-deploy) on project 你的项目: Cannot deploy artifacts when Maven is in offline mode 解决方案&#xff1a; 1.IDEA把这个钩子去掉 2. settings.xml里把 <offline>标…...

通天星CMSV6车载定位监控平台 point_manage/merge SQL注入致RCE漏洞复现

0x01 产品简介 通天星CMSV6车载定位监控平台拥有以位置服务、无线3G/4G视频传输、云存储服务为核心的研发团队,专注于为定位、无线视频终端产品提供平台服务,通天星CMSV6产品覆盖车载录像机、单兵录像机、网络监控摄像机、行驶记录仪等产品的视频综合平台。 0x02 漏洞概述 …...

图像识别技术在人脸识别领域的新突破

图像识别技术在人脸识别领域的新突破主要体现在多个方面&#xff0c;这些突破不仅提高了人脸识别的准确性和效率&#xff0c;还拓展了其应用领域。以下是对这些新突破的详细归纳&#xff1a; 深度学习技术的应用&#xff1a; 深度学习技术&#xff0c;特别是卷积神经网络&…...

iview 组件里面的(任何一个月)整月日期全部选中_iview时间轴选中有历史记录日期

iview 组件里面的整月日期全部选中&#xff1a; ①&#xff1a;第一种是当前月的日期全部选中&#xff1a; 先上效果图&#xff1a;当前月分 获取到的值&#xff1a; 当前月的方法&#xff1a; // getDateStr() {// var curDate new Date();// var curMonth curDate.ge…...

Charles配置与API数据抓取

2024软件测试面试刷题&#xff0c;这个小程序&#xff08;永久刷题&#xff09;&#xff0c;靠它快速找到工作了&#xff01;&#xff08;刷题APP的天花板&#xff09;-CSDN博客跳槽涨薪的朋友们有福了&#xff0c;今天给大家推荐一个软件测试面试的刷题小程序。https://blog.c…...

[FreeRTOS 内部实现] 信号量

文章目录 基础知识创建信号量获取信号量释放信号量信号量 内部实现框图 基础知识 [FreeRTOS 基础知识] 信号量 概念 创建信号量 #define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) #define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) #define xSe…...

Vue57-组件的自定义事件_解绑

给谁绑的自定义事件&#xff0c;就找谁去触发&#xff1b;给谁绑的自定义事件&#xff0c;就找谁去解绑&#xff1b; 一、解绑自定义事件 1-1、解绑一个自定义事件 到student.vue组件中去解绑。 1-2、解绑多个自定义事件 使用数组来解绑多个。 1-3、解绑所有的自定义事件 二、…...

Java启动jar设置内存分配详解

在微服务架构越来越盛行的情况下&#xff0c;我们通常一个系统都会拆成很多个小的服务&#xff0c;但是最终部署的时候又因为没有那么多服务器只能把多个服务部署在同一台服务器上&#xff0c;这个时候问题就来了&#xff0c;服务器内存不够&#xff0c;这个时候我们就需要对每…...

Feign Client超时时间设置不生效问题

在使用Feign Client时&#xff0c;可以通过两种方式来设置超时时间&#xff1a; 针对整个Feign Client设置超时时间 可以在Feign Client的配置类中通过修改Request.Options对象来设置超时时间。Request.Options对象有两个属性&#xff0c;connectTimeoutMillis用于设置连接超…...

Haproxy部署Web群集

概论 HAProxy是可提供高可用性、负载均衡以及基于TCP和HTTP应用的代理&#xff0c;是免费、快速并且可靠的一种解决方案。HAProxy非常适用于并发大&#xff08;并发达1w以上&#xff09;web站点&#xff0c;这些站点通常又需要会话保持或七层处理。HAProxy的运行模式使得它可以…...

C++STL梳理

CSTL标准手册&#xff1a; https://cplusplus.com/reference/stl/ https://cplusplus.com/reference/vector/vector/at/ 1、STL基础 1.1、STL基本组成(6大组件13个头文件) 通常认为&#xff0c;STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 部分构成&…...

找出1000以内的所有的完数

完数的概念&#xff1a;完数&#xff08;Perfect Number&#xff09;是一个正整数&#xff0c;它等于除了它本身以外所有正因子之和。例如&#xff0c;6的因子有1、2、3和6&#xff0c;其中1236&#xff0c;所以6是一个完数。 #include <stdio.h> // 函数用于计算一个数…...

3110. 字符串的分数

给你一个字符串 s 。一个字符串的 分数 定义为相邻字符 ASCII 码差值绝对值的和。 请你返回 s 的 分数 。 示例 1&#xff1a; 输入&#xff1a;s "hello" 输出&#xff1a;13 解释&#xff1a; s 中字符的 ASCII 码分别为&#xff1a;h 104 &#xff0c;e 1…...

Mybatis MySQL allowMultiQueries 一次性执行多条语句

在JDBC 增加参数allowMultiQueries jdbc:mysql://localhost:3306/abc?&allowMultiQueriestrue <insert id"addRi" parameterType"java.util.List">DELETE FROM sys_ri WHERE sr_id #{roId} AND sr_fion_id #{fod};INSERT into sys_rVALUES&…...

Kubernates容器化JVM调优笔记(内存篇)

Kubernates容器化JVM调优笔记&#xff08;内存篇&#xff09; 先说结论背景思路方案 先说结论 1、首先如果是JDK8&#xff0c;需要使用JDK8_191版本以上&#xff0c;才支持容器化环境和以下参数&#xff0c;否则就更新到JDK10以上&#xff0c;选择对应的镜像构建就行了 2、在容…...

Elasticsearch Scroll 报错entity content is too long

2024-06-24 15:22:01:568 ERROR [task-31] (ScrollFetcherProduceAction.java:129) 访问ES出错org.apache.http.ContentTooLongException: entity content is too long [112750110] for the configured buffer limit [104857600]at org.elasticsearch.client.HeapBufferedAsync…...

Vue iview输入框change事件replace正则替换不生效问题的解决。

// 需求&#xff1a;输入座机号只允许输入数字和"-" onChange(e){this.$nextTick(()>{this.phone e.target.value.replace(/[^0-9-]/g, );}) } 解决&#xff1a;添加**this.$nextTick**即可...

Prestashop跨境电商独立站,外贸B2C网站完整教程

Prestashop是一款来自法国专业的开源电商CMS(内容管理系统)平台&#xff0c;和wordpress一样比较轻量&#xff0c;适合中小网站。Prestashop跨境电商独立站在国内并不是很流行&#xff0c;不过国外是非常火的&#xff0c;从各大平台的Prestashop主题数量就可以看得出来。 最有…...

常用算法及参考算法 (1)累加 (2)累乘 (3)素数 (4)最大公约数 (5)最值问题 (6)迭代法

常用算法及参考算法 &#xff08;1&#xff09;累加 &#xff08;2&#xff09;累乘 &#xff08;3&#xff09;素数 &#xff08;4&#xff09;最大公约数 &#xff08;5&#xff09;最值问题 &#xff08;6&#xff09;迭代法 1. 累加 #include <stdio.h>int main() {…...

java简易计算器(多种方法)

parseDouble() 方法属于 java.lang.Double 类。它接收一个字符串参数&#xff0c;其中包含要转换的数字表示。如果字符串表示一个有效的 double&#xff0c;它将返回一个 double 值。 应用场景 parseDouble() 方法在以下场景中非常有用&#xff1a; 从用户输入中获取数字&a…...

spring的bean定义和扫描规则

1、bean的基本定义 在Spring框架中&#xff0c;Bean是一个核心概念&#xff0c;它是Spring IoC&#xff08;Inverse of Control&#xff0c;控制反转&#xff09;容器管理的一个对象实例。简单来说&#xff0c;Bean就是由Spring容器初始化、配置和管理的对象。这些对象可以是J…...

软件工程体系概念

软件工程 软件工程是应用计算机科学、数学及 管理科学等原理开发软件的工程。它借鉴 传统工程的原则、方法&#xff0c;以提高质量&#xff0c;降 低成本为目的。 一、软件生命周期 二、软件开发模型 1.传统模型 瀑布模型、V模型、W模型、X 模型、H 模型 (1)瀑布模型 瀑布…...

史上最全整合nacos单机模式整合哈哈哈哈哈

Nacos 是阿里巴巴推出的一个新开源项目&#xff0c;它主要是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos提供了一组简单易用的特性集&#xff0c;帮助用户快速实现动态服务发现、服务配置、服务元数据及流量管理。 Nacos 的关键特性包括&#x…...

Python xml.dom.minidom 读取XML元素

哈喽,大家好,我是木头左! 什么是 XML? XML(可扩展标记语言)是一种用于描述数据结构和交换数据的标记语言。它被广泛用于 Web 应用程序中,用于存储和传输数据。XML 具有自描述性,因此可以很容易地理解和处理。 Python 中的 xml.dom.minidom Python 提供了一个内置的库…...

【Python/Pytorch 】-- K-means聚类算法

文章目录 文章目录 00 写在前面01 基于Python版本的K-means代码02 X-means方法03 最小二乘法简单理解04 贝叶斯信息准则 00 写在前面 时间演变聚类算法&#xff1a;将时间演变聚类算法用在去噪上&#xff0c;基本思想是&#xff0c;具有相似信号演化的体素具有相似的模型参数…...

【Eureka】介绍与基本使用

Eureka介绍与基本使用 一个简单的Eureka服务器的设置方法&#xff1a;1 在pom.xml中添加Eureka服务器依赖&#xff1a;2 在application.properties或application.yml中添加Eureka服务器配置&#xff1a;3 创建启动类&#xff0c;使用EnableEurekaServer注解启用Eureka服务器&am…...

SpringBoot+Vue集成富文本编辑器

1.引入 我们常常在各种网页软件中编写文档的时候&#xff0c;常常会有富文本编辑器&#xff0c;就比如csdn写博客的这个页面&#xff0c;包含了富文本编辑器&#xff0c;那么怎么实现呢&#xff1f;下面来详细的介绍&#xff01; 2.安装wangeditor插件 在Vue工程中&#xff0c;…...

React@16.x(34)动画(中)

目录 3&#xff0c;SwitchTransition3.1&#xff0c;原理3.1.2&#xff0c;key3.1.2&#xff0c;mode 3.2&#xff0c;举例3.3&#xff0c;结合 animate.css 4&#xff0c;TransitionGroup4.1&#xff0c;其他属性4.1.2&#xff0c;appear4.1.2&#xff0c;component4.1.3&…...