Python 模拟类属性
本文的主要目的是演示如何使用 python 单元测试模块 unittest 操作类属性以进行测试和调试。
模拟类属性的原因
测试开发的代码是否存在错误、错误和极端情况是开发应用程序时最重要的方面之一,尤其是当应用程序面向多个用户时。
使用内置的Python模块unittest,我们可以执行测试用例来测试代码的完整性。 需要严格测试的最常见元素之一是类属性。
类属性可以处理随机输入以防止意外行为。
考虑以下代码:
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
考虑一个名为 Calculate 的类,它包含一个名为 value 的属性和一个名为 Process 的方法。 字典存储在值中,稍后根据需求和数据类型对其进行处理。
为确保该属性几乎可以存储任何类型的字典并无错误地处理,必须测试该属性以确保实现无错误且不需要修改。
模拟类属性的可能解决方案
我们可以通过两种方式模拟类属性; 使用 PropertyMock 和不使用 PropertyMock。 让我们使用示例代码在下面学习它们中的每一个。
使用 PropertyMock 模拟类属性
要模拟属性,我们可以使用 PropertyMock,主要用作属性的模拟或类的描述符。
值得注意的是,PropertyMock 提供了 __get__
和 __set__
方法来更改获取属性后的返回值。
考虑以下代码:
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock, PropertyMock
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, 'value', new_callable=PropertyMock)
def test_method(self, mocked_attrib):
mocked_attrib.return_value = 1
self.assertEqual(Calculate().value, 1) # Will pass without any complaints
print("First Assertion Passed!")
self.assertEqual(Calculate().value, 22) # Will throw an assertion
print("Second Assertion Passed!")
if __name__ == '__main__':
Test().test_method()
输出:
First Assertion Passed!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 24, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 20, in test_method
self.assertEqual(Calculate().value, 22) # Will throw an assertion
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 1 != 22
在解决方案中,创建了一个新方法test_method来修改Calculate.value的值。 值得注意的是,函数是用 patch.object 修饰的。
模块中的补丁装饰器有助于修补模块和类级属性。 为了使修补内容更加具体,我们使用 patch.object 而不是 patch 直接修补方法。
在装饰器中,首先传递类名Calculate,表示要修补的对象是Calculate的一部分,传递的属性值的名称。
最后一个参数是一个 PropertyMock 对象,我们通过传递不同的数字来覆盖 value 属性。
程序的总体流程如下:
- test_method 被调用。
- 类Calculate的值被修补,并分配了一个new_callable,一个PropertyMock的实例。
- 使用 return_value 属性用 1 覆盖 Calculate.value。
- 检查第一个断言,其中Calculate.value 必须等于1。
- 检查第二个断言,其中 Calculate.value 必须等于 22。
在不使用 PropertyMock 的情况下模拟类属性
我们也可以不使用 PropertyMock 来解决它。 只需对上述示例稍作修改。
考虑以下代码:
import unittest
from unittest.mock import patch
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, 'value', 1)
def test_method(self):
# Will pass without any complaints
self.assertEqual(Calculate().value, 1)
print("First Assertion Passed!")
# Will throw an assertion because "Calculate.value" is now 1
self.assertEqual(Calculate().value, 22)
print("Second Assertion Passed!")
if __name__ == '__main__':
Test().test_method()
输出:
First Assertion Passed!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 23, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 19, in test_method
self.assertEqual(Calculate().value, 22) # Will throw an assertion because "Calculate.value" is now 1
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 1 != 22
我们可以直接将希望存储到Calculate.value 中的值给出,而不是将PropertyMock 的实例传递给new_callable。
Python 模拟类构造函数
确保所有初始化变量按预期工作并且不会表现出意外行为。 还需要使用不同的输入来测试构造函数,以减少任何极端情况。
考虑以下代码:
class Calculate:
num = 0
dic = {}
def __init__(self, number, dictt):
self.num = number
self.dic = dictt
def Process(self): # An example method
pass
假设我们要测试Calculate 类的构造函数。 为了确保属性按预期工作,我们必须修补构造函数并传递不同的输入以消除任何可能的错误。
我们该怎么做? 请参阅以下解决方案。
使用 patch.object 装饰器来修补构造函数
我们可以使用 patch.object 装饰器来修补构造函数。
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock, PropertyMock
class Calculate:
num = 0
dic = {}
def __init__(self):
self.num = 1
self.dic = {"11", 2}
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, '__new__')
def test_method(self, mocked_calc):
mocked_instance = MagicMock()
mocked_instance.num = 10
mocked_instance.dic = {"22" : 3}
mocked_calc.return_value = mocked_instance
self.assertEqual(Calculate().num, 10)
self.assertEqual(Calculate().dic, {"22" : 3})
print("First set of Asserts done!")
self.assertEqual(Calculate().num, 1)
self.assertEqual(Calculate().dic, {"11", 2})
print("Second set of Asserts done!")
if __name__ == '__main__':
Test().test_method()
输出:
The first set of Asserts is done!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 37, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 32, in test_method
self.assertEqual(Calculate().num, 1)
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 10 != 1
值得注意的是,我们没有修补 __init__
,而是修补了 __new__
。
这是因为类的实例是在执行 __new__
时创建的,而在 __init__
中,只初始化了变量。 那么,既然我们需要创建一个新的模拟实例,为什么我们要修补 __new__
而不是 __init__
?
相关文章
Python 单元测试设置
发布时间:2023/06/22 浏览次数:127 分类:Python
-
在本文中,我们将讨论什么是单元测试和 Python 单元测试框架。 我们还讨论了如何使用 setUp() 函数进行单元测试以及示例。
Python rsync 同步
发布时间:2023/06/22 浏览次数:129 分类:Python
-
本文将探讨 rsync 以及我们如何从 Python 脚本中使用它。Python同步如上所述,rsync 是一个强大的工具
Python Deque Peek 介绍
发布时间:2023/06/22 浏览次数:198 分类:Python
-
本文演示了如何在 Python 中查看/检查双端队列(双端队列)前面的元素,而无需将它们从双端队列中删除。Python Deque Peek 概述
Python 中的异步请求
发布时间:2023/06/22 浏览次数:74 分类:Python
-
今天我们来学习一下异步请求; 这个讨论将引出代码示例,看看我们如何在 Python 中编写异步请求。Python 中的异步请求
Python - 等待异步函数完成
发布时间:2023/06/22 浏览次数:163 分类:Python
-
本文介绍如何创建异步函数并使用await 关键字中断进程。 我们还将学习如何在 Python 中使用任务而不是线程。
在 Python 中列出虚拟环境
发布时间:2023/06/21 浏览次数:149 分类:Python
-
在本文中,我们将讨论什么是 Python 中的虚拟环境以及列出它们的一些命令。虚拟环境是一个独立的环境,我们可以在其中安装库、包、脚本和Python解释器。
Python 中的 Urljoin 简介
发布时间:2023/06/21 浏览次数:178 分类:Python
-
本篇文章介绍了如何使用 Python 中的 urljoin() 模块形成 URL,并介绍了使用该模块时的行为。
Python 中并发方面的差异
发布时间:2023/06/21 浏览次数:68 分类:Python
-
本文将讨论如何在Python中实现并发以及其优点和缺点。线程和多线程 线程在Python中已经存在很长时间了。
Python 中从线程获取返回值
发布时间:2023/06/21 浏览次数:145 分类:Python
-
这篇文章首先讨论了线程的基础知识,并提供了一个在Python中启动线程的代码示例。然后,我们将讨论一个在线程中获取函数返回值的代码。