Django Selenium测试如何设置模拟时区解决timestamp断言失败问题
问题解决方法
你遇到的断言失败不需要单独伪造时区对象,核心原因是Django DateTimeField写入数据库时默认会截断微秒精度,内存中生成的timezone.now()带微秒值,和数据库读回的截断后时间自然不相等,再加上代码里几个小问题放大了这个误差,下面是几个可直接落地的修复方案:
方案1:断言时统一截断微秒(改动最小)
不需要改业务逻辑,只需要在时间相等断言时,把两边时间的微秒值统一置为0再比对,适配数据库的存储精度:
# 把原来报错的断言改成这行 self.assertEqual( a.timestamp.replace(microsecond=0), self.a1.timestamp.replace(microsecond=0) )
方案2:固定测试全局时间(最稳定,推荐)
用freezegun工具固定测试运行周期内的所有时间返回值,不管是代码里主动调用timezone.now()还是模型字段的默认值调用,都会返回你提前设定的固定时间,从根源消除时间差:
- 先安装依赖:
pip install freezegun - 修改测试代码:
from django.utils import timezone from django.test import TestCase from freezegun import freeze_time from myapp.tests.factories import UserFactory, AnalyticsFactory from myapp.models import Analytics # 固定整个测试类运行时的当前时间 @freeze_time("2022-04-08 13:03:28") class MyTestCase(TestCase): def setUp(self): self.admin = UserFactory( username='first.last@gmail.com', email='first.last@gmail.com', password=12345 ) # 固定时间后不需要手动传timestamp,默认生成的时间全局统一 self.a1 = AnalyticsFactory( user=self.admin, views=10 ) def test_analytics(self): # 不要硬写id=6,直接用生成的实例id查询,避免id不匹配问题 a = Analytics.objects.get(id=self.a1.id) self.assertEqual(a.views, 10) self.assertEqual(a.user, self.admin) self.assertEqual(a.timestamp, self.a1.timestamp) # 注意:原代码中response变量未定义,需要补充对应请求逻辑再做状态码断言 # self.assertEqual(response.status_code, 200)
方案3:工厂类统一生成无毫秒的时间
如果不想额外加依赖,可以直接修改AnalyticsFactory的时间生成逻辑,生成测试数据时自动截断微秒,保证和数据库存储精度一致:
class AnalyticsFactory(factory.django.DjangoModelFactory): class Meta: model = Analytics django_get_or_create = ( 'timestamp', 'views', ) user = factory.SubFactory(UserFactory) # 统一生成截断微秒的时间 timestamp = factory.LazyFunction(lambda: timezone.now().replace(microsecond=0))
额外需要注意的代码问题
- 你在
AnalyticsFactory的django_get_or_create配置中把timestamp作为唯一匹配条件非常不合理,时间精度问题很容易导致工厂类重复创建测试数据,建议替换为业务上真正能唯一标识记录的字段 - 原测试中硬编码
id=6查询记录非常容易出错,测试库的id是全局自增的,前面测试生成的其他记录会导致id偏移,直接使用生成的测试实例self.a1.id查询最稳妥
内容的提问来源于stack exchange,提问作者NewToJS
相关产品推荐
相关产品推荐

