dge*_*gel 17 python django unit-testing mocking python-mock
我正在尝试为执行大量日期操作的django应用程序编写单元测试.我为我的测试安装了模拟猴子补丁django的timezone.now
.
虽然我能够成功地嘲笑timezone.now
时,它通常被称为(实际调用timezone.now()
在我的代码,我不能嘲笑它为与一个创建的模型DateTimeField
用default=timezone.now
.
我有一个User
包含以下内容的模型:
from django.utils import timezone
...
timestamp = models.DateTimeField(default=timezone.now)
modified = models.DateTimeField(default=timezone.now)
...
def save(self, *args, **kwargs):
if kwargs.pop('modified', True):
self.modified = timezone.now()
super(User, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
我的单元测试看起来像这样:
from django.utils import timezone
def test_created(self):
dt = datetime(2010, 1, 1, tzinfo=timezone.utc)
with patch.object(timezone, 'now', return_value=dt):
user = User.objects.create(username='test')
self.assertEquals(user.modified, dt)
self.assertEquals(user.timestamp, dt)
Run Code Online (Sandbox Code Playgroud)
assertEquals(user.modified, dt)
通过,但assertEquals(user.timestamp, dt)
没有.
我怎么能模仿timezone.now
,即使default=timezone.now
在我的模型中也会创建模拟时间?
编辑
我知道我可以改变我的单元测试以通过timestamp
我的选择(可能是由模拟生成的timezone.now
)...好奇如果有一种方法可以避免这种情况.
Jor*_*son 11
我自己也遇到过这个问题.问题是在mock修补了时区模块之前加载了模型,因此在default=timezone.now
计算表达式时,它default
会将kwarg设置为实际timezone.now
函数.
解决方案如下:
class MyModel(models.Model):
timestamp = models.DateTimeField(default=lambda: timezone.now())
Run Code Online (Sandbox Code Playgroud)
cje*_*nek 10
这是您可以使用的方法,不需要更改非测试代码.只需修补default
您想要影响的字段的属性.例如 -
field = User._meta.get_field('timestamp')
mock_now = lambda: datetime(2010, 1, 1)
with patch.object(field, 'default', new=mock_now):
# Your code here
Run Code Online (Sandbox Code Playgroud)
您可以编写辅助函数以使其更简洁.例如,以下代码 -
@contextmanager
def patch_field(cls, field_name, dt):
field = cls._meta.get_field(field_name)
mock_now = lambda: dt
with patch.object(field, 'default', new=mock_now):
yield
Run Code Online (Sandbox Code Playgroud)
会让你写 -
with patch_field(User, 'timestamp', dt):
# Your code here
Run Code Online (Sandbox Code Playgroud)
同样,您可以编写辅助上下文管理器来一次修补多个字段.
小智 6
还有另一种简单的方法可以完成上述事情。
import myapp.models.timezone
from unittest.mock import patch
@patch('django.utils.timezone.now')
def test_created(self, mock_timezone):
dt = datetime(2010, 1, 1, tzinfo=timezone.utc)
mock_timezone.return_value = dt
user = User.objects.create(username='test')
self.assertEquals(user.modified, dt)
self.assertEquals(user.timestamp, dt)
Run Code Online (Sandbox Code Playgroud)
这是模拟 timezone.now 的最佳方式。