如何模拟称为默认参数的函数
How to mock a function called as default argument
我有一个包含时间戳的 class。在 init 函数中,初始时间戳设置为提供的参数,默认值为 'time.monotonic()'。我也有和更新设置时间戳的功能,也使用 time.monotonic()
在对此 class 进行单元测试时,我想模拟 time.monotonic,以获得可预测的结果。
但是,默认参数中的调用始终是真实的 time.monotonic
ClassWithTimestamp.py:
import time
class ClassWithTimestamp:
def __init__(self, value={}, timestamp=time.monotonic()):
self.timestamp = timestamp
def update(self):
self.timestamp = time.monotonic()
ClassWithTimestampTest.py:
import unittest
from unittest import mock
import ClassWithTimestamp
class ClassWithTimestampTest(unittest.TestCase):
def setUp(self):
ClassWithTimestamp.time.monotonic = mock.Mock(name='now')
ClassWithTimestamp.time.monotonic.return_value = 1000
def tearDown(self):
pass
def test_init(self):
sut = ClassWithTimestamp.ClassWithTimestamp()
self.assertEqual(sut.timestamp, 1000)
def test_update(self):
sut = ClassWithTimestamp.ClassWithTimestamp()
sut.update()
self.assertEqual(sut.timestamp, 1000)
if __name__ == '__main__':
unittest.main()
当运行时:
python3 ClassWithTimestampTest.py -v
test_init (__main__.ClassWithTimestampTest) ... FAIL
test_update (__main__.ClassWithTimestampTest) ... ok
======================================================================
FAIL: test_init (__main__.ClassWithTimestampTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "ClassWithTimestampTest.py", line 16, in test_init
self.assertEqual(sut.timestamp, 1000)
AssertionError: 762811.874163785 != 1000
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
我发现了这个:
"Least Astonishment" and the Mutable Default Argument
它解释了我的问题,默认参数是在定义 init 函数时调用的,而不是执行它。
所以我的代码一开始就有错误,因为我打算将时间戳设置为构建时间。
我修改后的代码是
def __init__(self, value={}, timestamp=None):
if timestamp:
self.timestamp = timestamp
else:
self.timestamp = time.monotonic()
问题本身仍然存在,我看到的唯一方法是在构造后用模拟函数的输出覆盖 class 的成员。
我有一个包含时间戳的 class。在 init 函数中,初始时间戳设置为提供的参数,默认值为 'time.monotonic()'。我也有和更新设置时间戳的功能,也使用 time.monotonic()
在对此 class 进行单元测试时,我想模拟 time.monotonic,以获得可预测的结果。
但是,默认参数中的调用始终是真实的 time.monotonic
ClassWithTimestamp.py:
import time
class ClassWithTimestamp:
def __init__(self, value={}, timestamp=time.monotonic()):
self.timestamp = timestamp
def update(self):
self.timestamp = time.monotonic()
ClassWithTimestampTest.py:
import unittest
from unittest import mock
import ClassWithTimestamp
class ClassWithTimestampTest(unittest.TestCase):
def setUp(self):
ClassWithTimestamp.time.monotonic = mock.Mock(name='now')
ClassWithTimestamp.time.monotonic.return_value = 1000
def tearDown(self):
pass
def test_init(self):
sut = ClassWithTimestamp.ClassWithTimestamp()
self.assertEqual(sut.timestamp, 1000)
def test_update(self):
sut = ClassWithTimestamp.ClassWithTimestamp()
sut.update()
self.assertEqual(sut.timestamp, 1000)
if __name__ == '__main__':
unittest.main()
当运行时:
python3 ClassWithTimestampTest.py -v
test_init (__main__.ClassWithTimestampTest) ... FAIL
test_update (__main__.ClassWithTimestampTest) ... ok
======================================================================
FAIL: test_init (__main__.ClassWithTimestampTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "ClassWithTimestampTest.py", line 16, in test_init
self.assertEqual(sut.timestamp, 1000)
AssertionError: 762811.874163785 != 1000
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
我发现了这个:
"Least Astonishment" and the Mutable Default Argument
它解释了我的问题,默认参数是在定义 init 函数时调用的,而不是执行它。
所以我的代码一开始就有错误,因为我打算将时间戳设置为构建时间。
我修改后的代码是
def __init__(self, value={}, timestamp=None):
if timestamp:
self.timestamp = timestamp
else:
self.timestamp = time.monotonic()
问题本身仍然存在,我看到的唯一方法是在构造后用模拟函数的输出覆盖 class 的成员。