【Python单元测试】学习笔记2

文章目录

python单元测试学习笔记1https://blog.csdn.net/qq_42761751/article/details/141144477?spm=1001.2014.3001.5501

python单元测试学习笔记2https://blog.csdn.net/qq_42761751/article/details/141202123?spm=1001.2014.3001.5501

python单元测试学习笔记3https://blog.csdn.net/qq_42761751/article/details/141233236?spm=1001.2014.3001.5501

04. Mock

  1. 什么是Mock
  2. Mock和MagicMock
  3. Mock实例

什么是Mock

Mock就是要模拟函数、方法、类的行为

Mock和MagicMock

unittest.mock模块提供了Mock和MagicMock:

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

MagicMock是Mock的子类,同时模拟了很多Magic方法(len, __str__等等)

Mock实例

python 复制代码
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())

如果执行代码逻辑中,某一个函数不想被真的调用的话,可以使用Mock方法

05. patch

  1. patch能做什么
  2. patch的目标
  3. 使用patch

patch能做什么

patch可以临时用Mock对象替换一个目标(函数,方法,类)

patch的目标

patch可以替换的目标:

  1. 目标必须是可以import的
  2. 是在使用目标的地方替换而不是替换定义

使用patch

使用patch的三种方式

  1. 装饰器方式
  2. 上下文管理器方式
  3. 手动方式

装饰器方式:

python 复制代码
import unittest
from unittest.mock import Mock,patch
from myprj.service import student_service

class 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是逆序的方式
        '''

        # setup
        student = Mock(id=1, name = 'Jack')
        find_student_mock.return_value = student

        # Action
        student_service.change_name(1, 'Tom')

        # Assert
        self.assertEqual('Tom', student.name)

上下文管理器方式:

python 复制代码
	def test_change_name_contextmanager(self):
        # setup
        student = Mock(id=1, name = 'Jack')
        find_student_mock.return_value = student

        with patch("myprj.service.student_service.find_student_by_id") as find_student_mock, \
            patch("myprj.service.student_service.save_student"):
            # Action
            find_student_mock.return_value = student
            student_service.change_name(1, 'Tom')

            # Assert
            self.assertEqual('Tom', student.name)

手动方式

python 复制代码
	@patch("myprj.service.student_service.find_student_by_id")
    def test_change_name_manual(self,find_student_mock):
        # setup
        student = Mock(id=1, name = 'Jack')
        find_student_mock.return_value = student

        patcher = patch("myprj.service.student_service.save_student")
        patcher.start()
        # Action
        student_service.change_name(1, 'Tom')
        patcher.stop()

        # Assert
        self.assertEqual('Tom', student.name)

06.测试实例

需要被测试的代码:

python 复制代码
from urllib.request import urlopen, Request
import os.path

class ProductService:

    def download_img(self, 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}"

测试代码:

python 复制代码
import unittest
from unittest.mock import patch, MagicMock
from product_service import ProductService

class TestProductService(unittest.TestCase):
    """
    """
    def setUp(self) -> None:
        self.service = ProductService()
    
    def tearDown(self) -> None:
        self.service = None


    @patch("product_service.urlopen")
    @patch("product_service.Request.__new__")
    def test_download_img_with_excertion(self, request_mock, urlopen_mock):
        # Setup
        url = "http://www.google.com/a.jpg"
        urlopen_return_mock = MagicMock()
        webfile_mock = MagicMock()
        urlopen_mock.return_value = urlopen_return_mock
        urlopen_return_mock.__enter__.return_value = webfile_mock
        webfile_mock.read.return_value = None 

        with self.assertRaises(Exception):
            # Action
            self.service.download_img(url)

    @patch("builtins.open")    
    @patch("os.path.basename")
    @patch("product_service.urlopen")
    @patch("product_service.Request.__new__")
    def test_download_img_with_success(self, request_mock, urlopen_mock, basename_mock, open_mock):
        # Setup
        url = "http://www.google.com/a.jpg"
        urlopen_return_mock = MagicMock()
        webfile_mock = MagicMock()
        urlopen_mock.return_value = urlopen_return_mock
        urlopen_return_mock.__enter__.return_value = webfile_mock
        webfile_mock.read.return_value = "not none" 
        basename_mock.return_value = "fff"

        # Action
        result = self.service.download_img(url)

        self.assertEqual("Download image successfully, fff", result)

07.覆盖率测试

测试覆盖率统计的是被测试代码在单元测试过程中有多少代码被执行到了, 覆盖率=执行到的代码行数/代码总行数

相关命令行:

统计测试覆盖率:

bash 复制代码
python -m coverage run -m unittest

查看覆盖率报告:

bash 复制代码
python -m coverage report

生成html格式的覆盖率报告:(在htmlcov中index.html,点击每个测试文件可以查看具体哪一行代码没有被执行到)

bash 复制代码
python -m coverage html

08. PyTest框架

本文参考:

https://www.bilibili.com/video/BV1S14y1u7FL/?p=7\&spm_id_from=pageDriver

相关推荐
晓晓hh4 分钟前
JavaSe学习——基础
java·开发语言·学习
峥嵘life13 分钟前
Android16 【CTS】CtsWindowManagerDeviceAnimations存在fail项
android·linux·学习
清水白石00816 分钟前
Python 内存陷阱深度解析——浅拷贝、深拷贝与对象复制的正确姿势
开发语言·python
国家二级编程爱好者18 分钟前
删除typora文档没有引用的资源文件
git·python
进击的雷神20 分钟前
邮箱编码解码、国际电话验证、主办方过滤、多页面深度爬取——柬埔寨塑料展爬虫四大技术难关攻克纪实
爬虫·python
深蓝电商API1 小时前
多线程 vs 异步 vs 多进程爬虫性能对比
爬虫·python
进击的雷神1 小时前
相对路径拼接、TEL前缀清洗、多链接过滤、毫秒级延迟控制——日本东京塑料展爬虫四大技术难关攻克纪实
爬虫·python
云溪·1 小时前
Milvus向量数据库混合检索召回案例
python·ai·milvus
柒.梧.1 小时前
Java集合核心知识点深度解析:数组与集合区别、ArrayList原理及线程安全问题
java·开发语言·python
AsDuang2 小时前
Python 3.12 MagicMethods - 49 - __imatmul__
开发语言·python